Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Connections

A connection tells the runtime how to call an external API and tells the model which calls exist. Define local connections in amodal/connections/<name>/ or the top-level connections/<name>/. The loader reads amodal/connections/ when it contains entries, otherwise connections/. amodal connections add <name> follows the same rule; see Use the CLI.

Where changes are saved

Entry pointDestination
Amodal with a GitHub sourceA commit on the selected branch. A branch that does not exist is created from the production branch.
Amodal with an Amodal sourceA source snapshot stored by Amodal.
CLI connections add or removeFiles in the working tree at --path. Commit and push them yourself.

Source holds only connection configuration. Secret values, OAuth client secrets, tokens, and account bindings stay in credential storage. Deploy the saved source to make a connection available to a hosted runtime.

Directory Structure

amodal/connections/my-api/
├── spec.json       ← protocol, base URL, auth, OpenAPI source
├── policy.json     ← endpoint permissions, confirmations, field restrictions
├── oauth.json      ← (custom OAuth) application settings, never the client secret
├── openapi.json    ← API contract when openapi.source selects this file
├── surface.md      ← endpoint documentation for the request tool
├── entities.md     ← (optional) entity definitions
└── rules.md        ← (optional) business rules

A REST connection is served either by native OpenAPI operation tools or by the generic request tool. surface.md documents endpoints for request; native connections derive their tools from the OpenAPI document and ignore it. policy.json is required for a local REST connection, and a REST connection without one is skipped with a warning. The minimal policy is {"endpoints": {}}. MCP connections need no policy.

Credentials

Credential values live outside the repository. auth.token, auth.privateKey, and any headers or env entry named like a credential (Authorization, Proxy-Authorization, X-Api-Key, Cookie, or a name ending in token, key, secret, or password) must hold env:NAME, scope:KEY, or a {{NAME}} template. A literal value fails validation with CONNECTION_SECRET_IN_SOURCE. Other headers and environment entries accept literals.

OAuth 2.0

Declare auth: {"type": "oauth2"} in a REST or HTTP MCP connection to authorize an account and refresh its tokens automatically. A custom application is described in oauth.json, and a managed provider is selected with auth.provider. An MCP server that publishes OAuth metadata needs neither. See OAuth connections.

spec.json

Built-in protocols are rest (the default) and mcp. Installed connection drivers can define other protocols.

REST Connection

A native OpenAPI connection:

{
  "baseUrl": "env:SHELTER_API_URL",
  "openapi": {
    "source": "./openapi.json",
    "exposure": "auto",
    "sessionHeader": "x-agent-session-id"
  },
  "auth": { "type": "bearer", "token": "env:SHELTER_API_TOKEN" },
  "filter": { "exclude_paths": ["/health"] }
}

The openapi block activates native tools. The runtime prepares the document when it builds a session, registers one tool per selected operation, and withholds the connection from request. A missing or invalid document fails preparation; the runtime does not fall back to request. A document file, format, or specUrl without the openapi block does not activate native tools.

A minimal request connection is a baseUrl plus an empty policy:

// spec.json
{ "baseUrl": "https://api.adviceslip.com" }
// policy.json
{ "endpoints": {} }

A named agent sees a connection only when its agent.json connections list includes it. Omit auth for a public API; there is no "none" auth type.

REST calls through native tools and request refuse redirects, cap responses at 5 MiB, and time out after 30 seconds. Set baseUrl to the final API URL.

MCP Connection

{
  "protocol": "mcp",
  "transport": "stdio",
  "command": "uvx",
  "args": ["mcp-server-github"],
  "env": { "GITHUB_TOKEN": "env:GITHUB_TOKEN" }
}

See MCP Servers for transports and options.

Fields

