Skip to content

MCP tools

The catalogue exposed over agent access at v0.1.0-preview.10: twenty-one tools and four resources — plus any tools you publish from your own API, which are listed after them and marked your API. The app itself lists them under Settings → AI → Agent access (MCP) → Tools, generated from the same source as the server, so it never drifts.

Asks first means an approval card appears in the app and the agent waits. A denial returns the error denied by user. Five of Tinspec’s own tools ask: send_request, run_chain, save_chain, save_request, and set_environment.

Every task an agent does is a session. It opens one before sending anything — send_request, run_chain and your published tools are refused without one — and everything it does is filed under it: in the app’s Agent view it shows up as one titled session, and the agent can list exactly its own requests without wading through yours.

Asks first: no. Opens a session and returns its sessionId.

ArgumentTypeNotes
titlestringRequired. What the session is for
goalstringWhat the agent is trying to find out
environmentstringDefault environment for every send in the session
provider, sender, orgstringA default identity — applied when a request authenticates with that provider

A call’s own arguments always win over the session’s defaults. Sends take a sessionId; it may be left out while the agent has exactly one session open. “Allow all” on the approval card lasts for the session, and so do the agent budgets. A session nobody used for 30 minutes ends as idle.

Asks first: no. The session’s own requests, in order — runId, method, path, status, who it went out as (as), tests — with totals, paged by limit/offset. A body is one get_run away.

Asks first: no. The agent’s sessions, newest first (allClients: true for everyone’s).

Asks first: no. Closes the session with an optional summary, which you see in the Agent view, and returns a recap: totals and the last runs.

Asks first: no. The project the app currently has open. With none open, every tool answers no project is open in Tinspec.

Asks first: no. Environment names and, per environment, the keys of its variables. Values are never returned.

Asks first: yes. Switches the project’s active environment — the variable set every later send resolves {{vars}} against, the same switch the app’s environment picker makes.

ArgumentTypeNotes
namestringAn environment name from list_environments

Returns the new active environment, its source, and its variable keys. Values do not cross here either. An unknown name fails before the prompt, and the error lists the names that exist.

It is read-shaped but it changes the app for you too, so it prompts every time — it is never covered by the “allow safe methods” setting. It is not flagged as destructive, because switching an environment neither destroys nor re-does anything, and an agent told otherwise would over-warn.

Asks first: no. The API providers in the project — id, name, kind, url, endpointCount, status. The id is what list_endpoints takes.

These two answer “who can I send as?” without ever naming a credential. Provider files hold non-secret config plus secret names; neither reaches the agent.

Asks first: no. The project’s auth providers: name, kind (script, request, integration, oauth2, awsSigV4), the IdP integration when the kind is integration, a human source label, defaultSender, senderCount, and perSenderPassword. This is what a request’s auth.provider refers to.

No config, no secret refs, no script source, no token.

Asks first: no. The senders — the identities a request can go out as — of one provider or of all of them.

ArgumentTypeNotes
providerstringA name from list_auth_providers; omit for every provider
querystringFree-text match on id, label, or tenant
includeOrgsbooleanAlso list each sender’s organization memberships (Clerk). At most 25 senders per call

Each sender comes back as provider, id, label, tenant, default, usable, and varKeys (names only, like environments). usable: false means the engine would refuse to authenticate as that identity — a password-grant identity with no password stored, say — and issue says why in a sentence. Pass a sender’s id as send_request’s sender. With includeOrgs, each also carries orgs: [{ id, slug, name, role }] — or orgsError from a provider that has no directory to ask — and an org’s id is what send_request’s org takes.

Sending the same endpoint as several senders and comparing the statuses is the authorization matrix; the bundled skill walks an agent through it.

Asks first: no. Search the spec-derived endpoints.

ArgumentTypeNotes
providerstringProvider id from list_providers
querystringFree-text match on path, summary, or operationId
tagstring
methodstringe.g. GET
limitinteger1–500, default 50

Returns the (limited) page plus total. Endpoint ids handed to agents are <providerId>:<endpointId>; a bare endpoint id is accepted when it is unique across providers, and is otherwise rejected as ambiguous rather than guessed.

