Expose only documented JSON GET operations, convert their path and query parameters into MCP tool input schemas, and keep the upstream credential scoped to retrieval. Mark the generated tools with read-only annotations, but enforce the boundary with method filtering, host allowlists, response limits and runtime policy. Treat this as a retrieval adapter for agents, not as a shortcut for giving a model a general API client.
Start with the HTTP boundary
A read-only REST connector should begin with the HTTP method, not with an endpoint name that sounds harmless. In HTTP semantics, a safe method is one where the client does not request or expect a state change at the origin server; GET is the normal retrieval method. That makes GET the right input set for generated agent tools, but it is still only a starting point. Some APIs attach analytics, cache warming, webhooks or unusual side effects to retrieval routes, so review the provider's documentation before publishing the route to an agent.
Be strict about the first version. Include JSON-producing GET operations whose behavior is documented and whose authentication scope is read-oriented. Exclude POST, PUT, PATCH and DELETE even when the endpoint is named /search or /query. Exclude GET routes that trigger exports, send emails, start long-running jobs or return pre-signed write URLs. This boundary belongs with read-only versus write tool policy, because the model should not be asked to infer safety from route names.
Map OpenAPI operations to tool contracts
An OpenAPI description gives you the material for deterministic tool generation: paths, operations, parameters, response schemas and security requirements. For a GET operation, use the path template and declared path or query parameters to build the tool input schema. Prefer operationId when it is stable and readable; otherwise generate a namespaced name from the method and path, then review the name because agents choose tools partly from names and descriptions.
paths:
/v1/customers/{customer_id}/invoices:
get:
operationId: listCustomerInvoices
summary: List invoices for one customer
parameters:
- name: customer_id
in: path
required: true
schema:
type: string
- name: status
in: query
required: false
schema:
type: string
enum: [draft, open, paid, void]
- name: limit
in: query
required: false
schema:
type: integer
maximum: 100
responses:
"200":
description: Invoice list
content:
application/json:
schema:
type: objectThe generated tool should preserve those constraints instead of accepting a raw URL. A raw URL parameter lets the model discover neighboring endpoints, add undocumented query strings or accidentally call a route outside the intended host. A shaped schema keeps the model inside the documented operation: customer_id, optional status, optional limit, and nothing else.
Expose the result through MCP
MCP clients discover tools with JSON-RPC tools/list and invoke one with tools/call. The generated REST tool should therefore appear as a normal MCP tool with a clear description, an inputSchema, a small output shape and a read-only annotation. The annotation is useful because trusted clients can use readOnlyHint: true for approval and risk display, but it is not enforcement. Enforcement still lives in the generator and runtime.
{
"name": "billing.list_customer_invoices",
"description": "List invoices for one customer from the Billing API. Reads JSON only; does not create, update, send or void invoices.",
"inputSchema": {
"type": "object",
"additionalProperties": false,
"properties": {
"customer_id": { "type": "string" },
"status": { "type": "string", "enum": ["draft", "open", "paid", "void"] },
"limit": { "type": "integer", "minimum": 1, "maximum": 100 }
},
"required": ["customer_id"]
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": false
}
}A call stays just as narrow. The agent supplies arguments, the connector expands the path template, attaches the upstream credential server-side and returns a compact answer. It should not reveal bearer tokens, let the model set arbitrary headers or let the model choose a different host.
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "billing.list_customer_invoices",
"arguments": {
"customer_id": "cus_123",
"status": "open",
"limit": 25
}
}
}
Clamp the runtime, not just the schema
Schemas catch malformed arguments; they do not make an API safe. The connector runtime should own URL construction, authentication, retries, pagination and output shaping. Build the request from known pieces: fixed base URL, fixed GET method, fixed path template, allowlisted query parameters and a credential that cannot write upstream. If the OpenAPI file says a parameter is optional but the operation becomes dangerous without it, require it in the generated tool or do not generate the tool.
| Control | Why it matters |
|---|---|
| Host and path allowlist | Stops the model from turning a tool into an arbitrary HTTP client. |
| GET-only dispatch | Keeps generated tools aligned with safe retrieval semantics. |
| Read-scoped credential | Limits damage if a route or annotation is wrong. |
| Timeout and byte cap | Prevents slow or huge responses from consuming the agent session. |
| Pagination limit | Keeps responses small enough for reasoning and audit. |
| Response projection | Returns fields the agent needs instead of whole upstream payloads. |
Output shaping is not cosmetic. A billing API may return internal IDs, tax details, URLs, status histories and nested payment metadata. For an agent answering a support question, the useful result may be five fields: invoice ID, status, amount due, due date and hosted invoice URL. Compact results also reduce context pressure, which connects directly to tool and context bloat.
Wire one endpoint into clients
Once the REST adapter is behind an MCP server, the client config should point to that MCP endpoint, not to the upstream REST API. Claude Code supports remote HTTP MCP servers from the CLI, including authorization headers. Keep the agent key in an environment variable so the command history and project files do not contain a reusable secret.
claude mcp add --transport http company-api https://api.example.com/mcp \
--header "Authorization: Bearer $AGENT_MCP_KEY"Cursor can load custom MCP servers from .cursor/mcp.json for a project or ~/.cursor/mcp.json globally. A remote server uses a url entry, with headers when the server requires them.
{
"mcpServers": {
"company-api": {
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer ${env:AGENT_MCP_KEY}"
}
}
}
}VS Code can use workspace MCP configuration in .vscode/mcp.json, where the VS Code format defines servers under a top-level servers object. For a remote HTTP server, keep the type and URL explicit and let the server-side policy decide which generated REST tools this key can call.
{
"servers": {
"company-api": {
"type": "http",
"url": "https://api.example.com/mcp"
}
}
}
How SPYN3 handles read-only REST
SPYN3's "Any REST API" connector gives the agent one tool, get, with a path and optional query. It has no method argument, so it cannot express a write. Paths cannot leave the configured base URL, redirects are refused, and you can restrict the agent to path prefixes such as /v1/items. The optional bearer token is stored encrypted and never shown to the agent. To let an agent create tickets, use the vendor's own MCP server; this connector will not.
Test the awkward cases
Before rollout, test the routes that look read-only but behave like actions. A common failure is an export endpoint that uses GET and returns a job ID while starting a background export. Another is a search endpoint that accepts an undocumented include_deleted=true flag. A third is pagination without a hard maximum, where the agent asks for every record because it is trying to be complete. These are connector problems, not model problems; the tool should make unsafe requests impossible.
Also test error handling. A 401 should tell the agent the source needs authentication, not leak the upstream token. A 404 should name the resource type that was not found without encouraging the model to try neighboring IDs. A 429 should surface rate limiting and retry-after information when the API provides it, while the connector still applies its own bounded retry policy. Log source, tool, decision, status, latency and response size so tool-call audit logs can explain what happened later. The finished connector should feel boring: fixed routes in, bounded JSON out, no surprise methods hidden behind friendly prose.
Built for the next step
Start with retrieval
Generate narrow GET tools before considering write-capable API actions.
REST generated toolsSPYN3 is a consolidated MCP layer: one hosted endpoint in front of every system you connect — MCP servers, Postgres, REST APIs, Google Drive — with tools foldered by source and read/write permissions per key. Switching agents means moving one URL. Free plan available; current capabilities are documented on the product facts page.
Sources
- RFC 9110: HTTP Semantics — safe methods, GET semantics and automated retrieval constraints. Verified 14 Oct 2026
- OpenAPI Specification 3.0.4 — describing paths, operations, parameters and responses. Verified 14 Oct 2026
- MCP specification: tools — tool discovery, invocation and security considerations. Verified 14 Oct 2026
- MCP project: tool annotations — risk hints and their enforcement limits. Verified 14 Oct 2026
- MCP specification: transports — stdio and Streamable HTTP transport boundaries. Verified 14 Oct 2026
- Anthropic docs: Claude Code MCP setup — Claude Code MCP commands, scopes, transports and JSON configuration. Verified 14 Oct 2026
- VS Code documentation: add and manage MCP servers — VS Code MCP configuration, trust and logging behavior. Verified 14 Oct 2026
- Cursor documentation: MCP integrations — Cursor MCP configuration locations, remote URL entries, headers and approval behavior. Verified 14 Oct 2026
