> ## Documentation Index
> Fetch the complete documentation index at: https://docs.armature.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Managed Code Mode MCP: run scripts against your API

> Publish an OpenAPI spec as a hosted MCP server. Agents call one execute_script tool that runs sandboxed code against a generated client bound to your API.

Managed Code Mode MCP turns an OpenAPI spec into a hosted MCP server that exposes your entire API surface through a single scriptable tool. Instead of generating one MCP tool per endpoint, Armature compiles your spec into an SDK bundle and serves it behind a `search_sdk` + `execute_script` pair. Agents browse the SDK, then write small TypeScript snippets that call your API directly — chaining requests, transforming responses, and returning the final value in one round trip. The endpoint lives at `/product-mcp/<source-slug>`, runs each script in an isolated sandbox with egress guardrails, and writes structured telemetry for every call.

<Note>
  Managed Code Mode is opt-in per MCP server source. Reach out to Armature support to enable it on an organization and to provision a `source_slug` for your API.
</Note>

## When to use Code Mode

Reach for Managed Code Mode MCP when:

* Your API has more than a handful of endpoints and you do not want to expose each one as a separate MCP tool.
* Agents need to chain calls (read, filter, then write) without burning tokens on intermediate tool round trips.
* You want a typed client experience for agents — full parameter shapes, response types, and JSDoc examples — without authoring tool schemas by hand.
* You want every script execution captured as telemetry you can analyze alongside your other Armature MCP runs.

If you only need a handful of tools or you already operate a hand-written MCP server, use [Connect an MCP server](/mcp-servers/connecting) instead.

## How it works

1. You upload an OpenAPI spec (plus an optional overlay and manifest) to Armature.
2. Armature compiles the spec into a bundle containing a generated SDK, an operation map, an SDK index for prompting, and a vector search index.
3. The bundle is stored as an immutable artifact and promoted to `active` for the source's environment.
4. Agents connect their MCP client to `https://<your-org>.armature.app/product-mcp/<source-slug>`.
5. On `initialize`, the server sends back instructions plus the SDK index so the agent knows what calls are available.
6. The agent uses `search_sdk` to look up exact schemas, then calls `execute_script` to run a TypeScript snippet against a client bound to your API.

## Connect an MCP client

Add the managed endpoint to your client's MCP server configuration. The URL is your Armature subdomain plus `/product-mcp/<source-slug>`, and authentication uses your Armature API key.

```json theme={null}
{
  "mcpServers": {
    "acme-api": {
      "url": "https://your-org.armature.app/product-mcp/acme-api",
      "headers": {
        "Authorization": "Bearer amt_<key-id>_<secret>"
      }
    }
  }
}
```

To target a non-production environment, append `?environment=staging` to the URL. The server reads the `mcp-session-id` header on subsequent requests and groups telemetry by session.

## The two tools

A managed Code Mode server exposes exactly two tools regardless of how large your spec is.

### search\_sdk

Use `search_sdk` when you need to confirm the shape of a request body, a response type, or a parameter list before writing a script. It runs against the bundle's SDK index and returns ranked snippets with operation IDs and signatures.

| Argument | Type    | Required | Description                                                       |
| -------- | ------- | -------- | ----------------------------------------------------------------- |
| `query`  | string  | yes      | Free-text query (operation name, path fragment, or schema field). |
| `limit`  | integer | no       | Number of matches to return. Defaults to 12, max 20.              |

`search_sdk` is annotated as read-only and idempotent — agents can call it freely without producing side effects.

### execute\_script

`execute_script` is the primary action. The script body is wrapped in an async IIFE and executed in a sandbox with a single global — the generated client — bound to your API. Use top-level `await` and return the final value; whatever you return becomes the tool result.

| Argument | Type   | Required | Description                                                                               |
| -------- | ------ | -------- | ----------------------------------------------------------------------------------------- |
| `code`   | string | yes      | TypeScript or JavaScript source to execute. Wrapped in `async () => { … }` automatically. |

The client global is named after your manifest's `clientName` (for example, `acme` or `api`). Auth is injected for you based on the manifest — agents never see raw credentials. Network egress is limited to the API's base URL.

```ts theme={null}
/**
 * @intent List the most recent open pull requests for a repo.
 * @context triage workflow for the on-call rotation
 * @frustration_level low
 */
const prs = await acme.repos.listPullRequests({
  owner: 'acme-tech',
  repo: 'platform',
  state: 'open',
  per_page: 5,
});

return prs.map((pr) => ({
  number: pr.number,
  title: pr.title,
  author: pr.user.login,
  updated_at: pr.updated_at,
}));
```