Settings → Endpoints → Show internal endpoints is a display setting: hiding x-internal operations affects the app’s own views, not this tool. Publishing is the opposite — it refuses them outright (see below).

Asks first: no. One endpoint in full: protocol, resolved URL, parameters, body schema, documented responses, an example body, and which auth provider applies (or "none"). The example body may come from your own successful sends (secret-scrubbed, with {{placeholders}}), from the endpoint’s saved body, or from its schema — exampleSource says which, so the agent knows whether it is looking at something the server accepted or a guess.

Asks first: no. The hand-authored requests saved in the project’s collections.

Asks first: no. One saved request in full.

Asks first: yes. Send one request. Either a spec endpoint with overrides, or an ad-hoc request object.

ArgumentTypeNotes
endpointIdstringA spec-derived endpoint from list_endpoints
requestobjectAn ad-hoc request — { name?, protocol?, method, url, headers?, query?, body?, auth?, prerunChain? }. method and url required
overridesobjectquery, headers, body, pathVars — applied to endpointId
environmentstringDefaults to the active one; an unknown name is an error
senderstringThe sender to authenticate as
orgstringThe organization the token is obtained for, when the sender is in several — see senders
selectstringA jq filter over the JSON body, applied before anything is returned — .data[] | {id, name}
bodyModefull · summary · nonesummary returns the body’s structure (keys, array lengths, the first item’s keys); none no body
maxItemsintegerCut every array to its first N items; each cut is reported in truncations
maxStringLengthintegerClip long strings, marked …[+n chars]
expectobject{ "<jq>": expected value } — pass/fail instead of the body
verbosebooleanAlso return response headers, the sent request, and the token’s identity claims. Off by default
waitUntilstringA jq condition. Re-sends until it holds — safe methods and GraphQL queries only
timeoutSecs, intervalSecsintegerThe poll’s limits: up to 300 s (default 60), every 1–60 s (default 2)

An ad-hoc request declares http, grpc, or graphql — the chainable, single-response protocols. A spec endpoint goes out as whatever protocol it is, which includes a tool from an MCP catalogue provider: that leaves as an MCP call.

sender and org pin the identity on the auth provider the request would have used anyway; with no provider to pin them to, the call errors rather than sending unauthenticated.

Every jq expression is compiled before anything is sent — and before the approval card — so a typo costs nothing. A waitUntil send asks once for the whole poll, and the card says it repeats; every attempt is still its own history row.

The send is the same one the editor’s Send button makes: the collection’s defaults and the provider’s send settings layer in, so the agent’s request runs on the resolved timeout, proxy, and TLS you configured. A spec endpoint belongs to a provider and to no collection, and an agent’s ad-hoc request belongs to neither — so neither inherits whatever collection you happen to have selected in the UI.

Returns:

FieldNotes
status, durationMs, runIdThe response, and the history row it became — pass it to get_run
bodyThe raw body text when no shaping was asked for; the selected / clipped / summarised value when it was. bodyTruncated: true when cut to the response-size limit (256 KB when unlimited)
selectError, truncations, stringsClipped, bodyCharsWhat shaping did: why a select could not run, every array cut, how many strings were clipped, the original size
expect{ passed, total, failed: [{ path, expected, actual }] }
testsCounts plus failures of the request’s assertions: { passed, total, failed } (the full list with verbose)
pollWith waitUntil: { attempts, satisfied, elapsedMs }
asWho it went out as — provider, sender, org, and tokenOrg, the organization the token itself names. Absent for an unauthenticated send
headers, sentRequest, auth, claimsOnly with verbose: response headers and the sent request with credentials masked (Bearer ****), and the token’s identity claims

The sent body is deliberately omitted. With auth injected into the body rather than a header, it would carry the raw token, and masking a body is a harder problem than masking a header.

If the request has pre/post hooks, anything they printed goes to the app’s Agent activity panel, not into the tool result: those lines are your code’s output, and an agent that asked for a status code has no claim on them. The durable copy is in run history.

