Skip to content

Agent integration

$ agent-integration · 8 min read · updated 2026-06-10

This page is the contract for agents (and agent developers) integrating with a capsol registry without a human walking them through it. Everything here is discoverable from the registry root.

GET /.well-known/capsol-agent.json (unauthenticated) describes the whole integration surface:

FieldMeaning
name, versionAlways "capsol" plus the registry version
mcp.urlURL template — https://host/mcp/:capsuleId
mcp.auth.type"oauth_required" — there is no anonymous MCP access
mcp.auth.oauth.protected_resourceRFC 9728 protected-resource metadata URL
mcp.auth.oauth.protected_resource_templatePer-capsule variant with :capsuleId
mcp.auth.oauth.authorization_serverRFC 8414 authorization-server metadata URL
mcp.auth.oauth.registration_endpointDynamic Client Registration (POST)
mcp.auth.oauth.dcr / cimdWhether DCR / client-id-metadata-documents are enabled
mcp.auth.oauth.redirect_policyAllowed redirect hosts / native schemes (empty lists = any safe redirect)
mcp.auth.oauth.pkceAlways true; only S256 is accepted
mcp.auth.token_in_urlAlways false — tokens in URLs are rejected with 410
enrollment.endpointPOST /v1/agent-enrollments
enrollment.approvalCurrent approval mode: human, supervisor, or policy
enrollment.idempotency_keyWhat makes a repeated enrollment idempotent
enrollment.flowStep-by-step instructions (also reproduced below)
grants.*Where grant requests are listed and decided
cli.*Copy-paste CLI equivalents
docs.llms / docs.fullThe llms.txt contract
scopesAll grantable scopes

Two auth paths exist. OAuth (DCR → authorize → PKCE token) suits clients with a browser-capable user. Enrollment suits headless autonomous agents and is specified next.

An enrollment is a request: “client X wants role R on capsule C. Create one with no credentials at all:

Terminal window
curl -X POST https://host/v1/agent-enrollments \
-H "Content-Type: application/json" \
-d '{
"client_id": "my-agent-stable-id",
"capsule_id": "<capsule-uuid>",
"agent_label": "Build agent",
"requested_role": "writer",
"human_email": "owner@example.com"
}'
create
┌────────┐ operator/supervisor/policy approves ┌──────────┐
│pending │ ───────────────────────────────────────▶│ approved │ (terminal)
└────────┘ └──────────┘
│ reject token is now an
▼ MCP credential
┌────────┐
│rejected│ (terminal)
└────────┘
│ 30 minutes elapse while pending
┌────────┐
│expired │ (terminal — create a new enrollment)
└────────┘
  • The create response returns enrollment_token once. Save it. It is the polling credential now and the MCP credential after approval.
  • repeated: true (HTTP 200 instead of 201) means an identical pending enrollment already exists — same client_id + capsule_id + requested role/content scope. The original token stays valid; the repeated response does not re-issue it.
  • Pending enrollments expire after 30 minutes. Terminal states never transition.
  • Under approval: "policy" the create response can come back already approved with connection_id and mcp_url inline.
Terminal window
curl https://host/v1/agent-enrollments/<enrollment_id> \
-H "Authorization: Bearer <enrollment_token>"
  • Poll with the token; unauthenticated or wrong-token polls get 401.
  • Sensible cadence: every 5–15 s for the first minutes, then back off. The lookup is rate-limited to 10/min per IP — a 429 with Retry-After tells you to slow down.
  • status: "pending" responses never include connection details. After approval the same poll returns capsule_id, mcp_url, and connection_id.
  • Once approved, send the enrollment token as the MCP bearer: Authorization: Bearer <enrollment_token> against mcp_url.

Every failed REST/MCP-transport request carries { error, error_code, recovery }. The agent-relevant ones:

error_codeYou should
invalid_tokenRe-run discovery and OAuth/DCR, or create a fresh enrollment. Do not retry the same credential.
token_expiredPOST /oauth/token with grant_type=refresh_token and your saved refresh token; fall back to re-authorization.
token_revokedStop. Access was withdrawn. File a new access request and wait for approval.
grant_revokedSame as token_revoked, but explicitly an operator decision on the grant.
connection_pausedBack off and retry later; credentials stay valid. Tell your human.
unknown_capsuleThe capsule id in your URL is wrong or gone. Re-check with your operator.
rate_limitedWait the Retry-After seconds, then resume.
payload_too_largeThe body has limit_bytes and actual_bytes. Split content or use the upload endpoint.
invalid_uriUse scheme://path (example: docs://readme). No .., no absolute paths.

Inside MCP tool results, errors come as status: "error" payloads with code and a hint — notably version_conflict (re-read, retry with the current version), item_exists, missing_if_version, string_not_found, and insufficient_scope.

  • GET /llms.txt — one screen of plain text: URL shape, “credentials never in URLs”, the four tools, and where enrollment lives. Stable, cacheable, safe to inline into a prompt.
  • GET /llms-full.txt — the full agent guide: OAuth discovery steps in order, enrollment instructions, tool semantics, artifact views, and signal audience filters.

Both are unauthenticated and intended to be fetched by agents at connect time. The contract: anything stated there matches the running registry version, and breaking changes to it are versioned with the registry (URL shapes are stable forever — Invariant G3 of the project).

  • capsol_read(output_path=…) and capsol_write(file_path=…) are disabled for hosted MCP connections — content travels in the tool response, not via server-side file paths.
  • Replacing or patching an existing item over a hosted connection requires if_version (read first, then write).
  • Signals are capped at 140 characters and delivered via per-connection unread cursors on your next tool call.