Skip to content

MCP and OAuth

How an agent reaches a network. For the steps in a given tool, see connect an agent; this page is for people who build agents.

POST https://app.inseam.io/mcp

MCP over Streamable HTTP, stateless, JSON-RPC 2.0, one message or a batch per request. The bearer token is an agent’s token from the OAuth flow below, or an API token. Without a valid token the answer is 401 with WWW-Authenticate: Bearer resource_metadata="https://app.inseam.io/.well-known/oauth-protected-resource", which is how an MCP host discovers that it must sign in. GET /mcp and DELETE /mcp are 405: there is no session to open or close.

The server speaks protocol version 2026-07-28 and still answers the 2025-11-25 initialize. initialize returns short instructions: what inseam is, that every result carries an address, that read gets the text behind a hit, and where the agent’s own folder is.

The usual flow for MCP hosts. Discovery is standard:

document
/.well-known/oauth-protected-resource RFC 9728: the resource, its authorization server, the scopes
/.well-known/oauth-authorization-server RFC 8414: the endpoints, code_challenge_methods_supported: ["S256"], token_endpoint_auth_methods_supported: ["none"]
  1. Register, or bring a client id. POST /oauth/register (RFC 7591) with {"client_name": "…", "redirect_uris": […], "token_endpoint_auth_method": "none"} returns {"client_id": "…"}. Or use an https URL as the client id that serves a Client ID Metadata Document: JSON with client_id equal to the URL, client_name and redirect_uris. Public clients only.
  2. Send the person to GET /oauth/authorize?response_type=code&client_id=…&redirect_uri=…&code_challenge=…&code_challenge_method=S256&state=…&scope=search%20read. PKCE is required. They sign in, pick a network, the scopes, the hosts and a duration, and approve.
  3. The browser returns to redirect_uri?code=…&state=…. Exchange it: POST /oauth/token with grant_type=authorization_code, code, client_id, redirect_uri, code_verifier.
  4. The answer is {"access_token": "ias_…", "token_type": "Bearer", "expires_in": …, "refresh_token": "rt_…", "scope": "search read"}.

Redirect URIs must match exactly; loopback URIs (http://127.0.0.1:…, http://localhost:…) may change their port. A refresh token rotates the access token with grant_type=refresh_token and lives 90 days. POST /oauth/revoke with token revokes either kind.

For a script on a server or a scheduled job.

  1. POST /oauth/device with client_id and scope. The answer is {"device_code", "user_code", "verification_uri", "verification_uri_complete", "expires_in", "interval"}.
  2. Show the person user_code and verification_uri (https://app.inseam.io/activate). They type the code and see the same approval page.
  3. Poll POST /oauth/token with grant_type=urn:ietf:params:oauth:grant-type:device_code, device_code and client_id, every interval seconds. Until approved the answer is authorization_pending; slow_down means wait 5 seconds; access_denied and expired_token end it. Codes expire after 15 minutes.

OAuth endpoints answer in RFC 6749 shape: 400 {"error": "invalid_grant", "error_description": "…"}.

scope lets the agent default at approval
search search the network, read records, list prefixes, see hosts and status on
read read the text and the bytes behind an address on
write write and delete on the hosts the person picked off

Whatever the scopes, every agent may read, write and delete in its own folder on the pad: inseam://{network}/pad/agents/{client id}/. The hosts tool returns it as padFolder.

tool input needs returns
search {q, hosts?: [id], topK?: 1-100} search {hits: [{address, host, score, title, snippet, meta}], hosts: [{id, name, searched, reason?, coverage, items}]}
get {address, follow?: true} search {record}: the catalog record; a forward carries deleted: true and movedTo
read {address, maxBytes?: ≤ 262144} read {address, mime, source: "index" | "host", truncated, text}, or {…, base64} for non-text
list {prefix, after?, limit?: ≤ 1000} search {entries: [{address, id}], more, next}
hosts {} {hosts: [{id, name, kind, state, items, coverage, writable, youMayWrite}], padFolder}
write {address, mime?, ifMatch?, content?, contentBase64?} write {entry: {address, content, etag, size, depth, created}}; the object is searchable when the call returns
delete {address} write {address}; the old address keeps a forward

read serves text from the index when it has it (source: "index") and from the host otherwise, so a read of an indexed document works while its device is off. A tool that cannot do what was asked returns isError: true with the reason (forbidden: not allowed: write …), not a JSON-RPC error. Addresses are full location addresses or the short form {host}/{path}; see addresses.

Connecting new sources from an agent (connect_host and its companions) is not yet: the tools exist and answer with the current way to add a host.

path
/llms.txt plain-text pointers to the endpoint and these docs
/.well-known/mcp/server-card.json the server card: name, endpoint, authentication