Prefix every script with JSDoc `@intent`, `@context`, and `@frustration_level` tags. Armature captures them as structured telemetry so you can analyze why agents reached for the tool and whether the run satisfied them.

<Warning>
  `execute_script` is annotated as destructive and open-world. Treat it like any tool that can mutate upstream state — gate it behind appropriate API key roles and audit telemetry regularly.
</Warning>

## Publish or update a bundle

Bundles are uploaded over HTTP and signed with the same Armature API key used to authenticate. Editor role or higher is required to upload a bundle; admin or owner is required to promote one to `active`.

`POST https://<your-org>.armature.app/api/openapi-artifacts/upload`

Required headers:

* `Authorization: Bearer amt_<key-id>_<secret>`
* `x-armature-timestamp: <unix-seconds>` — within 300 seconds of now.
* `x-armature-signature: <hex>` — `HMAC-SHA256(secret = api_key, message = "<timestamp>.<raw-body>")`.

Body fields:

| Field                                                    | Required | Description                                                                                   |
| -------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------- |
| `source`                                                 | yes      | The `source_slug` provisioned for your managed MCP server.                                    |
| `environment`                                            | no       | Target environment. Defaults to `production`.                                                 |
| `openapi`                                                | yes      | The spec as a JSON object or a JSON/YAML string.                                              |
| `overlay`                                                | no       | OpenAPI overlay applied to the spec before compilation.                                       |
| `manifest`                                               | no       | Overrides for `vendorName`, `clientName`, auth, base URLs, and analytics tags.                |
| `repository`, `commitSha`, `branch`, `pullRequestNumber` | no       | Provenance metadata recorded against the bundle row.                                          |
| `promote`                                                | no       | Set to `true` (admin+) to activate the bundle immediately. Otherwise it lands in `generated`. |

A successful upload returns the bundle ID, storage path, OpenAPI and overlay hashes, an `apiDiff` against the currently active bundle (`added`, `removed`, `signatureChanged`, `breaking`), and a `rollback.previousBundleId` you can promote to revert.

```bash theme={null}
TIMESTAMP=$(date -u +%s)
BODY=$(cat upload.json)
SIG=$(printf '%s' "$TIMESTAMP.$BODY" \
  | openssl dgst -sha256 -hmac "$ARMATURE_API_KEY" -hex \
  | awk '{print $2}')

curl -X POST "https://your-org.armature.app/api/openapi-artifacts/upload" \
  -H "Authorization: Bearer $ARMATURE_API_KEY" \
  -H "x-armature-timestamp: $TIMESTAMP" \
  -H "x-armature-signature: $SIG" \
  -H "content-type: application/json" \
  --data "$BODY"
```

Upload bodies are capped at 6 MB; compiled bundles are capped at 8 MB.

## Sandbox and egress guardrails

Every `execute_script` call runs in an isolated VM. The sandbox:

* Exposes only the generated client global and standard JavaScript primitives — no `require`, `process`, file system, or child process access.
* Restricts outbound HTTP to the base URL declared in your manifest. Calls to other hosts are blocked.
* Enforces a per-script timeout sourced from your manifest's `scriptTimeoutEnv`.
* Strips secret values from telemetry — only manifest-declared analytics tags and the JSDoc header you write are recorded.

If a script throws, the tool result is marked as an error and the exception message is returned to the agent. The script's runtime, telemetry, and structured visible output are still recorded so you can replay the failure.

## Roles and access

* **Connecting an agent** requires any role with access to the source's MCP server record. Standard tool authorization applies on top of the sandbox.
* **Uploading a bundle** requires editor, admin, or owner.
* **Promoting a bundle to active** requires admin or owner.

See [Roles](/mcp-api/roles) for the full permission matrix and [Authentication](/mcp-api/authentication) for how to generate keys.

<CardGroup cols={2}>
  <Card title="Connect an existing MCP server" icon="plug" href="/mcp-servers/connecting">
    Register a hand-written MCP server with Armature instead of compiling one from OpenAPI.
  </Card>

  <Card title="Tool monitors" icon="zap" href="/mcp-servers/tool-monitors">
    Schedule recurring health checks against any MCP server's tools.
  </Card>
</CardGroup>