Unless shaping was asked for, what crosses is the raw body text, truncated — not the response pane’s rendering. Preview, find, filter, and run-to-run diff are the app’s surfaces, and an agent gets the bytes instead.

An agent’s send never streams. SSE auto-streaming belongs to the editor, so a text/event-stream endpoint sent this way is buffered — it returns when the server closes the stream or the timeout fires.

Asks first: no. The chains in the project.

Asks first: yes. Run one.

ArgumentType
namestring
inputsobject — chain input variables

The chain is validated before the approval prompt, so an invalid chain fails with the reason — code: message (node) lines — rather than asking you to approve something that cannot run.

A run that stops partway is not a tool error: it comes back as a result, so the agent can see how far it got.

FieldNotes
okTrue only when the engine said the run passed
statuspassed, failed, or timedOut
errorA one-line reason, when ok is false. failedNode names the node it is attributed to, when it is a node’s
failuresEach { node, kind, message, region, handled }, in order. handled: true means the run carried on past it
stoppedtrue when the run ended where it failed, so the later nodes never ran
nodesPer node: id, kind, status (done/error/skipped), httpStatus, durationMs, plus region when it ran in teardown and iteration when it is a loop pass
outputsThe chain’s variables

Read ok, never the absence of an error. A node with onError: continue, a failure an error edge caught, a failed assert, and a broken teardown all come back resolved and failed — an agent that treats “the call returned” as success reports every one of them green.

Asks first: no — it is read-only and writes nothing.

ArgumentTypeNotes
endpointIdstringThe target to set up prerequisites for
reuseprefer | neverprefer (default) references matching saved requests; never generates every step from the spec

Returns a ready-to-save chain plus the steps, anything unresolved, notes, and validation issues. Pass the returned chain to save_chain, edited as needed.

Asks first: yes.

ArgumentTypeNotes
chainobjectThe chain to write
overwritebooleanDefault false; required to replace an existing name

Validated strictly before the approval prompt — against the workspace’s collections and the active environment’s keys, with extraction paths checked against the spec endpoint each node maps back to. A failure lists code: message (node) lines and writes nothing.

Asks first: no.

ArgumentTypeNotes
limitinteger1–500, default 50
methodstring
statusinteger
sincestringISO-8601 timestamp

Asks first: no. One run in full. The request shown is the pre-resolution snapshot.

For a send the agent made in the current app session, the full body is still in memory and bodySource is "full": select, bodyMode, maxItems, maxStringLength and expect work exactly as on send_request, so a body can be re-read another way without re-sending. Anything older has only what history stores — the first 2,000 characters (bodySource: "historyPrefix"), paged with body_offset / body_limit. Full bodies are never written to disk.

The two answer different questions, so they are two tools: one compares two fetches of the spec document, the other compares a live response against what the spec declared.

Asks first: no. Returns three things.

  • changes — endpoints added, removed, or modified since you last reviewed the spec, the same data as the Spec Changes view.
  • breaking — the engine’s classified diff joined to this workspace. Each row carries the operation, its changes (each with a stable machine-readable rule — requiredInputAdded, outputBecameNullable, typeNarrowed, … — and a breaking flag), and impact: the saved requests, chain nodes, and endpoint overlay that call it. Branch on rule, never on detail, which is prose written for a human.
  • classified — how many providers actually produced a verdict.

That last field is there because an empty breaking list is not proof of safety: a source polled for the first time this session seeds a baseline instead of reporting, and a .proto-backed gRPC provider produces no verdict at all. classified is how an agent tells “nothing broke” from “nothing was checked”.

endpointId is null for a removed operation — there is nothing left to open.

Asks first: no. The recorded contract drift the app’s Contract Drift view lists — responses that did not match what the bound spec declared, collected as sends happen, newest first and excluding findings you dismissed.

ArgumentTypeNotes
providerstringProvider id or name
kindstringundeclaredStatus, missingRequired, undocumentedField, typeMismatch
limitinteger1–500, default 50

A finding is { id, at, provider, providerName, method, path, status, kind, fieldPath, expected, actual } — compact shape facts. The response body is never stored and never returned. A project with no folder has no local database, so this is empty for one.

