# Guarded MCP server


ainfra can serve its typed application operations to MCP clients over stdio.
The server is bound to one deployment at startup, is read-only by default, and
never treats tool annotations or conversational claims as authorization.

## Start a read-only server

Run the server from a deployment directory:

```sh
ainfra mcp serve --stdio
```

Or bind it explicitly:

```sh
ainfra mcp serve --stdio --project path/to/deployment
```

Protocol frames are the only data written to stdout. Diagnostics and
operational logs use stderr or the configured logging sinks. The startup
project, configuration, executable paths, cache, run storage, environment
allowlist, and logging destinations cannot be overridden by tool arguments.

The default registry provides read-only tools for:

- build and contract versions;
- project, deployment, and verified template inspection;
- retained status, standardized output, and inventory;
- deployment, run, template, and environment diagnostics; and
- published schemas and contract resources.

## Enable optional capabilities

Additional tools must be allowlisted when the server starts:

```sh
ainfra mcp serve --stdio --capability planning
ainfra mcp serve --stdio --capability deployment
ainfra mcp serve --stdio \
  --capability deployment \
  --capability destruction
```

`planning` adds reconciliation, template lock/update/migration, and reviewed
OpenTofu planning. Planning may create private run evidence or
digest-addressed template cache entries, but it does not apply infrastructure
or publish a template lock.

`deployment` adds independently authorized reconciliation, template writes,
apply, configure, and composed deploy. `destruction` adds destroy execution,
requires `deployment`, and accepts only an approval with destroy intent.
Enabling a capability makes tools discoverable; it does not approve a
mutation.

## Configure signed approvals

Mutation tools require a trust file selected by the operator at startup:

```sh
ainfra mcp serve --stdio \
  --project path/to/deployment \
  --capability deployment \
  --authorization-trust path/to/mcp-trust.json
```

The trust file must be a private, non-symlink JSON file:

```json
{
  "schemaVersion": 1,
  "issuers": [
    {
      "id": "operator-1",
      "publicKey": "BASE64_ED25519_PUBLIC_KEY"
    }
  ]
}
```

An approval is a strict JSON envelope containing `schemaVersion: 1`, the raw
`grant` object, and a base64 Ed25519 signature. Sign the UTF-8 bytes
`ainfra-mcp-authorization-v1\n` followed by the exact raw JSON bytes used as
the envelope's `grant` value.

The grant binds all authority-relevant inputs:

```json
{
  "authorizationId": "approval-2026-08-16-1",
  "issuer": "operator-1",
  "caller": "agent-1",
  "projectRoot": "/absolute/canonical/deployment",
  "operation": "apply",
  "planId": "REVIEWED_PLAN_ID",
  "planDigest": "sha256:...",
  "intent": "apply",
  "approvedAt": "2026-08-16T12:00:00Z",
  "expiresAt": "2026-08-16T12:05:00Z"
}
```

The issuer must differ from the caller. Approval must be current and match the
startup root, canonical operation, plan identity, digest, and intent exactly.
Opaque approval bytes are never logged, returned, or retained. Sanitized
authorization evidence records only the authorization ID, caller, issuer,
binding, and expiry.

## Reviewed mutation workflow

For infrastructure mutations, use the planning capability to create a saved
plan and present its run ID and digest for independent review. Restarting with
both `planning` and `deployment` is allowed, but the same agent request cannot
approve the plan it created. Apply, deploy, and destroy always consume an
existing reviewed plan; no mutation tool creates an implicit replacement.

Template lock/update and reconciliation previews similarly return stable plan
IDs and digests. Their execution tools recompute the complete plan under the
deployment operation lock before writing. Changed source content or stale
preconditions require a new preview and approval.

## Operational safety

- Keep trust files and private keys outside the deployment and source tree.
- Enable only the capabilities needed for the current session.
- Use short approval expiries and unique authorization IDs.
- Treat an interrupted mutation as inspection-required; consult `ainfra.status`
  and retained logs before retrying.
- Do not expose the stdio server through a network bridge. Network transports
  are outside the v1 threat model.
- Close the MCP session normally so the stdio child can shut down cleanly.

The MCP result envelope is versioned independently from the transport. Clients
should inspect `apiVersion`, `tool`, `ok`, `result`, and `diagnostics` rather
than parse display text.


---
Source: https://projectious-work.github.io/ainfra/docs/mcp/index.md
