curlhub Now Speaks MCP: Correctly Quoted curl for AI Agents curlhub.sh is now a Model Context Protocol server. If you use Claude Desktop, Cursor, or anything else that speaks MCP, your assistant can call curlhub as a tool instead of guessing at syntax and handing you something that almost works. Point your client at it One line of config. No key, no signup: { "mcpServers": { "curlhub": { "url": "https://curlhub.sh/mcp" } } } The endpoint is https://curlhub.sh/mcp — JSON-RPC 2.0 over HTTP POST, sharing the same keyless quota as the rest of the site: 60 requests a minute per IP. The tool: getting curl quoting right There is one tool, validate_and_format_curl(url, method?, headers?, data?), and it exists because of a specific, boring failure that costs people real time. Language models write curl commands constantly, and they get the quoting wrong. Not often — but when they do, it fails silently. Consider a header value containing an apostrophe: curl -H 'X-Note: it's fine' https://api.example.com That apostrophe closes the quoted string. The shell now sees X-Note:, its, and fine as three separate words, and the command either errors confusingly or — worse — runs and does something you did not ask for. Nothing about the text on your screen looks wrong. curlhub builds the command by construction instead. Every interpolated value is wrapped in POSIX single quotes, with embedded single quotes rendered as '\'' — close the quoted run, emit an escaped literal quote, reopen. Inside single quotes the shell treats every other byte literally, so $, backticks, backslashes, semicolons, newlines and globs are all inert. There is exactly one metacharacter to handle, and it is handled. That is the whole trick, and it is why the result is correct rather than usually-correct. The same request, formatted properly: curl -H 'X-Note: it'\''s fine' 'https://api.example.com' What it refuses Some things should not be escaped into looking fine. These return a 400 with a reason instead of a command: Non-http(s) schemes. curl also speaks file://, gopher:// and dict://. A generated command reaching for a local file is a footgun, so we will not write one. Control characters in a URL or header name. Quoting makes them shell-inert but not terminal-inert — an escape sequence can make a pasted line render differently from how it executes. Header names that are not RFC 9110 tokens, and non-UTF-8 input. Bodies use --data-binary, not -d A detail worth stating because it silently corrupts JSON: -d strips newlines and carriage returns, and treats a leading @ as a filename. Generated commands use --data-binary, so your body survives intact. You do not need an agent The same thing works over plain HTTP, and returns text to curl and JSON to an API client at the same URL: curl -s https://curlhub.sh/format-curl \ -H 'Accept: application/json' \ -d '{"method":"POST","url":"https://api.example.com","data":"{}"}' Nothing is fetched and nothing is stored — it is a pure transform, and no outbound connection is ever made. Because of that, private and link-local targets are allowed: building a command for 127.0.0.1 or 169.254.169.254 is a normal developer task, not a request we need to police. In-browser agents too Pages here also register the same tool through document.modelContext, the emerging W3C browser API — native in Edge 147, and in Chrome 149 behind its origin trial. That call is same-origin, so it uses your existing session and quota and no key ever enters the model's context. Discovery The server advertises itself the standard ways: a Link header with rel="mcp" on every response, and a server card at /.well-known/mcp/server-card.json. Machine-readable documentation for agents lives at /llms.txt.