FieldDescription
protocol"rest" (default) or "mcp".
baseUrlAPI base URL. Required for REST.
specUrlDocumentation URL. Does not activate native tools.
skipRequestToolWithhold a REST connection from request, for example when custom tools own API execution. Does not disable native tools.
testRequest for Test connection: path relative to baseUrl, method (GET default, POST, or HEAD), query, body, headers, and expectStatuses (default [200]).
testPathPath for a HEAD test request when test is absent, for example "/me".
format"openapi", "graphql", "grpc", "rest", or "aws-api". REST only.
auth.type"bearer", "api_key", "basic", "header", "body", "google_service_account", or "oauth2". MCP supports bearer, and oauth2 over HTTP or SSE. oauth2 takes no token.
auth.tokenCredential reference for bearer, api_key, basic (base64 user:pass), and header.
auth.headerREST only. Header for bearer, api_key, and header. Defaults to Authorization, or X-API-Key for api_key.
auth.prefixREST bearer prefix (default Bearer). MCP bearer always sends Authorization: Bearer <token>.
auth.fieldsFor body: credential fields merged into the JSON body of POST, PUT, and PATCH requests from the request tool, and of every non-GET, non-HEAD request from native operation tools.
auth.clientEmailFor google_service_account, with auth.privateKey, auth.scope (default https://www.googleapis.com/auth/cloud-platform), and auth.tokenUri (default https://oauth2.googleapis.com/token).
auth.providerManaged OAuth application ID (oauth2 only).
filterNative operation selection: tags and include_paths intersect; exclude_paths applies last.
transport"stdio", "sse", or "http". MCP only.
command, argsCommand and arguments to spawn. MCP stdio only.
envEnvironment variables. MCP stdio only.
urlServer URL. MCP SSE and HTTP only.
headersHTTP headers. REST, and MCP SSE and HTTP.
contextInjectionForward request context values into API requests. See Context Injection.
openapi.source./file.json, ./file.yaml, ./file.yml, an HTTP(S) URL, or env:VAR resolving to a URL. Files must stay inside the connection directory after symlink resolution.
openapi.exposureauto (default), direct, or discovery. See Discovery.
openapi.sessionHeaderHeader that carries the runtime session ID on operation requests. Credential header names are reserved. Document fetches do not receive it. Operations of this connection run one at a time.

Context Injection

contextInjection forwards values from the chat request's context into calls to this connection, such as a tenant or workspace ID:

{
  "baseUrl": "https://api.example.com",
  "auth": { "type": "bearer", "token": "env:API_TOKEN" },
  "contextInjection": {
    "tenant_id": { "in": "header", "field": "X-Tenant-Id", "required": true },
    "org_id": { "in": "query", "field": "org_id" }
  }
}

Each key names a key in the request context:

{
  "message": "What are my recent orders?",
  "scope_id": "user-123",
  "context": { "tenant_id": "tenant-abc", "org_id": "org-456" }
}
FieldDescription
in"header", "query", "path", or "body".
fieldHeader name, query parameter, {placeholder} in the endpoint path, or body field.
requiredWhen true, a request fails if the context lacks the key. Default false, which skips the key.
  • query is not appended when the request already sets that parameter.
  • body adds the field to the JSON body, which is sent only for POST, PUT, and PATCH. The request tool rejects a call whose data is not an object when body injection is configured.
  • Native OpenAPI connections support header, query, and body injection. Path injection fails preparation. Native query injection supplies defaults that model arguments can override. Model values for injected body fields are rejected.

Context can contain client-supplied values and is not authenticated identity. The downstream API must validate tenant access. See Scope.

Native OpenAPI connections

Tools and arguments

Operation tools are named <connection>__<operationId>. Characters other than letters, digits, underscores, and hyphens become underscores. Names longer than 64 characters are shortened with a hash suffix. A missing operationId is derived from the method and path. Duplicate identifiers or tool names fail preparation.

Arguments contain only the declared parameter groups:

{
  "path": { "residentId": "r-2" },
  "query": { "notify": false },
  "body": { "adopterName": "Dana Kim" }
}

The model cannot select a method, URL, connection, header, or operation through arguments. Arguments that violate the schema fail with Invalid parameters: before hooks, policy, confirmation, and HTTP.

Supported contracts:

  • OpenAPI 3.0.x and 3.1.x, with GET, HEAD, POST, PUT, PATCH, and DELETE.
  • Path parameters with primitive values and simple serialization.
  • Query parameters with primitives or primitive arrays and form serialization.
  • JSON bodies, including application/*+json media types and DELETE bodies.
  • Internal JSON Pointer references. External or cyclic references fail.
  • OpenAPI 3.1 tuples with prefixItems and items: false. Draft-style items: [...] tuples are rejected.
  • Schemas without a __proto__ property.

Limits are 16 MiB per document, 2,000 operations, reference depth 64, and 1,024 characters per regex pattern. An unsupported selected operation fails preparation; exclude it with filter.

Document servers, security, and vendor extensions do not route requests, supply credentials, or grant permissions. Configure credentials in spec.json and permissions in policy.json.

Discovery

Every native connection exposes <connection>__discover:

  • Without arguments, it returns the tag index.
  • query searches operation IDs, tool names, paths, tags, and summaries, up to 256 characters.
  • tag selects a tag. Pages hold at most ten operations; cursor fetches the next page.
  • Returned operations become typed tools on the next model reply. Calling them in the same reply is refused.
  • refresh: true reloads the source. It is refused while native calls are pending.

At most 40 native operation tools are visible per model round across native connections. direct connections that exceed the budget fail session preparation. auto connections use direct mode when they fit beside the direct connections, and discovery otherwise. Discovery evicts the least recently used operations.

Model provider compatibility

Operation schemas are sent to the model as formal tool definitions. The runtime validates arguments against the original schema regardless of how the provider adapter translates it. See Providers.

Policy and confirmation

Native operations differ from request in these ways:

  • A write without a matching policy entry is denied.
  • GET and HEAD are reads. A matching entry with "intent": "read" makes another method a read.
  • All matching keys apply, including wildcards, and the strictest tier wins. Keys match the template and the encoded concrete path.
  • review and never also deny reads.
  • body in an entry lists allowed top-level body keys. Lists from several matching entries intersect, and an empty list permits no body. Other keys are rejected before confirmation.
{
  "endpoints": {
    "POST /residents/*": { "returns": [], "confirm": true },
    "POST /residents/{residentId}/adopt": { "returns": [], "body": ["adopterName"] }
  }
}

Approval is bound to the call and its effective arguments. A hook rewrite after approval asks again.

Freshness and results

File sources are read on every session preparation. URL sources are cached for 60 seconds per URL and credential set, and failures are not cached. A session keeps its catalog until refresh. Document fetches time out after 15 seconds and receive connection credentials only on the baseUrl origin.

Results over 16 KiB are kept in session memory and returned as a digest with a handle. read_spilled_result reads a nested dot path and pages arrays with offset and limit. The store keeps at most 16 results or 32 MiB and does not survive a process restart.

Custom tools

Custom handlers can call any connection with ctx.request(). These calls apply connection authentication and context injection, but not endpoint policy, field restrictions, schema validation, or confirmation. See Tools.

policy.json

{
  "endpoints": {
    "GET /tickets/{id}": {
      "returns": ["Ticket"]
    },
    "POST /tickets/{id}/comments": {
      "returns": ["Ticket"],
      "confirm": true,
      "reason": "Adds a visible support-ticket comment"
    },
    "DELETE /tickets/{id}": {
      "returns": ["Ticket"],
      "confirm": "never",
      "reason": "Ticket deletion is not allowed through the agent"
    }
  },
  "fieldRestrictions": [
    {
      "entity": "Customer",
      "field": "payment_token",
      "policy": "never_retrieve",
      "sensitivity": "secret",
      "reason": "Payment tokens are never exposed to the agent"
    },
    {
      "entity": "Customer",
      "field": "email",
      "policy": "retrieve_but_redact",
      "sensitivity": "pii_name"
    }
  ],
  "alternativeLookups": [
    {
      "restrictedField": "Customer.payment_token",
      "alternativeEndpoint": "GET /customers/{id}/billing-status",
      "description": "Use billing status instead of raw payment credentials"
    }
  ],
  "delegations": { "escalateConfirm": true }
}
FieldDescription
endpointsRequired. Keys are METHOD /path, with {param} and * patterns.
endpoints.*.returnsRequired. Entity names the response contains, used by field restrictions. Use [] when none apply.
endpoints.*.confirmOmitted, true, "review", or "never". See Action Tiers. false is invalid.
endpoints.*.reasonText shown in a confirmation card or denial.
endpoints.*.thresholdsParameter rules that raise the tier. See Threshold Escalation.
endpoints.*.intent"read" lets a mutating method run as a read.
endpoints.*.bodyAllowed top-level body keys. Native OpenAPI only.
fieldRestrictionsentity, field, policy, sensitivity, and optional reason. See Field Restriction Policies.
alternativeLookupsPrompt guidance of the form "Instead of restrictedField, use alternativeEndpoint".
delegations.escalateConfirmWhen true, confirm becomes review for calls made in a delegated session (a subagent or an autonomous run).

Action Tiers

confirmBehavior
omittedRun without approval.
truePause for user approval, then run.
"review"Refuse with a reason stating human review is required. The runtime does not queue the call.
"never"Refuse.

For the generic request tool:

  • Tiers apply only to writes (POST, PUT, PATCH, DELETE). Reads run without a policy check.
  • A write that matches no entry runs without approval. Add a "*" or wildcard entry to set a minimum tier.
  • An exact key wins. Otherwise the strictest matching pattern applies.

Confirmation Model describes the approval flow and how custom-tool tiers map to these values.

Field Restriction Policies

PolicyEffect
never_retrieveField removed from the JSON response.
retrieve_but_redactValue masked: ****6789 for a value of 8 or more characters ending in 4 digits, **** otherwise.
role_gatedField always removed. allowedRoles is accepted but not applied.

Restrictions also add "Do not request" and "Will be redacted" lines to the system prompt.

For request calls, the scrubber checks the top-level object, or each item of a top-level array, against every entity in the endpoint's returns. A key named after an entity, such as customer or tickets, limits everything under it to that entity's restrictions. Other keys, such as data or items, keep the combined set. Entity names match regardless of case.

Native OpenAPI connections identify entities from the response schema: data at a position whose schema references #/components/schemas/Taxpayer is a Taxpayer, whatever its key. Restrictions apply to every oneOf, anyOf, and allOf branch, even without a returns entry. Key-name matching applies where the schema names no restricted component.

MCP tools, connection-driver tools, and ctx.request() results are not scrubbed. Verify the response shape in an eval before relying on removal or redaction.

Threshold Escalation

{ "field": "body.amount", "above": 10000, "escalate": "review" }

Add this object to an endpoint's thresholds array. When body.amount exceeds 10000, the tier escalates to review and the call is denied. escalate accepts "review" or "never". A missing or non-numeric value escalates as if the threshold were exceeded, and numeric strings compare as numbers. field is a dot path into the grouped parameters: body and query, plus path for native tools. Native thresholds see values after context injection.

Packaged Connections

Packages listed in amodal.json#packages can ship connections. See amodal.json. A local connection with its own spec.json takes precedence over a packaged one of the same name. To customize a packaged connection while keeping its spec.json, add surface.md, policy.json, oauth.json, entities.md, or rules.md under the top-level connections/<name>/, not under amodal/. A local policy.json merges with the package policy.