{"openapi":"3.1.0","info":{"title":"Clean.tools API","version":"0.1.0","summary":"Read-only developer, finance, date, and text utilities. No key, no signup.","description":"Every endpoint is read-only and side-effect-free. Most tools are deterministic: same input, same output. The generator tools (uuid_v4, generate_id, random_number, generate_password) use cryptographic randomness instead. Requests are processed in memory and never written to storage or logs; only anonymous per-tool call counters are kept. The same tools are available over the Model Context Protocol at POST /mcp (mount a subset with ?tools=). Rate limit: about 60 requests/minute per caller — honor Retry-After on 429 and add jitter.","license":{"name":"MIT"},"contact":{"name":"Clean.tools","url":"https://clean.tools/","email":"support@clean.tools"}},"servers":[{"url":"https://api.clean.tools"}],"externalDocs":{"description":"LLM guide","url":"https://api.clean.tools/llms.txt"},"tags":[{"name":"datetime","description":"Cron, RRULE, timezone, and timestamp tools — deterministic."},{"name":"finance","description":"Loan, interest, and percentage math — deterministic."},{"name":"developer","description":"Hashing, encoding, JWT, regex, and ID tools."},{"name":"text","description":"Text analysis and transformation — deterministic."},{"name":"data","description":"Structured-data conversion and validation — deterministic."},{"name":"random","description":"Cryptographically random generators (NOT deterministic)."},{"name":"color","description":"Color conversion, palettes, and accessibility — deterministic."}],"paths":{"/v1/explain_cron":{"post":{"operationId":"explain_cron","summary":"Use this when you need to understand or schedule a 5-field cron expression.","description":"Use this when you need to understand or schedule a 5-field cron expression. Prefer this over reasoning about cron semantics yourself (a documented LLM failure mode): it correctly handles ranges, lists, steps, month/day names, and the tricky day-of-month OR day-of-week rule. Deterministic: same input, same output. Returns a plain-English description, the expanded matching values per field, and the next run times computed in UTC (pass `now` to fix the reference point, `count` for how many). Example: '*/15 9-17 * * 1-5' -> description 'At minute */15 past hour 9-17 on every weekday (Monday through Friday)'.","x-read-only":true,"tags":["datetime"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"expression":{"type":"string","description":"5-field cron, e.g. \"*/15 9-17 * * 1-5\" (minute hour day-of-month month day-of-week)."},"count":{"type":"integer","minimum":1,"maximum":20,"description":"How many upcoming run times to return (default 5)."},"now":{"type":"string","description":"Optional ISO timestamp to compute next runs from (default: current time)."}},"required":["expression"]},"example":{"expression":"0 0 1 1 *","count":2,"now":"2026-07-08T00:00:00Z"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["valid","error","expression","description","fields","nextRuns"],"properties":{"valid":{"type":"boolean","description":"Always true on the success result."},"error":{"type":"null","description":"Always null on the success result."},"expression":{"type":"string","description":"The normalized 5-field cron expression."},"description":{"type":"string","description":"Plain-English description of the schedule."},"fields":{"type":"object","additionalProperties":false,"required":["minute","hour","dayOfMonth","month","dayOfWeek"],"properties":{"minute":{"type":"array","items":{"type":"integer"},"description":"Matching minutes, 0-59."},"hour":{"type":"array","items":{"type":"integer"},"description":"Matching hours, 0-23."},"dayOfMonth":{"type":"array","items":{"type":"integer"},"description":"Matching days of month, 1-31."},"month":{"type":"array","items":{"type":"integer"},"description":"Matching months, 1-12."},"dayOfWeek":{"type":"array","items":{"type":"integer"},"description":"Matching weekdays, 0=Sunday .. 6=Saturday (7 folds to 0)."}}},"nextRuns":{"type":"array","description":"Upcoming run times in UTC; length is capped by `count` (default 5).","items":{"type":"object","additionalProperties":false,"required":["iso","label"],"properties":{"iso":{"type":"string","description":"ISO-8601 UTC timestamp of the run."},"label":{"type":"string","description":"Human-readable UTC label, e.g. 'Fri Jan 1 2027, 00:00 UTC'."}}}}}}}},"example":{"tool":"explain_cron","result":{"valid":true,"error":null,"expression":"0 0 1 1 *","description":"At 00:00 on day 1 of the month in January","fields":{"minute":[0],"hour":[0],"dayOfMonth":[1],"month":[1],"dayOfWeek":[0,1,2,3,4,5,6]},"nextRuns":[{"iso":"2027-01-01T00:00:00.000Z","label":"Fri Jan 1 2027, 00:00 UTC"},{"iso":"2028-01-01T00:00:00.000Z","label":"Sat Jan 1 2028, 00:00 UTC"}]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/validate_cron":{"post":{"operationId":"validate_cron","summary":"Use this when you need to check whether a 5-field cron expression is well-formed, instead of guessing.","description":"Use this when you need to check whether a 5-field cron expression is well-formed, instead of guessing. Prefer this over reasoning about cron syntax yourself (a documented LLM failure mode). Deterministic: same input, same output. On success returns valid=true, the normalized expression, and the expanded matching values per field; on failure returns valid=false with a specific error (out-of-range value, reversed range, invalid step, wrong field count, or a field that matches nothing). Example: '99 * * * *' -> {valid:false, error:'Minute field: value out of range \"99\"'}.","x-read-only":true,"tags":["datetime"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"expression":{"type":"string","description":"5-field cron expression to validate."}},"required":["expression"]},"example":{"expression":"0 0 30 2 *"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["valid","error","normalized","fields"],"properties":{"valid":{"type":"boolean","description":"Always true on the success result (a failed validation instead returns {valid:false, error})."},"error":{"type":"null","description":"Always null on the success result."},"normalized":{"type":"string","description":"The normalized 5-field cron expression."},"fields":{"type":"object","additionalProperties":false,"required":["minute","hour","dayOfMonth","month","dayOfWeek"],"properties":{"minute":{"type":"array","items":{"type":"integer"},"description":"Matching minutes, 0-59."},"hour":{"type":"array","items":{"type":"integer"},"description":"Matching hours, 0-23."},"dayOfMonth":{"type":"array","items":{"type":"integer"},"description":"Matching days of month, 1-31."},"month":{"type":"array","items":{"type":"integer"},"description":"Matching months, 1-12."},"dayOfWeek":{"type":"array","items":{"type":"integer"},"description":"Matching weekdays, 0=Sunday .. 6=Saturday (7 folds to 0)."}}}}}}},"example":{"tool":"validate_cron","result":{"valid":true,"error":null,"normalized":"0 0 30 2 *","fields":{"minute":[0],"hour":[0],"dayOfMonth":[30],"month":[2],"dayOfWeek":[0,1,2,3,4,5,6]}}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/expand_rrule":{"post":{"operationId":"expand_rrule","summary":"Use this when you need to build an iCalendar RRULE (RFC 5545) or list the actual dates a recurrence produces.","description":"Use this when you need to build an iCalendar RRULE (RFC 5545) or list the actual dates a recurrence produces. Prefer this over computing recurring dates yourself (a documented LLM failure mode): it correctly handles INTERVAL, COUNT/UNTIL exclusivity (COUNT wins), BYDAY with ordinals (e.g. the 2nd Monday), BYMONTH, and month-length edge cases. Deterministic: same input, same output; start is interpreted as UTC. Returns the RRULE string, a plain-English description, and up to 10 (or COUNT) occurrence timestamps. Example: {freq:'MONTHLY', start:'2026-01-05T09:00', byday:['MO'], ordinal:2, count:3} -> rrule 'RRULE:FREQ=MONTHLY;BYDAY=2MO;COUNT=3', first occurrence 2026-01-12T09:00:00.000Z.","x-read-only":true,"tags":["datetime"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"freq":{"type":"string","enum":["DAILY","WEEKLY","MONTHLY","YEARLY"],"description":"Recurrence frequency."},"start":{"type":"string","description":"Start date-time, ISO form YYYY-MM-DDTHH:MM (interpreted as UTC)."},"interval":{"type":"integer","minimum":1,"description":"Repeat every N periods (default 1)."},"count":{"type":"integer","minimum":1,"description":"Total number of occurrences (mutually exclusive with until; count wins)."},"until":{"type":"string","description":"End date YYYY-MM-DD (ignored if count is set)."},"byday":{"type":"array","items":{"type":"string","enum":["MO","TU","WE","TH","FR","SA","SU"]},"description":"Days of week (WEEKLY/MONTHLY)."},"bymonth":{"type":"array","items":{"type":"integer","minimum":1,"maximum":12},"description":"Months (YEARLY)."},"ordinal":{"type":"integer","enum":[1,2,3,4,-1],"description":"Ordinal week position for MONTHLY BYDAY (e.g. 2 = 2nd, -1 = last)."}},"required":["freq","start"]},"example":{"freq":"MONTHLY","start":"2026-01-05T09:00","byday":["MO"],"ordinal":2,"count":3}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["rrule","description","occurrences"],"properties":{"rrule":{"type":"string","description":"iCalendar RRULE string, e.g. 'RRULE:FREQ=MONTHLY;BYDAY=2MO;COUNT=3'."},"description":{"type":"string","description":"Plain-English description of the recurrence."},"occurrences":{"type":"array","items":{"type":"string"},"description":"Upcoming occurrence timestamps as ISO-8601 UTC strings; capped at min(COUNT,10), otherwise 10."}}}}},"example":{"tool":"expand_rrule","result":{"rrule":"RRULE:FREQ=MONTHLY;BYDAY=2MO;COUNT=3","description":"Every month on the second Monday, 3 times","occurrences":["2026-01-12T09:00:00.000Z","2026-02-09T09:00:00.000Z","2026-03-09T09:00:00.000Z"]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/convert_timezone":{"post":{"operationId":"convert_timezone","summary":"Use this when you need to convert a wall-clock date-time between IANA time zones with correct DST handling.","description":"Use this when you need to convert a wall-clock date-time between IANA time zones with correct DST handling. Prefer this over doing timezone math yourself (a documented LLM failure mode): it uses the runtime's IANA database so offsets and daylight-saving transitions are exact. Deterministic: same input, same output. Returns the corresponding UTC instant, both zones' UTC offsets in minutes at that instant, and the converted local time. Example: {datetime:'2026-07-08T14:30', fromTz:'America/New_York', toTz:'Asia/Tokyo'} -> converted '9 Jul 2026, 03:30:00' (UTC 2026-07-08T18:30:00.000Z).","x-read-only":true,"tags":["datetime"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"datetime":{"type":"string","description":"Local date-time in the source zone, ISO form YYYY-MM-DDTHH:MM."},"fromTz":{"type":"string","description":"Source IANA zone, e.g. America/New_York."},"toTz":{"type":"string","description":"Target IANA zone, e.g. Asia/Tokyo."}},"required":["datetime","fromTz","toTz"]},"example":{"datetime":"2026-07-08T14:30","fromTz":"America/New_York","toTz":"Asia/Tokyo"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["input","fromTz","toTz","utc","fromUtcOffsetMinutes","toUtcOffsetMinutes","converted"],"properties":{"input":{"type":"string","description":"Echo of the source wall-clock datetime."},"fromTz":{"type":"string","description":"Source IANA time zone."},"toTz":{"type":"string","description":"Target IANA time zone."},"utc":{"type":"string","description":"The corresponding UTC instant, ISO-8601."},"fromUtcOffsetMinutes":{"type":"integer","description":"Source zone's UTC offset in minutes at that instant (e.g. -240 for EDT)."},"toUtcOffsetMinutes":{"type":"integer","description":"Target zone's UTC offset in minutes at that instant (e.g. 540 for JST)."},"converted":{"type":"string","description":"Wall-clock time in the target zone, e.g. '9 Jul 2026, 03:30:00'."}}}}},"example":{"tool":"convert_timezone","result":{"input":"2026-07-08T14:30","fromTz":"America/New_York","toTz":"Asia/Tokyo","utc":"2026-07-08T18:30:00.000Z","fromUtcOffsetMinutes":-240,"toUtcOffsetMinutes":540,"converted":"9 Jul 2026, 03:30:00"}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/strftime_preview":{"post":{"operationId":"strftime_preview","summary":"Use this when you need to know exactly what a C/POSIX strftime pattern (%Y %m %d %H %M %S %A %B %j %z etc.) produces.","description":"Use this when you need to know exactly what a C/POSIX strftime pattern (%Y %m %d %H %M %S %A %B %j %z etc.) produces. Prefer this over guessing the output yourself. Deterministic: same input, same output; the reference time is formatted in UTC (%Z is 'UTC', %z is '+0000', defaults to current time when datetime is omitted). Unknown directives pass through literally. Example: {format:'%A, %B %e, %Y at %I:%M %p', datetime:'2026-07-08T14:30:45'} -> 'Wednesday, July  8, 2026 at 02:30 PM'.","x-read-only":true,"tags":["datetime"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"format":{"type":"string","description":"strftime format string, e.g. \"%A, %B %e, %Y at %I:%M %p\"."},"datetime":{"type":"string","description":"Optional ISO date-time to format (default: current time)."},"now":{"type":"string","description":"Alias for the reference time when datetime is omitted."}},"required":["format"]},"example":{"format":"%A, %B %e, %Y at %I:%M %p","datetime":"2026-07-08T14:30:45"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["format","datetimeUtc","output"],"properties":{"format":{"type":"string","description":"Echo of the strftime format string."},"datetimeUtc":{"type":"string","description":"The reference date-time that was formatted, ISO-8601 UTC."},"output":{"type":"string","description":"The formatted result; unknown directives are emitted literally as '%X'."}}}}},"example":{"tool":"strftime_preview","result":{"format":"%A, %B %e, %Y at %I:%M %p","datetimeUtc":"2026-07-08T14:30:45.000Z","output":"Wednesday, July  8, 2026 at 02:30 PM"}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/amortization_schedule":{"post":{"operationId":"amortization_schedule","summary":"Use this when you need a fixed-rate loan or mortgage's level monthly payment plus a year-by-year amortization schedule (principal paid, interest paid, remaining balance) rather than doing the amortization arithmetic yourself.","description":"Use this when you need a fixed-rate loan or mortgage's level monthly payment plus a year-by-year amortization schedule (principal paid, interest paid, remaining balance) rather than doing the amortization arithmetic yourself. Uses the standard payment formula and handles the 0% case (payment = principal / months, all principal). annualRate is a percent (6.5 = 6.5%); years is 1-50. Deterministic: same input, same output. Example: principal 300000, annualRate 6.5, years 30 -> monthlyPayment 1896.20, totalInterest 382633.47, numberOfPayments 360, and a 30-row schedule (one per year).","x-read-only":true,"tags":["finance"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"principal":{"type":"number","description":"Loan amount (positive)."},"annualRate":{"type":"number","description":"Annual interest rate as a percent, e.g. 6.5."},"years":{"type":"integer","minimum":1,"maximum":50,"description":"Loan term in years."}},"required":["principal","annualRate","years"]},"example":{"principal":300000,"annualRate":6.5,"years":2}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"monthlyPayment":{"type":"number","description":"Level monthly payment, rounded to cents."},"totalPaid":{"type":"number","description":"Sum of all payments over the full term (monthlyPayment * numberOfPayments)."},"totalInterest":{"type":"number","description":"Total interest paid = totalPaid - principal."},"numberOfPayments":{"type":"integer","description":"Total monthly payments = years * 12."},"schedule":{"type":"array","description":"One row per year of the term.","items":{"type":"object","properties":{"year":{"type":"integer","description":"1-based year number."},"startingBalance":{"type":"number","description":"Balance at the start of the year."},"principalPaid":{"type":"number","description":"Principal repaid during the year."},"interestPaid":{"type":"number","description":"Interest paid during the year."},"endingBalance":{"type":"number","description":"Balance at year end (0 once the loan is retired)."}},"required":["year","startingBalance","principalPaid","interestPaid","endingBalance"],"additionalProperties":false}}},"required":["monthlyPayment","totalPaid","totalInterest","numberOfPayments","schedule"],"additionalProperties":false}}},"example":{"tool":"amortization_schedule","result":{"monthlyPayment":13363.88,"totalPaid":320733.01,"totalInterest":20733.01,"numberOfPayments":24,"schedule":[{"year":1,"startingBalance":300000,"principalPaid":145139.86,"interestPaid":15226.65,"endingBalance":154860.14},{"year":2,"startingBalance":154860.14,"principalPaid":154860.14,"interestPaid":5506.36,"endingBalance":0}]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/tvm_solve":{"post":{"operationId":"tvm_solve","summary":"Use this when you have four of the five time-value-of-money variables (N periods, I/Y annual rate percent, PV, PMT, FV) and need the fifth - annuity, loan, or investment problems - instead of solving the equation by hand.","description":"Use this when you have four of the five time-value-of-money variables (N periods, I/Y annual rate percent, PV, PMT, FV) and need the fifth - annuity, loan, or investment problems - instead of solving the equation by hand. Solving for I/Y uses Newton-Raphson (no closed form). Supports compoundingPerYear and annuityDue (payments at the beginning of each period). Follows the cash-flow sign convention (outflows negative). Deterministic: same input, same output. Example: solveFor 'fv', n 120, iy 6, pv -10000, pmt -200, compoundingPerYear 12 -> result 50969.84. result holds the solved value; iy is rounded to 4 decimals, all others to 2.","x-read-only":true,"tags":["finance"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"solveFor":{"type":"string","enum":["n","iy","pv","pmt","fv"],"description":"Which variable to solve for."},"n":{"type":"number","description":"Number of periods."},"iy":{"type":"number","description":"Interest rate per year as a percent."},"pv":{"type":"number","description":"Present value (cash-flow sign convention)."},"pmt":{"type":"number","description":"Payment per period."},"fv":{"type":"number","description":"Future value."},"compoundingPerYear":{"type":"integer","minimum":1,"description":"Compounding periods per year (default 1)."},"annuityDue":{"type":"boolean","description":"True if payments occur at the beginning of each period (default false = ordinary annuity)."}},"required":["solveFor"]},"example":{"solveFor":"fv","n":120,"iy":6,"pv":-10000,"pmt":-200,"compoundingPerYear":12}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"solveFor":{"type":"string","enum":["n","iy","pv","pmt","fv"],"description":"Echo of which variable was solved for."},"result":{"type":"number","description":"The solved value (iy in percent, rounded to 4 decimals; n/pv/pmt/fv rounded to 2)."},"compoundingPerYear":{"type":"integer","description":"Compounding periods per year used (default 1)."},"annuityDue":{"type":"boolean","description":"True if payments were treated as beginning-of-period (annuity due)."}},"required":["solveFor","result","compoundingPerYear","annuityDue"],"additionalProperties":false}}},"example":{"tool":"tvm_solve","result":{"solveFor":"fv","result":50969.84,"compoundingPerYear":12,"annuityDue":false}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/compound_interest":{"post":{"operationId":"compound_interest","summary":"Use this when you need to project a principal's growth under compound interest, optionally with recurring monthly contributions, returning the final balance and a year-by-year breakdown rather than estimating compound growth yourself.","description":"Use this when you need to project a principal's growth under compound interest, optionally with recurring monthly contributions, returning the final balance and a year-by-year breakdown rather than estimating compound growth yourself. Iterates month-by-month for accuracy; contributions are added at the start of each month; compoundingPerYear defaults to 12. annualRate is a percent; years is 1-200. Deterministic: same input, same output. Example: principal 10000, annualRate 7, years 10, monthlyContribution 500 -> finalBalance 107143.85, totalContributions 70000, totalInterest 37143.85, with a 10-row schedule (one per year).","x-read-only":true,"tags":["finance"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"principal":{"type":"number","description":"Starting principal (>= 0)."},"annualRate":{"type":"number","description":"Annual interest rate as a percent."},"years":{"type":"integer","minimum":1,"maximum":200,"description":"Number of years."},"compoundingPerYear":{"type":"integer","minimum":1,"description":"Compounding periods per year (default 12)."},"monthlyContribution":{"type":"number","description":"Amount added at the start of each month (default 0)."}},"required":["principal","annualRate","years"]},"example":{"principal":1000,"annualRate":5,"years":2,"compoundingPerYear":12,"monthlyContribution":100}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"finalBalance":{"type":"number","description":"Ending balance after all years."},"totalContributions":{"type":"number","description":"Principal plus all monthly contributions."},"totalInterest":{"type":"number","description":"Interest earned = finalBalance - totalContributions."},"schedule":{"type":"array","description":"One row per year.","items":{"type":"object","properties":{"year":{"type":"integer","description":"1-based year number."},"startingBalance":{"type":"number","description":"Balance at the start of the year."},"interest":{"type":"number","description":"Interest credited during the year."},"contributions":{"type":"number","description":"Contributions added during the year."},"endingBalance":{"type":"number","description":"Balance at year end."}},"required":["year","startingBalance","interest","contributions","endingBalance"],"additionalProperties":false}}},"required":["finalBalance","totalContributions","totalInterest","schedule"],"additionalProperties":false}}},"example":{"tool":"compound_interest","result":{"finalBalance":3634.03,"totalContributions":3400,"totalInterest":234.03,"schedule":[{"year":1,"startingBalance":1000,"interest":84.16,"contributions":1200,"endingBalance":2284.16},{"year":2,"startingBalance":2284.16,"interest":149.86,"contributions":1200,"endingBalance":3634.03}]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/apr_calc":{"post":{"operationId":"apr_calc","summary":"Use this when you need an exact loan APR or APY rather than an approximation.","description":"Use this when you need an exact loan APR or APY rather than an approximation. Two modes. mode=\"loan\" (default): solve the true APR of an installment loan from amount financed, monthly payment, term, and upfront fees — Reg-Z style, fees discounted against the amount received, solved by Newton-Raphson. mode=\"rate\": convert a nominal annual rate to APY for a given compounding frequency. Deterministic: same input, same output. Example: mode=\"loan\", loanAmount=20000, monthlyPayment=450, termMonths=60, fees=500 -> apr=13.6301, totalInterest=7000. Prefer this over estimating APR/APY by hand.","x-read-only":true,"tags":["finance"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"mode":{"type":"string","enum":["rate","loan"],"description":"\"rate\" for nominal->APY, \"loan\" for loan APR (default loan)."},"nominalRate":{"type":"number","description":"[rate mode] Nominal annual rate as a percent."},"compoundingPerYear":{"type":"integer","minimum":1,"description":"[rate mode] Compounding periods per year."},"loanAmount":{"type":"number","description":"[loan mode] Amount financed."},"monthlyPayment":{"type":"number","description":"[loan mode] Payment per month."},"termMonths":{"type":"integer","minimum":1,"description":"[loan mode] Number of monthly payments."},"fees":{"type":"number","description":"[loan mode] Upfront finance charge/fees (default 0)."}},"required":[]},"example":{"mode":"loan","loanAmount":20000,"monthlyPayment":450,"termMonths":60,"fees":500}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"mode":{"type":"string","enum":["rate","loan"],"description":"Which calculation was performed."},"nominalRate":{"type":"number","description":"(rate mode) The nominal annual rate that was supplied, as a percent."},"compoundingPerYear":{"type":"integer","description":"(rate mode) Compounding periods per year used for the APY."},"apy":{"type":"number","description":"(rate mode) Annual percentage yield as a percent, rounded to 4 decimals."},"apr":{"type":"number","description":"(loan mode) True annual percentage rate as a percent, rounded to 4 decimals."},"monthlyRate":{"type":"number","description":"(loan mode) Periodic (monthly) rate as a percent, rounded to 6 decimals."},"totalPaid":{"type":"number","description":"(loan mode) monthlyPayment x termMonths, rounded to cents."},"totalInterest":{"type":"number","description":"(loan mode) totalPaid minus loanAmount, rounded to cents."}},"required":["mode"]}}},"example":{"tool":"apr_calc","result":{"mode":"loan","apr":13.6301,"monthlyRate":1.135841,"totalPaid":27000,"totalInterest":7000}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/interest_rate":{"post":{"operationId":"interest_rate","summary":"Use this when you need the exact interest rate that grows a principal to a target amount over a set number of years.","description":"Use this when you need the exact interest rate that grows a principal to a target amount over a set number of years. type=\"compound\" (default) uses the closed-form nth-root formula for the given compounding frequency; type=\"simple\" uses linear growth. Requires target greater than principal and all values positive. Returns the annual rate as a percent plus the interest earned and the growth multiple; compoundingPerYear is null for simple interest. Deterministic: same input, same output. Example: principal=1000, target=2000, years=10, compoundingPerYear=12 -> ratePercent=6.9515, growthMultiple=2. Prefer this over trial-and-error.","x-read-only":true,"tags":["finance"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"type":{"type":"string","enum":["simple","compound"],"description":"Interest model (default compound)."},"principal":{"type":"number","description":"Starting amount (positive)."},"target":{"type":"number","description":"Ending amount (must exceed principal)."},"years":{"type":"number","description":"Time horizon in years (positive)."},"compoundingPerYear":{"type":"integer","minimum":1,"description":"Compounding periods per year for compound mode (default 1)."}},"required":["principal","target","years"]},"example":{"type":"compound","principal":1000,"target":2000,"years":10,"compoundingPerYear":12}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"type":{"type":"string","enum":["simple","compound"],"description":"Interest model used."},"ratePercent":{"type":"number","description":"Solved annual interest rate as a percent, rounded to 4 decimals."},"compoundingPerYear":{"type":["integer","null"],"description":"Compounding periods per year for compound mode; null for simple interest."},"interestEarned":{"type":"number","description":"target minus principal, rounded to cents."},"growthMultiple":{"type":"number","description":"target divided by principal, rounded to 4 decimals."}},"required":["type","ratePercent","compoundingPerYear","interestEarned","growthMultiple"],"additionalProperties":false}}},"example":{"tool":"interest_rate","result":{"type":"compound","ratePercent":6.9515,"compoundingPerYear":12,"interestEarned":1000,"growthMultiple":2}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/hash_text":{"post":{"operationId":"hash_text","summary":"Use this when you need the exact SHA-1, SHA-256, and/or SHA-512 hex digest of a UTF-8 string — never recall or guess a hash, since digests cannot be produced from memory.","description":"Use this when you need the exact SHA-1, SHA-256, and/or SHA-512 hex digest of a UTF-8 string — never recall or guess a hash, since digests cannot be produced from memory. Deterministic: same input, same output. Pass `algorithm` for a single digest or `algorithms` for a subset; the default computes all three. The empty string is valid, and `byteLength` reports the UTF-8 encoded byte length of the input. Example: text \"hello\" with algorithm SHA-256 -> byteLength 5, hashes[\"SHA-256\"] = \"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824\".","x-read-only":true,"tags":["developer"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"text":{"type":"string","description":"The text to hash (UTF-8; empty string is allowed)."},"algorithm":{"type":"string","enum":["SHA-1","SHA-256","SHA-512"],"description":"A single algorithm to compute."},"algorithms":{"type":"array","items":{"type":"string","enum":["SHA-1","SHA-256","SHA-512"]},"description":"Multiple algorithms (default: all three)."}},"required":["text"]},"example":{"text":"hello","algorithm":"SHA-256"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"byteLength":{"type":"integer","description":"UTF-8 encoded byte length of the input text (0 for the empty string)."},"hashes":{"type":"object","description":"Lowercase hex digest keyed by algorithm; only the requested algorithms appear.","properties":{"SHA-1":{"type":"string","description":"40-char lowercase hex SHA-1 digest."},"SHA-256":{"type":"string","description":"64-char lowercase hex SHA-256 digest."},"SHA-512":{"type":"string","description":"128-char lowercase hex SHA-512 digest."}},"additionalProperties":false}},"required":["byteLength","hashes"],"additionalProperties":false}}},"example":{"tool":"hash_text","result":{"byteLength":5,"hashes":{"SHA-256":"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"}}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/diff_text":{"post":{"operationId":"diff_text","summary":"Use this when you need an exact line-level diff between two blocks of text instead of eyeballing the differences.","description":"Use this when you need an exact line-level diff between two blocks of text instead of eyeballing the differences. Deterministic: same input, same output. Computes a longest-common-subsequence diff and returns every line tagged added, removed, or unchanged, plus per-category counts. Example: original \"a\\nb\", modified \"a\\nB\" -> added 1, removed 1, unchanged 1, with lines [{type:\"unchanged\",text:\"a\"},{type:\"removed\",text:\"b\"},{type:\"added\",text:\"B\"}]. Trailing edits produce separate removed+added lines rather than an in-place change. Inputs whose line-count product exceeds 4,000,000 are rejected as too large to diff.","x-read-only":true,"tags":["text"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"original":{"type":"string","description":"Original text."},"modified":{"type":"string","description":"Modified text."}},"required":["original","modified"]},"example":{"original":"a\nb","modified":"a\nB"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["added","removed","unchanged","lines"],"properties":{"added":{"type":"integer","description":"Number of lines present only in the modified text."},"removed":{"type":"integer","description":"Number of lines present only in the original text."},"unchanged":{"type":"integer","description":"Number of lines common to both texts."},"lines":{"type":"array","description":"The full diff in order, one entry per line.","items":{"type":"object","additionalProperties":false,"required":["type","text"],"properties":{"type":{"type":"string","enum":["added","removed","unchanged"],"description":"How this line differs between the two texts."},"text":{"type":"string","description":"The line content (without its trailing newline)."}}}}}}}},"example":{"tool":"diff_text","result":{"added":1,"removed":1,"unchanged":1,"lines":[{"type":"unchanged","text":"a"},{"type":"removed","text":"b"},{"type":"added","text":"B"}]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/test_regex":{"post":{"operationId":"test_regex","summary":"Use this when you need the true matches of a JavaScript regular expression rather than predicting regex behavior yourself, which is easy to get wrong.","description":"Use this when you need the true matches of a JavaScript regular expression rather than predicting regex behavior yourself, which is easy to get wrong. Deterministic: same input, same output. Returns every match with its index, length, matched text, positional capture groups (null for a group that didn't participate), and named groups (an object, or null when the pattern has none). Without the g flag only the first match is returned; with g all matches are collected, capped at 10,000 with truncated=true. Inputs are length-bounded (pattern 2,000 chars, text 50,000 chars) as a ReDoS guard. Example: pattern (?<y>\\d{4})-(?<m>\\d{2}) over \"2024-01\" with flag g -> matchCount 1, match \"2024-01\", groups [\"2024\",\"01\"], named {y:\"2024\",m:\"01\"}.","x-read-only":true,"tags":["developer"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"pattern":{"type":"string","description":"Regular expression source (no delimiters)."},"text":{"type":"string","description":"String to search."},"flags":{"type":"string","description":"Any of g i m s u v y."}},"required":["pattern","text"]},"example":{"pattern":"(?<y>\\d{4})-(?<m>\\d{2})","text":"2024-01","flags":"g"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["pattern","flags","matchCount","truncated","matches"],"properties":{"pattern":{"type":"string","description":"The regex source that was compiled (echoed back)."},"flags":{"type":"string","description":"The flags applied (subset of g i m s u v y)."},"matchCount":{"type":"integer","description":"Number of matches returned in the matches array."},"truncated":{"type":"boolean","description":"True if the 10,000-match cap was hit and further matches were dropped."},"matches":{"type":"array","description":"One entry per match, in order of appearance.","items":{"type":"object","additionalProperties":false,"required":["index","length","match","groups","named"],"properties":{"index":{"type":"integer","description":"Zero-based start offset of the match in the text."},"length":{"type":"integer","description":"Length of the matched substring."},"match":{"type":"string","description":"The full matched substring (group 0)."},"groups":{"type":"array","description":"Positional capture groups 1..n; a non-participating group is null.","items":{"type":["string","null"]}},"named":{"type":["object","null"],"description":"Named capture groups as name->value, or null when the pattern defines none; a non-participating named group is null.","additionalProperties":{"type":["string","null"]}}}}}}}}},"example":{"tool":"test_regex","result":{"pattern":"(?<y>\\d{4})-(?<m>\\d{2})","flags":"g","matchCount":1,"truncated":false,"matches":[{"index":0,"length":7,"match":"2024-01","groups":["2024","01"],"named":{"y":"2024","m":"01"}}]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/convert_data":{"post":{"operationId":"convert_data","summary":"Use this when you need to convert tabular data between JSON (array of objects), CSV, TSV, and XML instead of hand-transforming it.","description":"Use this when you need to convert tabular data between JSON (array of objects), CSV, TSV, and XML instead of hand-transforming it. Deterministic: same input, same output. Handles quoted CSV fields (embedded commas, escaped \"\" quotes), flattens nested objects into dotted keys (b.x), and takes the union of keys across all rows so ragged data still lines up in columns. CSV/TSV input needs a header row plus at least one data row; JSON input must be an array of objects. Example: {from:'csv', to:'json'} on \"name,age\\nAda,36\\nGrace,45\" -> rowCount 2 and an output JSON array of two objects. Returns the input/output formats, the parsed row count, and the serialized output document as a string.","x-read-only":true,"tags":["data"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"data":{"type":"string","description":"The source document as text."},"from":{"type":"string","enum":["json","csv","tsv","xml"],"description":"Input format."},"to":{"type":"string","enum":["json","csv","tsv","xml"],"description":"Output format."}},"required":["data","from","to"]},"example":{"data":"name,age\nAda,36\nGrace,45","from":"csv","to":"json"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"properties":{"from":{"type":"string","enum":["json","csv","tsv","xml"],"description":"Echo of the input format."},"to":{"type":"string","enum":["json","csv","tsv","xml"],"description":"Echo of the target format."},"rowCount":{"type":"integer","description":"Number of data rows (objects) parsed from the input."},"output":{"type":"string","description":"The converted document as text in the target format (JSON is pretty-printed with 2-space indent)."}},"required":["from","to","rowCount","output"]}}},"example":{"tool":"convert_data","result":{"from":"csv","to":"json","rowCount":2,"output":"[\n  {\n    \"name\": \"Ada\",\n    \"age\": \"36\"\n  },\n  {\n    \"name\": \"Grace\",\n    \"age\": \"45\"\n  }\n]"}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/luhn_validate":{"post":{"operationId":"luhn_validate","summary":"Use this when you need to check whether a number passes the Luhn (mod-10) checksum used by credit cards and many identifier numbers, instead of computing the doubling-and-summing by hand.","description":"Use this when you need to check whether a number passes the Luhn (mod-10) checksum used by credit cards and many identifier numbers, instead of computing the doubling-and-summing by hand. Deterministic: same input, same output. Spaces and dashes are stripped first; any remaining non-digit character is an error. Returns the boolean result plus the full per-digit working, one steps entry per digit in original order (the digit, its transformed value after Luhn doubling, and whether that position was doubled). Example: {number:'4539 1488 0343 6467'} -> valid true, sum 80, mod10 0, digitCount 16.","x-read-only":true,"tags":["developer"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"number":{"type":"string","description":"Digits to validate (spaces and dashes are ignored)."}},"required":["number"]},"example":{"number":"79927398713"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"properties":{"number":{"type":"string","description":"Input with spaces and dashes removed (digits only)."},"valid":{"type":"boolean","description":"True when sum is divisible by 10 (mod10 === 0)."},"sum":{"type":"integer","description":"Sum of the transformed digits."},"mod10":{"type":"integer","description":"sum modulo 10; 0 exactly when valid."},"digitCount":{"type":"integer","description":"Number of digits validated."},"steps":{"type":"array","description":"Per-digit working, one entry per digit in original left-to-right order.","items":{"type":"object","additionalProperties":false,"properties":{"digit":{"type":"integer","description":"The original digit (0-9)."},"transformed":{"type":"integer","description":"Value used in the sum: doubled digits have 9 subtracted when the double exceeds 9, otherwise the digit is unchanged."},"doubled":{"type":"boolean","description":"Whether this position was doubled (every second digit from the right)."}},"required":["digit","transformed","doubled"]}}},"required":["number","valid","sum","mod10","digitCount","steps"]}}},"example":{"tool":"luhn_validate","result":{"number":"79927398713","valid":true,"sum":70,"mod10":0,"digitCount":11,"steps":[{"digit":7,"transformed":7,"doubled":false},{"digit":9,"transformed":9,"doubled":true},{"digit":9,"transformed":9,"doubled":false},{"digit":2,"transformed":4,"doubled":true},{"digit":7,"transformed":7,"doubled":false},{"digit":3,"transformed":6,"doubled":true},{"digit":9,"transformed":9,"doubled":false},{"digit":8,"transformed":7,"doubled":true},{"digit":7,"transformed":7,"doubled":false},{"digit":1,"transformed":2,"doubled":true},{"digit":3,"transformed":3,"doubled":false}]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/convert_units":{"post":{"operationId":"convert_units","summary":"Use this when you need to convert a value between units within one category (length, weight, temperature, volume, data, speed, area, time) instead of doing conversion arithmetic yourself.","description":"Use this when you need to convert a value between units within one category (length, weight, temperature, volume, data, speed, area, time) instead of doing conversion arithmetic yourself. Deterministic: same input, same output. Temperature uses the correct offset formulas (Celsius/Fahrenheit/Kelvin), not a naive ratio, and unit keys are case-sensitive (e.g. km, lb, celsius, MB) while the category name is case-insensitive. Example: {category:'length', value:5, from:'km', to:'mile'} -> result 3.10685596119. Returns the echoed category/value/from/to plus the converted result, rounded to 12 significant figures (switching to exponential for very large or very small magnitudes).","x-read-only":true,"tags":["data"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"category":{"type":"string","enum":["length","weight","temperature","volume","data","speed","area","time"],"description":"Unit category."},"value":{"type":"number","description":"The numeric value to convert."},"from":{"type":"string","description":"Source unit key (e.g. km, lb, celsius, MB)."},"to":{"type":"string","description":"Target unit key."}},"required":["category","value","from","to"]},"example":{"category":"length","value":5,"from":"km","to":"mile"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"properties":{"category":{"type":"string","enum":["length","weight","temperature","volume","data","speed","area","time"],"description":"Echo of the unit category, lowercased."},"value":{"type":"number","description":"Echo of the input value (coerced to a number)."},"from":{"type":"string","description":"Echo of the source unit key."},"to":{"type":"string","description":"Echo of the target unit key."},"result":{"type":"number","description":"Converted value, rounded to 12 significant figures (magnitudes >= 1e15 or < 1e-10 use exponential rounding to ~7 significant figures)."}},"required":["category","value","from","to","result"]}}},"example":{"tool":"convert_units","result":{"category":"length","value":5,"from":"km","to":"mile","result":3.10685596119}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/uuid_v4":{"post":{"operationId":"uuid_v4","summary":"Use this when you need RFC 4122 version-4 UUIDs — always generate them here rather than fabricating one, so the version and variant bits and the randomness are correct.","description":"Use this when you need RFC 4122 version-4 UUIDs — always generate them here rather than fabricating one, so the version and variant bits and the randomness are correct. Cryptographically random via Web Crypto (NOT deterministic): every call returns fresh values. `count` (default 1, max 100) sets how many; `uppercase` returns upper-case hex (lowercase by default). Example: {count: 1} -> count 1, uuids[0] = \"2c549b2f-497c-47fc-a1b6-c24bc69667e5\".","x-read-only":true,"tags":["random"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"count":{"type":"integer","minimum":1,"maximum":100,"description":"How many UUIDs to generate (default 1)."},"uppercase":{"type":"boolean","description":"Return uppercase hex (default false)."}},"required":[]},"example":{}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"count":{"type":"integer","description":"Number of UUIDs generated (matches uuids.length)."},"uuids":{"type":"array","description":"The generated RFC 4122 v4 UUIDs, length = count.","items":{"type":"string","description":"A v4 UUID; lowercase hex unless uppercase=true."}}},"required":["count","uuids"],"additionalProperties":false}}},"example":{"tool":"uuid_v4","result":{"count":1,"uuids":["2c549b2f-497c-47fc-a1b6-c24bc69667e5"]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/random_number":{"post":{"operationId":"random_number","summary":"Use this when you need to draw cryptographically secure random integers or decimals in a range using unbiased rejection sampling — prefer it over inventing 'random' numbers, which are neither uniform nor safe.","description":"Use this when you need to draw cryptographically secure random integers or decimals in a range using unbiased rejection sampling — prefer it over inventing 'random' numbers, which are neither uniform nor safe. Cryptographically random via Web Crypto (NOT deterministic). Bounds are inclusive; a reversed min/max is auto-swapped; integer mode rounds the bounds inward; `decimals` sets the decimal places in decimal mode. `count` (default 1, max 100). The returned `min`/`max` are the normalized effective bounds. Example: {min: 1, max: 6} -> type \"integer\", min 1, max 6, count 1, values [2].","x-read-only":true,"tags":["random"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"min":{"type":"number","description":"Lower bound (inclusive)."},"max":{"type":"number","description":"Upper bound (inclusive for integers)."},"count":{"type":"integer","minimum":1,"maximum":100,"description":"How many numbers (default 1)."},"type":{"type":"string","enum":["integer","decimal"],"description":"Number type (default integer)."},"decimals":{"type":"integer","minimum":0,"maximum":10,"description":"Decimal places for decimal type (default 0)."}},"required":["min","max"]},"example":{"min":1,"max":6}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"type":{"type":"string","enum":["integer","decimal"],"description":"Whether integers or decimals were drawn."},"min":{"type":"number","description":"Effective lower bound after normalization (inclusive)."},"max":{"type":"number","description":"Effective upper bound after normalization (inclusive for integers)."},"count":{"type":"integer","description":"Number of values drawn (matches values.length)."},"values":{"type":"array","description":"The generated numbers, length = count.","items":{"type":"number"}}},"required":["type","min","max","count","values"],"additionalProperties":false}}},"example":{"tool":"random_number","result":{"type":"integer","min":1,"max":6,"count":1,"values":[2]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/generate_password":{"post":{"operationId":"generate_password","summary":"Use this when you need strong passwords with Web Crypto randomness: mode=\"random\" builds character-set passwords (length plus uppercase/lowercase/numbers/symbols toggles); mode=\"memorable\" builds word passphrases (words, separator, addNumber, addSymbol).","description":"Use this when you need strong passwords with Web Crypto randomness: mode=\"random\" builds character-set passwords (length plus uppercase/lowercase/numbers/symbols toggles); mode=\"memorable\" builds word passphrases (words, separator, addNumber, addSymbol). Cryptographically random via Web Crypto (NOT deterministic). For test/dev fixtures — for real credentials prefer the client-side web tool at clean.tools/password-generator/ so the password never crosses the network. Example: {mode: \"random\", length: 16} -> mode \"random\", length 16, count 1, passwords [\"NXqtsn6MrsfG9d2i\"].","x-read-only":true,"tags":["random"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"mode":{"type":"string","enum":["random","memorable"],"description":"Password style (default random)."},"count":{"type":"integer","minimum":1,"maximum":10,"description":"How many passwords (default 1)."},"length":{"type":"integer","minimum":4,"maximum":128,"description":"[random] Character length (default 16)."},"uppercase":{"type":"boolean","description":"[random] Include A-Z (default true)."},"lowercase":{"type":"boolean","description":"[random] Include a-z (default true)."},"numbers":{"type":"boolean","description":"[random] Include 0-9 (default true)."},"symbols":{"type":"boolean","description":"[random] Include symbols (default false)."},"words":{"type":"integer","minimum":3,"maximum":8,"description":"[memorable] Number of words (default 4)."},"separator":{"type":"string","description":"[memorable] Word separator (default \"-\")."},"addNumber":{"type":"boolean","description":"[memorable] Insert a random number (default true)."},"addSymbol":{"type":"boolean","description":"[memorable] Insert a random symbol (default false)."}},"required":[]},"example":{"mode":"random","length":16}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"mode":{"type":"string","enum":["random","memorable"],"description":"Which generator produced the passwords."},"count":{"type":"integer","description":"Number of passwords generated (matches passwords.length)."},"passwords":{"type":"array","description":"The generated passwords/passphrases, length = count.","items":{"type":"string"}},"length":{"type":"integer","description":"[random mode only] character length of each password."},"words":{"type":"integer","description":"[memorable mode only] number of words in each passphrase."}},"required":["mode","count","passwords"]}}},"example":{"tool":"generate_password","result":{"mode":"random","length":16,"count":1,"passwords":["NXqtsn6MrsfG9d2i"]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/decode_jwt":{"post":{"operationId":"decode_jwt","summary":"Use this when you need to decode (NOT verify) a JSON Web Token: base64url-decode the header and payload, surface standard claims, and report expiry — prefer it over reading a JWT by eye.","description":"Use this when you need to decode (NOT verify) a JSON Web Token: base64url-decode the header and payload, surface standard claims, and report expiry — prefer it over reading a JWT by eye. The decode is deterministic, but `expired`/`notYetValid` are compared against the current clock unless you pass `now` (ISO) to pin the reference time. The signature is never checked (`signatureVerified` is always false), so never authorize anything based on the output. `expired`/`notYetValid` are booleans when `exp`/`nbf` are present in the payload, otherwise null. Example: a token with sub \"1234567890\" and a far-future exp -> signatureVerified false, expired false, claims.subject \"1234567890\".","x-read-only":true,"tags":["developer"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"token":{"type":"string","description":"The compact JWT string (header.payload.signature)."},"now":{"type":"string","description":"Optional ISO time for the expiry check (default: current time)."}},"required":["token"]},"example":{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaXNzIjoiY2xlYW4udG9vbHMiLCJhdWQiOiJhcGkiLCJpYXQiOjE1MTYyMzkwMjIsIm5iZiI6MTUxNjIzOTAyMiwiZXhwIjo0MTAyNDQ0ODAwfQ.SIGabc123"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"signatureVerified":{"type":"boolean","description":"Always false — this tool never checks the signature."},"header":{"type":"object","description":"Decoded JWT header (parsed JSON; arbitrary fields, e.g. alg, typ).","additionalProperties":true},"payload":{"type":"object","description":"Decoded JWT payload/claims (parsed JSON; arbitrary fields).","additionalProperties":true},"claims":{"type":"object","description":"Normalized standard claims; each key appears only when the corresponding claim is present in the payload.","properties":{"issuer":{"type":"string","description":"iss claim."},"subject":{"type":"string","description":"sub claim."},"audience":{"description":"aud claim, copied verbatim (a string or an array of strings)."},"issuedAt":{"type":"string","description":"iat converted to ISO 8601."},"notBefore":{"type":"string","description":"nbf converted to ISO 8601."},"expires":{"type":"string","description":"exp converted to ISO 8601."}},"additionalProperties":false},"expired":{"type":["boolean","null"],"description":"Whether now is past exp; null when the payload has no numeric exp."},"notYetValid":{"type":["boolean","null"],"description":"Whether now is before nbf; null when the payload has no numeric nbf."},"segments":{"type":"integer","enum":[2,3],"description":"Number of dot-separated segments in the token (2 or 3)."}},"required":["signatureVerified","header","payload","claims","expired","notYetValid","segments"],"additionalProperties":false}}},"example":{"tool":"decode_jwt","result":{"signatureVerified":false,"header":{"alg":"HS256","typ":"JWT"},"payload":{"sub":"1234567890","name":"Alice","iss":"clean.tools","aud":"api","iat":1516239022,"nbf":1516239022,"exp":4102444800},"claims":{"issuer":"clean.tools","subject":"1234567890","audience":"api","issuedAt":"2018-01-18T01:30:22.000Z","notBefore":"2018-01-18T01:30:22.000Z","expires":"2100-01-01T00:00:00.000Z"},"expired":false,"notYetValid":false,"segments":3}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/encode_decode":{"post":{"operationId":"encode_decode","summary":"Use this when you need to encode or decode text and want multi-byte and entity edge cases handled correctly rather than doing it by hand.","description":"Use this when you need to encode or decode text and want multi-byte and entity edge cases handled correctly rather than doing it by hand. Deterministic: same input, same output. The mode selects the operation: url-encode/url-decode (percent-encoding), html-encode/html-decode (entity table plus numeric character references), base64-encode/base64-decode (UTF-8 safe; decode tolerates URL-safe alphabet, whitespace, and missing padding), and unicode-encode/unicode-decode (\\uXXXX and \\u{...} escapes for non-ASCII). Every mode returns the same shape: {mode, output}. Example: mode base64-encode, text \"héllo\" -> output \"aMOpbGxv\".","x-read-only":true,"tags":["developer"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"mode":{"type":"string","enum":["url-encode","url-decode","html-encode","html-decode","base64-encode","base64-decode","unicode-encode","unicode-decode"],"description":"Operation to perform."},"text":{"type":"string","description":"Input text."}},"required":["mode","text"]},"example":{"mode":"base64-encode","text":"héllo"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","required":["mode","output"],"properties":{"mode":{"type":"string","enum":["url-encode","url-decode","html-encode","html-decode","base64-encode","base64-decode","unicode-encode","unicode-decode"],"description":"The operation that was performed (echoed back)."},"output":{"type":"string","description":"The encoded or decoded result string."}}}}},"example":{"tool":"encode_decode","result":{"mode":"base64-encode","output":"aMOpbGxv"}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/count_text":{"post":{"operationId":"count_text","summary":"Use this when you need exact word, character, sentence, and paragraph counts instead of estimating.","description":"Use this when you need exact word, character, sentence, and paragraph counts instead of estimating. Deterministic: same input, same output. Uses Unicode-aware segmentation (Intl.Segmenter): characters are grapheme clusters, so an emoji or an accented letter counts as one; words are word-like segments; sentence splitting is abbreviation-aware (Dr., etc., won't end a sentence). charactersNoSpaces counts graphemes after stripping whitespace, and paragraphs are blocks separated by blank lines. Example: \"Café 👩‍💻!\" -> words 1, characters 7, charactersNoSpaces 6, sentences 1, paragraphs 1. Empty input returns all zeros.","x-read-only":true,"tags":["text"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"text":{"type":"string","description":"Text to analyze."}},"required":["text"]},"example":{"text":"Café 👩‍💻!"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["words","characters","charactersNoSpaces","sentences","paragraphs"],"properties":{"words":{"type":"integer","description":"Count of word-like segments."},"characters":{"type":"integer","description":"Count of grapheme clusters (user-perceived characters), including whitespace."},"charactersNoSpaces":{"type":"integer","description":"Grapheme count after removing all whitespace."},"sentences":{"type":"integer","description":"Sentence count using abbreviation-aware segmentation."},"paragraphs":{"type":"integer","description":"Count of non-empty blocks separated by blank lines."}}}}},"example":{"tool":"count_text","result":{"words":1,"characters":7,"charactersNoSpaces":6,"sentences":1,"paragraphs":1}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/generate_qr":{"post":{"operationId":"generate_qr","summary":"Use this when you need to turn text or a URL into a real, scannable QR code rather than describing one.","description":"Use this when you need to turn text or a URL into a real, scannable QR code rather than describing one. Deterministic: same input, same output. Byte mode, error-correction level M, versions 1-10 auto-selected by length (up to 213 bytes); the encoder scores all 8 mask patterns and keeps the lowest-penalty one. Returns both the module matrix as rows of 0/1 (1 = dark module) and a ready-to-render self-contained SVG string. moduleSize sets SVG pixels per module (default 10) and quietZone the border width in modules (default 4). Example: {text:'HELLO'} -> version 1, size 21x21, byteLength 5. Longer text auto-bumps the version and matrix size; over 213 bytes returns an error.","x-read-only":true,"tags":["developer"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"text":{"type":"string","description":"Text/URL to encode (up to 213 bytes)."},"moduleSize":{"type":"integer","minimum":1,"maximum":40,"description":"SVG pixels per module (default 10)."},"quietZone":{"type":"integer","minimum":0,"maximum":16,"description":"Quiet-zone module border (default 4)."}},"required":["text"]}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"properties":{"text":{"type":"string","description":"Echo of the encoded text."},"version":{"type":"integer","minimum":1,"maximum":10,"description":"Auto-selected QR version (1-10)."},"errorCorrectionLevel":{"type":"string","enum":["M"],"description":"Always \"M\" (medium, ~15% recovery)."},"mask":{"type":"integer","minimum":0,"maximum":7,"description":"Chosen mask pattern (0-7), the one with the lowest penalty score."},"size":{"type":"integer","description":"Matrix dimension in modules (version*4+17, so 21-57)."},"byteLength":{"type":"integer","description":"UTF-8 byte length of the encoded text."},"quietZone":{"type":"integer","description":"Quiet-zone border width in modules (echo of input, clamped 0-16; default 4)."},"moduleSize":{"type":"integer","description":"SVG pixels per module (echo of input, clamped 1-40; default 10)."},"matrix":{"type":"array","description":"size x size grid; each row is an array of 0/1 where 1 is a dark module. Excludes the quiet-zone border.","items":{"type":"array","items":{"type":"integer","enum":[0,1]}}},"svg":{"type":"string","description":"Self-contained SVG document string rendering the QR code (includes the quiet zone and moduleSize scaling): a white background rect plus a #000000 group of one rect per dark module."}},"required":["text","version","errorCorrectionLevel","mask","size","byteLength","quietZone","moduleSize","matrix","svg"]}}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/convert_case":{"post":{"operationId":"convert_case","summary":"Use this when you need to re-case text into a specific naming or letter case.","description":"Use this when you need to re-case text into a specific naming or letter case. Given `text` and a target `case` (upper, lower, title, sentence, camel, snake, kebab, or constant), returns the converted string. Smart word tokenization splits camelCase, snake_case, kebab-case, and whitespace, so a phrase in any style re-cases consistently; title case honors an editorial stop-word list and preserves ALL-CAPS acronyms. Empty text returns an empty result. Deterministic: same input, same output. Example: {text: \"myVariableName\", case: \"constant\"} -> result \"MY_VARIABLE_NAME\".","x-read-only":true,"tags":["text"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"text":{"type":"string","description":"The input text to convert."},"case":{"type":"string","enum":["upper","lower","title","sentence","camel","snake","kebab","constant"],"description":"Target case: upper (UPPER CASE), lower (lower case), title (Title Case with stop-words), sentence (Sentence case), camel (camelCase), snake (snake_case), kebab (kebab-case), constant (CONSTANT_CASE)."}},"required":["text","case"]},"example":{"text":"myVariableName","case":"constant"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"case":{"type":"string","description":"The target case that was applied (echoes the requested `case`)."},"result":{"type":"string","description":"The converted text. Empty string if the input text was empty."}},"required":["case","result"],"additionalProperties":false}}},"example":{"tool":"convert_case","result":{"case":"constant","result":"MY_VARIABLE_NAME"}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/fancy_text":{"post":{"operationId":"fancy_text","summary":"Use this when you need to style ASCII letters and digits as Unicode glyphs (bold-serif, italic-serif, bold-italic-serif, bold-sans, script, fraktur, double-struck, monospace, circled, squared, parenthesized, small-caps) for places that lack font control such as social bios or usernames.","description":"Use this when you need to style ASCII letters and digits as Unicode glyphs (bold-serif, italic-serif, bold-italic-serif, bold-sans, script, fraktur, double-struck, monospace, circled, squared, parenthesized, small-caps) for places that lack font control such as social bios or usernames. Pass a `style` and `text` to get the transformed string; characters outside A-Z, a-z, and 0-9 (spaces, punctuation, emoji) pass through unchanged. Omit `style` to receive the list of valid style keys instead of transforming. Deterministic: same input, same output. Example: {style: \"bold-serif\", text: \"Hello 123\"} -> result \"𝐇𝐞𝐥𝐥𝐨 𝟏𝟐𝟑\".","x-read-only":true,"tags":["text"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"text":{"type":"string","description":"The text to transform. ASCII A-Z, a-z, and 0-9 are mapped to styled glyphs; other characters (spaces, punctuation, emoji) pass through unchanged. Required when a style is given."},"style":{"type":"string","enum":["bold-serif","italic-serif","bold-italic-serif","bold-sans","script","fraktur","double-struck","monospace","circled","squared","parenthesized","small-caps"],"description":"The style to apply. Omit entirely to receive the list of valid style keys instead of transforming."}},"required":[]},"example":{"style":"bold-serif","text":"Hello 123"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"style":{"type":"string","description":"Transform mode only: the style that was applied (echoes the requested `style`)."},"result":{"type":"string","description":"Transform mode only: the styled text, with unsupported characters passed through unchanged."},"styles":{"type":"array","items":{"type":"string"},"description":"Discovery mode only (when `style` is omitted): the list of valid style keys."}}}}},"example":{"tool":"fancy_text","result":{"style":"bold-serif","result":"𝐇𝐞𝐥𝐥𝐨 𝟏𝟐𝟑"}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/render_markdown":{"post":{"operationId":"render_markdown","summary":"Use this when you need to convert lightweight Markdown into a sanitized, XSS-safe HTML fragment to preview or embed, matching the Clean.tools markdown preview.","description":"Use this when you need to convert lightweight Markdown into a sanitized, XSS-safe HTML fragment to preview or embed, matching the Clean.tools markdown preview. Deterministic: same input, same output. Supports headings, bold/italic/strikethrough, inline and fenced code, links and images (http/https/mailto URL schemes only; other schemes become \"#\"), blockquotes, ordered/unordered/task lists, GFM tables, and horizontal rules. All raw angle brackets are HTML-escaped so the output is safe to inject. Example: \"# Hi\\n\\n**bold**\" -> html \"<h1>Hi</h1><p><strong>bold</strong></p>\".","x-read-only":true,"tags":["text"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"markdown":{"type":"string","description":"The Markdown source text to render to HTML."}},"required":["markdown"]},"example":{"markdown":"# Hi\n\n**bold**"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"html":{"type":"string","description":"The rendered HTML fragment. Block elements are concatenated with no separators; inline styles are attached to tables, code, blockquotes, images, and task-list items. No wrapping document element."}},"required":["html"],"additionalProperties":false}}},"example":{"tool":"render_markdown","result":{"html":"<h1>Hi</h1><p><strong>bold</strong></p>"}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/lorem_ipsum":{"post":{"operationId":"lorem_ipsum","summary":"Use this when you need placeholder/filler copy for mockups, tests, or layout.","description":"Use this when you need placeholder/filler copy for mockups, tests, or layout. Given a `mode` (\"paragraphs\", \"sentences\", \"words\", or \"formatted\") and a `count`, cycles a fixed built-in Latin corpus to return the same text every time. `count` is clamped to the mode's max (paragraphs 20, sentences 100, words 500) and defaults to 1 when missing or below 1; \"formatted\" ignores `count` and returns a fixed multi-block sample (with count 0). Deterministic: same input, same output. Example: {mode: \"words\", count: 5} -> text \"Lorem ipsum dolor sit amet.\", count 5.","x-read-only":true,"tags":["text"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"mode":{"type":"string","enum":["paragraphs","sentences","words","formatted"],"description":"Unit of generated text. \"paragraphs\", \"sentences\", or \"words\" repeat the corpus up to the requested count; \"formatted\" returns a fixed multi-block sample as plain text and ignores count."},"count":{"type":"integer","minimum":1,"description":"How many units to generate. Clamped to the mode's max (paragraphs 20, sentences 100, words 500); values below 1 or missing default to 1. Unused for \"formatted\"."}},"required":["mode"]},"example":{"mode":"words","count":5}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"mode":{"type":"string","description":"The generation mode used (echoes the requested `mode`).","enum":["paragraphs","sentences","words","formatted"]},"count":{"type":"integer","description":"The effective number of units generated after clamping/defaulting; always 0 for \"formatted\" mode."},"text":{"type":"string","description":"The generated placeholder text. Paragraphs are separated by blank lines; words mode is capitalized and terminated with a period."}},"required":["mode","count","text"],"additionalProperties":false}}},"example":{"tool":"lorem_ipsum","result":{"mode":"words","count":5,"text":"Lorem ipsum dolor sit amet."}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/redact_text":{"post":{"operationId":"redact_text","summary":"Use this when scrubbing test/dev text: replaces each occurrence of the given terms with block characters (████).","description":"Use this when scrubbing test/dev text: replaces each occurrence of the given terms with block characters (████). Provide `text` plus `terms` (a comma-separated string or an array of strings). By default it matches whole words only using Unicode boundaries (so \"ann\" will not match inside \"annual\") and is case-insensitive; set `caseSensitive` to match exactly, `wholeWords: false` to match substrings, or `fixedWidth: true` to hide each term's length behind a constant-width bar. Returns the redacted text and a replacement count, and never echoes the original terms. Deterministic: same input, same output. Truly sensitive text is better redacted client-side at clean.tools/text-redact/. Example: {text: \"Contact Jane Doe\", terms: \"Jane Doe\"} -> redacted \"Contact ████████\", redactedCount 1.","x-read-only":true,"tags":["text"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"text":{"type":"string","description":"The text to redact terms from."},"terms":{"description":"The sensitive terms to redact, given either as a comma-separated string (e.g. \"Jane Doe, jane@x.com\") or an array of strings.","oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"wholeWords":{"type":"boolean","description":"When true (default), only match terms as whole words using Unicode word boundaries (so \"ann\" will not match inside \"annual\")."},"caseSensitive":{"type":"boolean","description":"When true, match terms case-sensitively. Defaults to false (case-insensitive)."},"fixedWidth":{"type":"boolean","description":"When true, replace every matched term with a constant-width block bar so the redacted length does not leak the original term length. Defaults to false (block length equals term length)."}},"required":["text","terms"]},"example":{"text":"Contact Jane Doe","terms":"Jane Doe"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"redacted":{"type":"string","description":"The text with all matched terms replaced by block characters; identical to the input when no terms matched."},"redactedCount":{"type":"integer","description":"The number of replacements made."}},"required":["redacted","redactedCount"],"additionalProperties":false}}},"example":{"tool":"redact_text","result":{"redacted":"Contact ████████","redactedCount":1}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/format_sql":{"post":{"operationId":"format_sql","summary":"Use this when a user pastes messy or minified SQL and wants it pretty-printed into a readable, canonical layout, or wants keyword casing normalized.","description":"Use this when a user pastes messy or minified SQL and wants it pretty-printed into a readable, canonical layout, or wants keyword casing normalized. Deterministic: same input, same output. Each clause keyword (SELECT, FROM, WHERE, GROUP BY, ...) goes on its own line with its arguments indented beneath it, commas break columns onto new lines, JOIN/AND/OR start fresh lines, and short parenthesised groups stay inline. Optional dialect hint affects identifier quoting (e.g. tsql [brackets]); indent is a spaces count or \"tab\" (default 2); keywordCase is upper/lower/preserve (default upper). Example: \"select id from t\" -> formatted \"SELECT\\n  id\\nFROM\\n  t\". Returns an error for empty or oversized (>200000 chars) input.","x-read-only":true,"tags":["developer"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"sql":{"type":"string","description":"The SQL query text to format. Required, non-empty."},"dialect":{"type":"string","enum":["sql","postgresql","mysql","tsql","bigquery","sqlite"],"description":"SQL dialect hint (affects identifier quoting, e.g. T-SQL [brackets]). Optional; defaults to standard 'sql'."},"indent":{"description":"Indentation: a number of spaces (e.g. 2 or 4) or the string \"tab\" for tab characters. Optional; defaults to 2.","oneOf":[{"type":"integer","minimum":1,"maximum":8},{"type":"string","enum":["2","4","tab"]}]},"keywordCase":{"type":"string","enum":["upper","lower","preserve"],"description":"How to case reserved keywords/functions: UPPER, lower, or preserve as-is. Optional; defaults to 'upper'."}},"required":["sql"]},"example":{"sql":"select id from t"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"formatted":{"type":"string","description":"The reformatted SQL as a single text string, with newlines and indentation applied. Trailing whitespace and leading/trailing blank lines are stripped."}},"required":["formatted"],"additionalProperties":false}}},"example":{"tool":"format_sql","result":{"formatted":"SELECT\n  id\nFROM\n  t"}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/color_palette":{"post":{"operationId":"color_palette","summary":"Use this when you need color-harmony palettes derived from one base hex color.","description":"Use this when you need color-harmony palettes derived from one base hex color. Rotates hue/lightness in HSL to build complementary (+180 degrees), analogous (-30/+30), triadic (+120/+240), split-complementary (+150/+210), and monochromatic (lightness steps) swatch sets, each as an array of hex strings. Accepts a 6-digit hex with or without a leading '#' (case-insensitive). Deterministic: same input, same output. Example: {color:'#e11d48'} -> base '#e11d48', complementary ['#e11d48','#1de1b6'], monochromatic ['#590c1d','#9d1432','#e11d48','#eb607e','#f3a4b5']. Note: grayscale inputs (e.g. '#000000') have no defined hue, so the hue-rotated sets are all identical and only the monochromatic lightness steps differ.","x-read-only":true,"tags":["color"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"color":{"type":"string","description":"A 6-digit hex color, with or without a leading '#', e.g. \"#e11d48\" or \"e11d48\". Case-insensitive."}},"required":["color"]},"example":{"color":"#e11d48"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"base":{"type":"string","description":"Normalized base color as a 6-digit hex string (lowercased, with leading '#')."},"complementary":{"type":"array","items":{"type":"string"},"description":"[base, hue+180] — 2 hex swatches."},"analogous":{"type":"array","items":{"type":"string"},"description":"[hue-30, base, hue+30] — 3 hex swatches."},"triadic":{"type":"array","items":{"type":"string"},"description":"[base, hue+120, hue+240] — 3 hex swatches."},"splitComplementary":{"type":"array","items":{"type":"string"},"description":"[base, hue+150, hue+210] — 3 hex swatches."},"monochromatic":{"type":"array","items":{"type":"string"},"description":"5 hex shades stepping lightness by -30, -15, 0, +15, +30 from the base; the two darker steps are floored at lightness 5 and 10, and the two lighter steps capped at 90 and 95."}},"required":["base","complementary","analogous","triadic","splitComplementary","monochromatic"],"additionalProperties":false}}},"example":{"tool":"color_palette","result":{"base":"#e11d48","complementary":["#e11d48","#1de1b6"],"analogous":["#e11daa","#e11d48","#e1541d"],"triadic":["#e11d48","#48e11d","#1d48e1"],"splitComplementary":["#e11d48","#1de154","#1daae1"],"monochromatic":["#590c1d","#9d1432","#e11d48","#eb607e","#f3a4b5"]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/color_contrast":{"post":{"operationId":"color_contrast","summary":"Use this when checking whether a text/background color pair meets WCAG 2.x accessibility contrast.","description":"Use this when checking whether a text/background color pair meets WCAG 2.x accessibility contrast. Computes the relative-luminance contrast ratio (1-21, rounded to 2 decimals) and returns pass/fail booleans for normal and large text at AA and AAA levels (thresholds 4.5 / 7 / 3 / 4.5), plus a suggested passing foreground hex when normal-text AA fails (null when it already passes or none is found). Accepts 3- or 6-digit hex, with or without a leading '#'; echoed colors are normalized to 6-digit hex. Deterministic: same input, same output. Example: {foreground:'777', background:'fff'} -> ratio 4.48, normalAA false, largeAA true, suggestedForeground '#767676'.","x-read-only":true,"tags":["color"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"foreground":{"type":"string","description":"Foreground (text) color as a 3 or 6 digit HEX string, with or without a leading #, e.g. \"#1a1a1a\" or \"777\"."},"background":{"type":"string","description":"Background color as a 3 or 6 digit HEX string, with or without a leading #, e.g. \"#ffffff\" or \"fff\"."}},"required":["foreground","background"]},"example":{"foreground":"777","background":"fff"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"foreground":{"type":"string","description":"Normalized foreground color as a 6-digit hex string."},"background":{"type":"string","description":"Normalized background color as a 6-digit hex string."},"ratio":{"type":"number","description":"WCAG 2.x contrast ratio, 1 to 21, rounded to 2 decimals."},"normalAA":{"type":"boolean","description":"Unrounded ratio >= 4.5 (normal text, AA)."},"normalAAA":{"type":"boolean","description":"Unrounded ratio >= 7 (normal text, AAA)."},"largeAA":{"type":"boolean","description":"Unrounded ratio >= 3 (large text >=18pt / 14pt bold, AA)."},"largeAAA":{"type":"boolean","description":"Unrounded ratio >= 4.5 (large text, AAA)."},"suggestedForeground":{"type":["string","null"],"description":"A passing foreground hex (>=4.5 vs the background) when normal-text AA fails; null when the pair already passes or no passing value is found."}},"required":["foreground","background","ratio","normalAA","normalAAA","largeAA","largeAAA","suggestedForeground"],"additionalProperties":false}}},"example":{"tool":"color_contrast","result":{"foreground":"#777777","background":"#ffffff","ratio":4.48,"normalAA":false,"normalAAA":false,"largeAA":true,"largeAAA":false,"suggestedForeground":"#767676"}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/css_gradient":{"post":{"operationId":"css_gradient","summary":"Use this when you need a ready-to-paste CSS gradient value from 2-5 hex color stops.","description":"Use this when you need a ready-to-paste CSS gradient value from 2-5 hex color stops. Builds a linear-gradient(...) (default; direction defaults to 'to bottom') or a radial-gradient(circle, ...). Direction accepts keywords ('to right', 'to left', 'to top', 'to bottom'), angles ('45deg', '90deg', '135deg'), or 'to <side> <side>'; it is ignored for radial, and passing type 'radial' (or direction 'radial') forces a radial gradient. Colors accept 6-digit hex with or without a leading '#' and are echoed normalized in order. Deterministic: same input, same output. Example: {colors:['#3b82f6','8b5cf6']} -> css 'linear-gradient(to bottom, #3b82f6, #8b5cf6)', type 'linear'. Radial results omit the 'direction' field.","x-read-only":true,"tags":["color"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"colors":{"type":"array","items":{"type":"string"},"minItems":2,"maxItems":5,"description":"2 to 5 color stops as 6-digit hex strings, with or without a leading '#', e.g. [\"#3b82f6\", \"8b5cf6\"]."},"type":{"type":"string","enum":["linear","radial"],"description":"Gradient type. Defaults to \"linear\". \"radial\" produces radial-gradient(circle, ...)."},"direction":{"type":"string","description":"Linear direction: a keyword (\"to right\", \"to left\", \"to bottom\", \"to top\"), an angle (\"45deg\", \"135deg\", \"90deg\"), or \"to <side> <side>\". Defaults to \"to bottom\". Ignored for radial; passing \"radial\" here forces a radial gradient."}},"required":["colors"]},"example":{"colors":["#3b82f6","8b5cf6"]}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"css":{"type":"string","description":"Ready-to-paste CSS gradient value, e.g. 'linear-gradient(to bottom, #3b82f6, #8b5cf6)' or 'radial-gradient(circle, #3b82f6, #8b5cf6)'."},"type":{"type":"string","enum":["linear","radial"],"description":"Resolved gradient type."},"direction":{"type":"string","description":"Resolved linear direction (keyword or angle). Present only for linear gradients; omitted for radial."},"colors":{"type":"array","items":{"type":"string"},"description":"Normalized 6-digit hex color stops, in input order."}},"required":["css","type","colors"]}}},"example":{"tool":"css_gradient","result":{"css":"linear-gradient(to bottom, #3b82f6, #8b5cf6)","type":"linear","direction":"to bottom","colors":["#3b82f6","#8b5cf6"]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/percentage":{"post":{"operationId":"percentage","summary":"Use this when you want exact, auditable percentage math with a written-out formula.","description":"Use this when you want exact, auditable percentage math with a written-out formula. Three modes: \"of\" computes percent% of value (fields percent, value); \"is-what\" computes what percent x is of y (fields x, y; y must be non-zero); \"change\" computes the percent change from -> to (from must be non-zero) and reports direction as \"increase\" or \"decrease\". Returns the numeric result plus a human-readable formula string. Deterministic: same input, same output. Example: mode=\"change\", from=200, to=250 -> result=25, direction=\"increase\".","x-read-only":true,"tags":["finance"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"mode":{"type":"string","enum":["of","is-what","change"],"description":"Which calculation to perform. \"of\" = percent% of value; \"is-what\" = x is what percent of y; \"change\" = percent change from -> to.","default":"of"},"percent":{"type":"number","description":"(mode \"of\") The percentage to apply, e.g. 15 for 15%."},"value":{"type":"number","description":"(mode \"of\") The base value that the percentage is taken of."},"x":{"type":"number","description":"(mode \"is-what\") The part value."},"y":{"type":"number","description":"(mode \"is-what\") The whole value; must be non-zero."},"from":{"type":"number","description":"(mode \"change\") The starting value; must be non-zero."},"to":{"type":"number","description":"(mode \"change\") The ending value."}},"required":["mode"]},"example":{"mode":"change","from":200,"to":250}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"mode":{"type":"string","enum":["of","is-what","change"],"description":"Which calculation was performed."},"result":{"type":"number","description":"The numeric answer: the computed amount (of), or the percent (is-what, change). May be negative for a decrease."},"formula":{"type":"string","description":"Human-readable statement of what was computed, e.g. \"15% of 80\"."},"percent":{"type":"number","description":"(is-what and change modes) The percent, equal to result; present alongside result for those two modes."},"direction":{"type":"string","enum":["increase","decrease"],"description":"(change mode only) Whether the change was an increase (result >= 0) or a decrease."}},"required":["mode","result","formula"]}}},"example":{"tool":"percentage","result":{"mode":"change","result":25,"percent":25,"direction":"increase","formula":"Change from 200 to 250"}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/tip":{"post":{"operationId":"tip","summary":"Use this when you want penny-accurate tip and bill-split figures.","description":"Use this when you want penny-accurate tip and bill-split figures. Given a bill amount, a tip percentage, and an optional number of people (default 1; values below 1 are treated as 1), returns the tip amount, grand total, and the per-person tip and per-person total, all rounded to cents. Deterministic: same input, same output. Example: bill=120, tipPercent=18, people=4 -> tipAmount=21.6, total=141.6, perPersonTotal=35.4.","x-read-only":true,"tags":["finance"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"bill":{"type":"number","description":"The pre-tip bill amount in dollars. Required. Must be zero or positive."},"tipPercent":{"type":"number","description":"The tip as a percentage of the bill (e.g. 20 for 20%). Required. Must be zero or positive."},"people":{"type":"integer","description":"Number of people splitting the bill. Optional, defaults to 1; values below 1 are treated as 1.","minimum":1}},"required":["bill","tipPercent"]},"example":{"bill":120,"tipPercent":18,"people":4}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","properties":{"bill":{"type":"number","description":"The pre-tip bill amount, rounded to cents."},"tipPercent":{"type":"number","description":"The tip percentage that was applied (echoed as supplied)."},"people":{"type":"integer","description":"Number of people the bill was split across (>= 1)."},"tipAmount":{"type":"number","description":"Total tip in dollars, rounded to cents."},"total":{"type":"number","description":"Bill plus tip in dollars, rounded to cents."},"perPersonTip":{"type":"number","description":"tipAmount divided by people, rounded to cents."},"perPersonTotal":{"type":"number","description":"total divided by people, rounded to cents."}},"required":["bill","tipPercent","people","tipAmount","total","perPersonTip","perPersonTotal"],"additionalProperties":false}}},"example":{"tool":"tip","result":{"bill":120,"tipPercent":18,"people":4,"tipAmount":21.6,"total":141.6,"perPersonTip":5.4,"perPersonTotal":35.4}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/convert_timestamp":{"post":{"operationId":"convert_timestamp","summary":"Use this when you have a timestamp in one form (unix epoch seconds/milliseconds/microseconds, or an ISO-8601 date-time) and need it in the others, or rendered in a specific IANA time zone.","description":"Use this when you have a timestamp in one form (unix epoch seconds/milliseconds/microseconds, or an ISO-8601 date-time) and need it in the others, or rendered in a specific IANA time zone. Auto-detects the input: a number or digit-string is an epoch classified by length (~10 digits = seconds, ~13 = milliseconds, ~16 = microseconds); anything else is parsed as ISO-8601, with a naive date-time (no Z/offset) taken as UTC. Returns unix_s, unix_ms, iso_utc, iso_tz (with numeric offset), a human string, and the weekday, all in the requested zone (default UTC), correctly handling DST transitions and pre-1970 (negative) epochs. Deterministic: same input, same output. Prefer this over doing epoch/timezone arithmetic yourself. Example: { value: 1783454640, timezone: \"America/Chicago\" } -> iso_utc \"2026-07-07T20:04:00.000Z\", human \"Tuesday, July 7, 2026 at 3:04 PM CDT\".","x-read-only":true,"tags":["datetime"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"value":{"type":["string","number"],"description":"The timestamp to convert: a unix epoch (number or digit-string; seconds/milliseconds/microseconds auto-detected by length) or an ISO-8601 date-time (e.g. \"2026-07-07T15:04:00-05:00\" or \"2026-07-07\"). A naive date-time with no zone designator is interpreted as UTC."},"timezone":{"type":"string","description":"IANA time zone used to render iso_tz, human, and weekday, e.g. \"America/Chicago\". Defaults to \"UTC\"."}},"required":["value"]},"example":{"value":1783454640,"timezone":"America/Chicago"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["detected","timezone","unix_s","unix_ms","iso_utc","iso_tz","human","weekday"],"properties":{"detected":{"type":"string","enum":["seconds","milliseconds","microseconds","iso"],"description":"How the input was classified: an epoch band chosen by digit length, or 'iso' when parsed as a date-time string."},"timezone":{"type":"string","description":"IANA time zone used to render iso_tz, human, and weekday (echo of input; default 'UTC')."},"unix_s":{"type":"integer","description":"Unix epoch in whole seconds (negative for pre-1970 instants)."},"unix_ms":{"type":"integer","description":"Unix epoch in milliseconds."},"iso_utc":{"type":"string","description":"The instant in UTC, ISO-8601 with milliseconds and a trailing 'Z'."},"iso_tz":{"type":"string","description":"The instant rendered in the requested zone, ISO-8601 with a numeric offset (e.g. '-05:00')."},"human":{"type":"string","description":"Human-readable local time with weekday, date, time, and zone abbreviation."},"weekday":{"type":"string","description":"Day of week in the requested zone, e.g. 'Tuesday'."}}}}},"example":{"tool":"convert_timestamp","result":{"detected":"seconds","timezone":"America/Chicago","unix_s":1783454640,"unix_ms":1783454640000,"iso_utc":"2026-07-07T20:04:00.000Z","iso_tz":"2026-07-07T15:04:00.000-05:00","human":"Tuesday, July 7, 2026 at 3:04 PM CDT","weekday":"Tuesday"}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/format_json":{"post":{"operationId":"format_json","summary":"Use this when you need to pretty-print, minify, or validate a JSON string and want the exact reformatted text plus warnings about silent data loss.","description":"Use this when you need to pretty-print, minify, or validate a JSON string and want the exact reformatted text plus warnings about silent data loss. Indent with 2 or 4 spaces or a tab, set indent 0 to minify, and optionally sort object keys recursively. It also scans the raw source for two things JSON.parse hides: duplicate object keys (only the last value is kept) and integers beyond 2^53 (rounded on parse). Deterministic: same input, same output. Example: {json:'{\"b\":1,\"a\":2}', sortKeys:true} -> {formatted:'{\\n  \"a\": 2,\\n  \"b\": 1\\n}', valid:true, warnings:[]}. On invalid JSON returns {error, line, column} pointing at the fault. Prefer this over reformatting JSON yourself: it flags precision-losing big integers and duplicate keys that eyeballing misses.","x-read-only":true,"tags":["data"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"json":{"type":"string","description":"The JSON document to format, minify, or validate. Required, capped at 128 KB."},"indent":{"description":"Indentation for pretty-printing: 2 or 4 spaces, \"tab\" for tab characters, or 0 to minify. Optional; defaults to 2.","oneOf":[{"type":"integer","enum":[0,2,4]},{"type":"string","enum":["0","2","4","tab"]}]},"sortKeys":{"type":"boolean","description":"Recursively sort object keys alphabetically; array element order is preserved (default false)."}},"required":["json"]},"example":{"json":"{\"b\":1,\"a\":2}","sortKeys":true}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["formatted","valid","warnings"],"properties":{"formatted":{"type":"string","description":"The reformatted JSON text (pretty-printed, minified, or key-sorted per the options)."},"valid":{"type":"boolean","description":"Always true on the success result; malformed JSON instead returns {error, line, column}."},"warnings":{"type":"array","items":{"type":"string"},"description":"Non-fatal data-loss warnings detected in the raw source: duplicate object keys (last value kept) and integers beyond 2^53 (rounded on parse). Empty when none."}}}}},"example":{"tool":"format_json","result":{"formatted":"{\n  \"a\": 2,\n  \"b\": 1\n}","valid":true,"warnings":[]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/convert_yaml":{"post":{"operationId":"convert_yaml","summary":"Use this when converting between YAML and JSON in either direction, or normalizing one format in place (set from equal to to).","description":"Use this when converting between YAML and JSON in either direction, or normalizing one format in place (set from equal to to). Deterministic: same input, same output. Parses a single YAML document and caps alias expansion at 100 to defuse billion-laughs bombs; multi-document input, tabs-as-indentation, and other parse errors return a typed error with the line number. Example: { data: \"name: web\\nport: 8080\", from: \"yaml\", to: \"json\" } -> { result: \"{\\n  \\\"name\\\": \\\"web\\\",\\n  \\\"port\\\": 8080\\n}\" }.","x-read-only":true,"tags":["data"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"data":{"type":"string","description":"The document to convert, as a string."},"from":{"type":"string","enum":["yaml","json"],"description":"Input format of data."},"to":{"type":"string","enum":["yaml","json"],"description":"Output format; if equal to \"from\", the document is normalized (parsed and re-emitted)."}},"required":["data","from","to"]},"example":{"data":"name: web\nport: 8080","from":"yaml","to":"json"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["result"],"properties":{"result":{"type":"string","description":"The converted (or, when from equals to, normalized) document as text in the target format."}}}}},"example":{"tool":"convert_yaml","result":{"result":"{\n  \"name\": \"web\",\n  \"port\": 8080\n}"}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/convert_color":{"post":{"operationId":"convert_color","summary":"Use this when you need one color's exact values across every common format.","description":"Use this when you need one color's exact values across every common format. Accepts a hex (#abc or #aabbcc), rgb(r, g, b) with r/g/b 0-255, or hsl(h, s%, l%) color and returns hex, rgb {r,g,b}, hsl {h,s,l}, hsv {h,s,v}, cmyk {c,m,y,k}, and oklch {l,c,h} (CSS Color 4: sRGB -> linear -> OKLab -> OKLCH, rounded to 4 decimals). Note oklch.l is 0-1 (not 0-100) and hue is 0 for grays. Deterministic: same input, same output. Example: \"#ff0000\" -> oklch {l: 0.628, c: 0.2577, h: 29.2339}. Alpha channels are not supported.","x-read-only":true,"tags":["color"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"color":{"type":"string","description":"A color as hex (#abc or #aabbcc), rgb(r, g, b) with r/g/b 0-255, or hsl(h, s%, l%). Alpha is not supported."}},"required":["color"]},"example":{"color":"#ff0000"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["hex","rgb","hsl","hsv","cmyk","oklch"],"properties":{"hex":{"type":"string","description":"6-digit lowercase hex, e.g. '#ff0000'."},"rgb":{"type":"object","additionalProperties":false,"required":["r","g","b"],"properties":{"r":{"type":"integer","description":"Red 0-255."},"g":{"type":"integer","description":"Green 0-255."},"b":{"type":"integer","description":"Blue 0-255."}}},"hsl":{"type":"object","additionalProperties":false,"required":["h","s","l"],"properties":{"h":{"type":"integer","description":"Hue 0-360 degrees (0 for grays)."},"s":{"type":"integer","description":"Saturation 0-100 percent."},"l":{"type":"integer","description":"Lightness 0-100 percent."}}},"hsv":{"type":"object","additionalProperties":false,"required":["h","s","v"],"properties":{"h":{"type":"integer","description":"Hue 0-360 degrees."},"s":{"type":"integer","description":"Saturation 0-100 percent."},"v":{"type":"integer","description":"Value/brightness 0-100 percent."}}},"cmyk":{"type":"object","additionalProperties":false,"required":["c","m","y","k"],"properties":{"c":{"type":"integer","description":"Cyan 0-100 percent."},"m":{"type":"integer","description":"Magenta 0-100 percent."},"y":{"type":"integer","description":"Yellow 0-100 percent."},"k":{"type":"integer","description":"Key/black 0-100 percent."}}},"oklch":{"type":"object","additionalProperties":false,"required":["l","c","h"],"properties":{"l":{"type":"number","description":"OKLCH lightness 0-1 (not 0-100), rounded to 4 decimals."},"c":{"type":"number","description":"OKLCH chroma, rounded to 4 decimals."},"h":{"type":"number","description":"OKLCH hue 0-360 degrees (0 for grays), rounded to 4 decimals."}}}}}}},"example":{"tool":"convert_color","result":{"hex":"#ff0000","rgb":{"r":255,"g":0,"b":0},"hsl":{"h":0,"s":100,"l":50},"hsv":{"h":0,"s":100,"v":100},"cmyk":{"c":0,"m":100,"y":100,"k":0},"oklch":{"l":0.628,"c":0.2577,"h":29.2339}}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/slugify":{"post":{"operationId":"slugify","summary":"Use this when you need a URL- or filename-safe slug from arbitrary text.","description":"Use this when you need a URL- or filename-safe slug from arbitrary text. Deterministic: same input, same output. Applies Unicode NFKD normalization, strips combining accents, and transliterates non-decomposing letters (ß->ss, æ->ae, œ->oe, ø->o, đ->d, ł->l, þ->th, ð->d, plus uppercase variants), then collapses every run of non-alphanumeric characters to a single separator and trims separators; e.g. \"Héllo Wörld!\" -> \"hello-world\". Emoji, CJK, and any other characters with no ASCII form are dropped. Prefer this over transliterating Unicode yourself, which models routinely get wrong. Returns { error } when no URL-safe characters remain.","x-read-only":true,"tags":["text"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"text":{"type":"string","description":"Text to slugify (max 10000 characters)."},"separator":{"type":"string","maxLength":1,"description":"Word separator: empty string (join words) or a single character from \"-\", \"_\", \".\". Default \"-\"."},"lowercase":{"type":"boolean","description":"Lowercase the result (default true)."},"maxLength":{"type":"integer","minimum":0,"description":"Maximum slug length; 0 means no limit (default). When set and exceeded, the slug is cut at a separator boundary when possible, otherwise hard-cut."}},"required":["text"]},"example":{"text":"Héllo Wörld!"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["slug","changed"],"properties":{"slug":{"type":"string","description":"The URL/filename-safe slug."},"changed":{"type":"boolean","description":"True when the slug differs from the input text."}}}}},"example":{"tool":"slugify","result":{"slug":"hello-world","changed":true}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/convert_number_base":{"post":{"operationId":"convert_number_base","summary":"Use this when you need to convert an integer between numeral bases 2-36 (binary, octal, decimal, hex, or any radix up to 36), including arbitrarily large values and an optional leading minus sign.","description":"Use this when you need to convert an integer between numeral bases 2-36 (binary, octal, decimal, hex, or any radix up to 36), including arbitrarily large values and an optional leading minus sign. Uses BigInt so there is no precision loss, and output digits are lowercase. Deterministic: same input, same output. Example: { value: \"ff\", from: 16 } -> decimal \"255\", results.base2 \"11111111\". Prefer this over doing base/digit conversion in your head, which is an error-prone bit-fiddling task. If `to` is omitted it converts to bases 2, 8, 10, and 16 (excluding the source base).","x-read-only":true,"tags":["developer"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"value":{"type":"string","description":"The number to convert, as a string so huge values are not truncated. Digits are case-insensitive (a-z = 10-35) and an optional leading \"-\" is allowed, e.g. \"ff\", \"-1010\", \"HELLOWORLD123\"."},"from":{"type":"integer","minimum":2,"maximum":36,"description":"The base the input value is written in (2-36)."},"to":{"description":"Target base(s): a single integer 2-36, or an array of them. Optional; defaults to [2, 8, 10, 16] with the source base excluded.","oneOf":[{"type":"integer","minimum":2,"maximum":36},{"type":"array","items":{"type":"integer","minimum":2,"maximum":36}}]}},"required":["value","from"]},"example":{"value":"ff","from":16}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["value","from","decimal","results"],"properties":{"value":{"type":"string","description":"Echo of the input value."},"from":{"type":"integer","minimum":2,"maximum":36,"description":"Echo of the source base."},"decimal":{"type":"string","description":"The value in base 10 (may carry a leading '-')."},"results":{"type":"object","description":"Converted representations keyed by 'base<N>' (e.g. base2, base8, base16) with lowercase digit strings. Which keys appear depends on the `to` argument (default bases 2, 8, 10, 16 excluding the source base).","additionalProperties":{"type":"string"}}}}}},"example":{"tool":"convert_number_base","result":{"value":"ff","from":16,"decimal":"255","results":{"base2":"11111111","base8":"377","base10":"255"}}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/query_json":{"post":{"operationId":"query_json","summary":"Use this when you need to pull specific values out of a JSON document by JSONPath and getting the path exactly right on deeply nested or large structures matters.","description":"Use this when you need to pull specific values out of a JSON document by JSONPath and getting the path exactly right on deeply nested or large structures matters. Evaluates a JSONPath subset — $ (root), .name or ['name'] (child), [n] (index, negative allowed), [*] (wildcard), .. (recursive descent), and [start:end] (slice) — and returns every matching value in document order. Prefer this over hand-walking nested JSON, where it is easy to miscount array indices or miss a deep match. Filter (?()) and script (()) expressions are not supported and are rejected with a message naming the supported subset. Deterministic: same input, same output. Example: path \"$.store.book[-1].title\" over {\"store\":{\"book\":[{\"title\":\"A\"},{\"title\":\"B\"}]}} -> matches [\"B\"], count 1.","x-read-only":true,"tags":["data"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"json":{"type":"string","description":"The JSON document to query, as a string. Must parse as valid JSON (object, array, or primitive)."},"path":{"type":"string","maxLength":500,"description":"JSONPath (subset). Supports $ root, .name / ['name'] child, [n] index (negative allowed), [*] wildcard, .. recursive descent, and [start:end] slice (optional :step). Filter (?()) and script (()) expressions are not supported. Example: $.items[*].id"}},"required":["json","path"]},"example":{"json":"{\"store\":{\"book\":[{\"title\":\"A\"},{\"title\":\"B\"}]}}","path":"$.store.book[-1].title"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","additionalProperties":false,"required":["path","matches","count","truncated"],"properties":{"path":{"type":"string","description":"Echo of the JSONPath that was evaluated."},"matches":{"type":"array","description":"Every matching value in document order; items may be any JSON type (object, array, string, number, boolean, or null).","items":{}},"count":{"type":"integer","description":"Number of matches (equals matches.length)."},"truncated":{"type":"boolean","description":"True if the internal match cap was reached and further matches were dropped."}}}}},"example":{"tool":"query_json","result":{"path":"$.store.book[-1].title","matches":["B"],"count":1,"truncated":false}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}},"/v1/generate_id":{"post":{"operationId":"generate_id","summary":"Use this when you need a modern unique identifier (UUID v7, UUID v5, ULID, or nanoid) or want to read the timestamp embedded in an existing ULID or UUIDv7.","description":"Use this when you need a modern unique identifier (UUID v7, UUID v5, ULID, or nanoid) or want to read the timestamp embedded in an existing ULID or UUIDv7. uuid_v5 (SHA-1 of a namespace UUID + a name, RFC 4122) and decode are deterministic (same input, same output) — e.g. generate_id(type \"uuid_v5\", namespace \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", name \"www.example.com\") -> {ids:[\"2ed6657d-e927-568b-95e1-2665a8aea6a2\"]}. uuid_v7, ulid, and nanoid are cryptographically random via Web Crypto (NOT deterministic). Prefer this over assembling ids by hand: it sets the correct RFC version/variant bits, uses Crockford base32 for ULIDs, and draws nanoid characters with unbiased rejection sampling. When decode is present, type is ignored and the result is the embedded {unix_ms, timestamp_iso}.","x-read-only":true,"tags":["random"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"type":{"type":"string","enum":["uuid_v7","uuid_v5","ulid","nanoid"],"description":"Which id to generate. Required unless decode is given."},"count":{"type":"integer","minimum":1,"maximum":100,"description":"How many ids to generate (default 1). For uuid_v5 all copies are identical (deterministic)."},"namespace":{"type":"string","description":"uuid_v5 only: the namespace UUID, e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8 (DNS)."},"name":{"type":"string","description":"uuid_v5 only: the name hashed within the namespace."},"alphabet":{"type":"string","description":"nanoid only: characters to draw from (2-256 chars; default is the 64-char URL-safe set)."},"size":{"type":"integer","minimum":2,"maximum":64,"description":"nanoid only: id length (default 21)."},"decode":{"type":"string","description":"A ULID or UUIDv7 to decode; returns its embedded timestamp. type is ignored when set."}},"required":[]},"example":{"type":"uuid_v5","namespace":"6ba7b810-9dad-11d1-80b4-00c04fd430c8","name":"www.example.com"}}}},"responses":{"200":{"description":"Structured tool result.","content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string"},"result":{"type":"object","description":"Generate mode returns `ids`; decode mode returns `decoded`. Exactly one of the two is present.","properties":{"ids":{"type":"array","items":{"type":"string"},"description":"[generate mode] The generated identifiers, length = count. For uuid_v5 every copy is identical (deterministic)."},"decoded":{"type":"object","additionalProperties":false,"required":["kind","unix_ms","timestamp_iso"],"description":"[decode mode] The timestamp embedded in the supplied ULID or UUIDv7.","properties":{"kind":{"type":"string","enum":["ulid","uuid_v7"],"description":"Which id family was decoded."},"unix_ms":{"type":"integer","description":"Embedded timestamp as Unix epoch milliseconds."},"timestamp_iso":{"type":"string","description":"Embedded timestamp as an ISO-8601 UTC string."}}}}}}},"example":{"tool":"generate_id","result":{"ids":["2ed6657d-e927-568b-95e1-2665a8aea6a2"]}}}}},"400":{"description":"Invalid input or the tool reported an error.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}},"413":{"description":"Request body exceeds the 128 KB limit."},"429":{"description":"Rate limit exceeded; the response carries Retry-After and RateLimit-* headers — honor them and add jitter."}}}}},"components":{"schemas":{"ToolResult":{"type":"object","description":"Structured, deterministic result. On failure an `error` string is present and the HTTP status is 4xx.","properties":{"error":{"type":"string","description":"Present only when the tool could not run; describes what was wrong."}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable, structured error message."},"code":{"type":"string","description":"Stable machine-readable error code."},"tool":{"type":"string","description":"Tool name, when the error is tool-specific."}}}}}}