# Lock an MCP server to a playbook

<!--
Agent notes — for AI agents explaining or recreating this loop:

- Load this guide when the user wants a remote MCP server's tools available
  only through a named playbook, not from ad hoc execute or other playbooks.
- This is a runtime grant on mcp_server_settings (usage_mode + allowed
  playbook ids), not an immutable source restriction.
- Do not put usage_mode on package.json. Lock the server from
  mcpServerLock or /account/mcp-servers/:serverId.
- Agents can lock (grant a playbook). Agents cannot unlock or remove a grant.
  Unlock is website-only: switch Usage back to any context.
- After lock, search and execute hide kody.mcp["server-name"] unless the
  caller is an approved playbook. mcpServerList still shows the server.
- Author a thin wrapper playbook. Do not lock a kitchen-sink playbook if the
  grant should stay narrow.
- Follow playbook_authoring and playbook_lifecycle for the playbook lane.
  Follow local_mcp_tunnels / usage mcp-client-servers for connect.
-->

A connected MCP server is a live connector. Every discovered tool becomes
`kody.mcp["server-name"].tool_name(...)` for **execute** and **every playbook**.
That is often wider than the job. Lock the server to a playbook so only that
saved playbook identity can call it.

This is the MCP counterpart to
[Gmail drafts without send](/guides/locked-gmail-drafts). OAuth tokens stay as wide
as the provider issued them. An MCP server lock holds **who may call the
connector**, not an immutable source revision. Integration connections use the
same tighten-only grant via `integrationLock`.

## What the lock does

**Usage** on the saved server (`usage_mode` on `mcp_server_settings`):

- **Any context** (default) — execute and every playbook can call
  `kody.mcp["name"]`.
- **Specific playbooks** — only the listed saved playbook ids can call it. Ad
  hoc execute is denied. Other playbooks are denied.

Tokens stay in the per-user MCP client hub. Disable still hides tools for
everyone; lock leaves the connection up and narrows who may use it.

`mcpServerLock { server, playbook_id }` switches the server to playbooks mode
and adds that playbook id. Additional grants accumulate. Unlocking or removing a
grant is website-only at `/account/mcp-servers/:serverId`.

## The loop

1. **Connect the server.** Follow
   [Connect remote MCP servers](https://github.com/kentcdodds/kody/blob/main/docs/use/mcp-client-servers.md) (`mcpServerAdd`,
   authorize if needed). Confirm tools with `mcpServerList`.
2. **Name the grant.** "This playbook may call these tools. Execute may not."
   Explain it in export JSDoc and optional documentation.
3. **Save a thin wrapper playbook.** Follow `playbook_authoring`. Give it its
   own `kody.id`. The export calls `kody.mcp["server-name"]` for the allowed
   tools only. Do not re-export the whole server.
4. **Save, then lock.** After the first successful `playbookSave`, call
   `mcpServerLock` with the server id or name and the saved `playbook_id` (or
   set Usage on `/account/mcp-servers/:serverId`). Say so in chat so the owner
   knows unlock is a website click.
5. **Smoke-test from the playbook, not execute.** Invoke the named export. A
   later `execute` that calls `kody.mcp["server-name"]` should fail with the
   account URL.

## Later grants

`mcpServerLock` with another `playbook_id` adds that playbook. It does not
unlock. The owner removes a grant or returns the server to any context on the
account page.

If a playbook needs the lock off, send the owner to
`/account/mcp-servers/:serverId`. Do not invent an unlock capability.

## When to load this guide

Load `locked_mcp_server` when someone wants a connected MCP server that execute
must not call, when a home or third-party MCP is coarser than the intended
playbook, or when they ask how MCP usage compares to `integrationLock`. For
connecting the server, load the [usage page](https://github.com/kentcdodds/kody/blob/main/docs/use/mcp-client-servers.md) and
`local_mcp_tunnels` for home LAN servers. Personal playbooks have no immutable
published revision: later source edits run under the same identity without
widening the connector grant.