Asks first: no. Turns a curl command into a request object. Nothing is saved.

Asks first: yes.

ArgumentType
requestobject
folderstring

Writes the request into the project as YAML.

The twenty-one above drive Tinspec. The other direction is new in v0.1.0-preview.10: your own API, handed to your agent as tools — createOrder, listInvoices — so it never learns a URL, a header, or a token.

Publishing is off by default and turned on per API provider, in that provider’s settings under Agent → Publish to your agent. An agent gaining the ability to call your API should be an act, not a setting that drifts on. The resulting catalogue is the Agent tools view (command palette: Show Published Agent Tools), which lists each tool, the endpoint behind it, and the identity it runs as.

Scope is explicit and small. Publish one tag, only the read-only verbs (GET/HEAD/ OPTIONS), or only what one auth rule’s sender can reach. A prefix (billing_) is there for when two providers both want to publish a createOrder.

Naming. An operationId that is already an identifier is used verbatim, because createOrder reads as something an agent can reason about where post_orders reads as plumbing. Anything else is slugified from the verb and the path template (get_pets_petId), which is stable across polls. Names are capped at 64 characters.

Arguments come from the endpoint’s parameters and request body. Cookie parameters are not published — the engine’s cookie jar already carries one. The body rides as body (or requestBody, if a parameter is already called body), and its default is a body that actually produced a 2xx against this endpoint, secret-scrubbed, rather than a draft synthesized from types. That lookup is bounded to 120 endpoints per rebuild; the rest publish with their schema alone.

Never published, and the view says so rather than dropping them quietly: deprecated endpoints, x-internal endpoints, a name that collides with one of Tinspec’s own tools, a name two endpoints both resolved to, and a schema that is not an object. A scope decision — “you asked for read-only and this is a POST” — is the filter working and is not listed as a refusal.

Each tool carries a resolved provider and sender, and the agent is never told which. The resolution is held in the app alone; the process that serves the catalogue holds only names, descriptions, and schemas. So a published tool has no credential field of any kind, the approval card names the identity (as tenant-admin), and the agent sees the response.

A published call is an ordinary send. Same engine, same auth injection, same pre-run chain, same settings layers, same gateway rewrite, same cookie jar, same run history, same drift check — and the same approval card, per-client serialization, and Agent activity row. A published write always prompts; a published read follows the “allow GET/HEAD/OPTIONS without asking” setting, exactly as an agent-driven GET does.

The catalogue is recomputed when you change what a provider publishes, and the Agent tools view has a Republish button for everything else. Tinspec does not emit a tools/list_changed notification, so the list result tells clients not to cache it and a change shows up on the agent’s next tools/list.

URIContents
tinspec://spec/<providerId>The provider’s OpenAPI document as YAML
tinspec://collection/<file>A collection file
tinspec://response/lastThe most recent response
tinspec://skillThe bundled agent skill — the server’s instructions point here first

The spec resource is offered only for OpenAPI-backed providers. A gRPC, GraphQL, or MCP provider has a live projection and no document, and an agent reading a resource that says “this API has no endpoints” is worse off than one that was never offered it.

  • Environment values. Keys only — including from set_environment after it switches.
  • Auth tokens. Headers in the returned sentRequest are masked as Bearer ****, and the sent body is omitted entirely.
  • Secret names. The identity tools return neither a secret ref nor a provider’s config.
  • The keychain. Not reachable through any tool.
  • Script output. Hook console lines go to the app’s Agent activity panel, not the agent.

Response bodies do reach the agent — that is the point of the surface — truncated to the response-size limit, with the agent told when truncation happened.

Bridged sends land in run history with source: "mcp" and a source_detail naming the client and tool; chain runs and published-tool calls likewise. The Agent activity panel additionally shows every tool call and resource read with timing, outcome, and the approval decision, session-only — SQLite history is the durable record.

No stdio transport, no remote or hosted MCP, no tools/list_changed notification, no sampling or elicitation, no tools for WebSocket, streaming gRPC, or LangGraph runs, and no cloud tools. Switching the active environment is the only app state an agent can change: it cannot edit an environment’s values, an auth provider, or a sender.