Skip to content

Use from Claude Code

Connect Claude Code (or any MCP client) to the Worker's /mcp gateway with a scoped bearer token and queue approval-gated coding runs.

Updated

The Worker serves an MCP gateway at https://<worker-host>/mcp (streamable HTTP). Claude Code connects with a per-agent bearer token. It can queue coding runs, read run and approval state, and use the mailbox and memory tools its token’s scopes allow. It cannot approve anything. A queued run waits until a human approves it in the dashboard or in Slack.

1. Mint a token

From the repo root:

node scripts/mint-token.mjs --agent claude-code --scopes sandbox:exec,runs:read --ttl-days 30 --host <worker-host>
Flag Meaning
--agent <name> Principal recorded on every audit row. No whitespace, max 128 chars.
--scopes <a,b> Grant only needed scopes. MCP scopes include gmail:read, email:read, email:draft, email:send, email:delete, memory:read, memory:write, runs:read, sandbox:exec, mcp:external, integrations:read, integrations:execute and admin:tokens. --help lists all accepted scopes, including the separate API-key scopes.
--ttl-days N Optional. The KV entry expires after N days, and the token stops working then.
--host <host> Your Worker host, used in the printed claude mcp add line.
--write Also runs the printed wrangler kv key put … --remote for you. Without it, nothing is written.

The script prints the raw shb_… token once, the KV key (tok_<sha256(token)>), the record value, the wrangler command and the claude mcp add line. Only the hash is stored, so a lost token cannot be recovered. Mint a new one instead.

--binding AGENT_TOKENS --config apps/backend/wrangler.jsonc writes to the namespace id in wrangler.jsonc. If you deploy with Alchemy, first check that this id matches the shiba-agent-tokens namespace (npx wrangler kv namespace list). If it doesn’t, run the command with --namespace-id <id> in place of --binding AGENT_TOKENS.

2. Add the server to Claude Code

claude mcp add --transport http shiba https://<worker-host>/mcp --header "Authorization: Bearer shb_…"

Run claude mcp list to confirm that shiba connects. A 401 means the token is missing, malformed, revoked, expired, or was written to a different namespace.

External coworkers

External coworkers (a background agent or any Streamable HTTP MCP client) use the same /mcp gateway and shb_… tokens as Claude Code. The usual pairing for run queueing:

node scripts/mint-token.mjs --agent scout --scopes sandbox:exec,runs:read --ttl-days 30 \
  --host <worker-host> --namespace-id <shiba-agent-tokens-id> --write

Register the connector with URL https://<worker-host>/mcp and header Authorization: Bearer shb_… (store the token in the client vault, not in chat). Smoke-test with list_runs (expect [], not 401). Queue work with queue_run — the response is { approvalId, runId: "agent-tool:<approvalId>", status: "pending_approval" } until a human approves in the dashboard or Slack. Poll with run_status and list_approvals. There is still no approve tool on /mcp.

Pair sandbox:exec with runs:read on tokens that both queue and poll. Run visibility is per-principal (scout only sees runs it queued). Revoke by deleting the KV entry (tok_<sha256>) as in section 4 below.

Before minting, confirm npx wrangler whoami shows the Cloudflare account that owns your Worker host and that --namespace-id targets the live shiba-agent-tokens namespace.

3. Tools and scopes

Tool Scope What it does
queue_run sandbox:exec Queues {repoUrl, task, baseBranch?, publishPullRequest?, testCommand?} as a pending approval on the shared default orchestrator, the same route as POST /api/runs. Returns approvalId and runId (agent-tool:<approvalId>). It never starts a run.
run_status runs:read Returns one run record by runId.
list_runs runs:read Lists run records, newest first (limit, default 20).
list_approvals runs:read Lists pending and recently decided run approvals. Email approvals are left out.
list_mailboxes, list_emails, get_email, get_thread, search_emails, mark_email_read, extract_otp, latest_verification email:read Mailbox reads.
create_draft, update_draft, draft_reply, move_email email:draft Draft and organize.
send_email, send_reply email:send Queue an email send for human approval.
delete_email email:delete Queue a delete for human approval.
memory_recall, memory_sessions memory:read Read memory banked by this verified token principal.
memory_bank, memory_forget memory:write Write or forget facts in this verified token principal’s memory partition.
list_skills, get_skill_instructions integrations:read Discover readable, enabled extension skills and load one skill’s instructions by ID and version. Instructions do not grant tools or approval.
integrations.search_actions integrations:read Discover actions for an employeeId with current integration grants and an exact toolkitSlug. A scope alone does not grant an app connection.
integrations.execute integrations:execute Request an employee-authorized action with exact arguments; execution requires the separate human approval and current connection/resource grants.

Select a skill as an MCP prompt

Clients that support MCP prompts can use prompts/list and prompts/get to select enabled extension skills without writing files into the client’s skill folders. The bearer needs integrations:read. The list contains only skills readable by that verified principal in its organization, with the skill name as the display title and no instruction body. Private skills belonging to another principal, disabled skills and archived skills are excluded. Team-workspace skills remain unavailable through this bearer surface because it does not establish native workspace membership.

Each prompt name includes the skill ID and version, such as shiba_skill_<id>_v1. Refresh the client’s prompt list after editing a skill: selecting an old version fails instead of silently loading changed instructions. Retrieval checks the principal, scope, current visibility, enabled state and version again. Names remain distinct even when two skills share a display title. The picker offers up to 100 skills; list_skills and get_skill_instructions remain available for directory search and on-demand reads.

Selecting a prompt returns untrusted guidance, not permission to act. It cannot grant tools, approve a run or bypass any approval gate. No external tool or sandbox runs when a prompt is read. If the skill directory is unavailable, prompts are withheld while the other MCP tools remain available.

Add an owner-reviewed external MCP server

In MCP → External MCP servers, create a draft with an HTTPS Streamable HTTP endpoint. Choose a Worker credential reference (the name of an already provisioned MCP_CREDENTIAL_* secret) or Custom OAuth public client. Custom public hosts must be exact hostnames in the deployment’s comma-separated MCP_TRUSTED_HOSTS. The eight curated hosts for Stripe, Notion, Sentry, PostHog, Figma, Atlassian, GitHub and Linear are also trusted by the registry; an empty custom allowlist does not disable those presets. Trusting a host permits the explicit connection flow; it does not connect an account, enable a tool or grant access.

Integrations → MCP servers provides those eight presets with account labels and only the authentication methods supported for each preset. Six retain the OAuth path; GitHub and Linear use restricted bearer tokens and vendor-documented read-only endpoints. OAuth requires the deployment’s credential encryption key (MODEL_CREDENTIAL_KEY), a vendor-registered client ID in MCP_OAUTH_CLIENT_<CATALOG_ID> (uppercase, with hyphens changed to underscores), and the exact Shiba callback URL registered with the vendor: https://<worker-host>/api/v1/mcp/oauth/callback. For example, Atlassian uses MCP_OAUTH_CLIENT_JIRA_ATLASSIAN. The preset is not proof that the vendor accepts the deployment’s client, requested scopes or account tier. Unsupported or missing vendor metadata, client configuration or consent leaves the connection unavailable. There is no automatic vendor-client registration. A token submitted through the dedicated bearer form is stored encrypted by the Worker; credentials are not model context or capability-catalog content. Never paste credentials into chat.

The GitHub (read-only) preset pins https://api.githubcopilot.com/mcp/readonly. Supply a fine-grained personal access token restricted to selected repositories and read permissions. The Linear (read-only) preset pins https://mcp.linear.app/mcp/readonly; use a key with only Read permission and limited team access. Both use the existing encrypted bearer form and HTTPS Streamable HTTP transport. GitHub client-secret OAuth and Linear dynamic client registration are not implemented by these presets. See GitHub’s endpoint documentation, GitHub’s authentication requirements and Linear’s official setup guide.

These two entries were checked against vendor documentation on November 7, 2026, not through live authenticated calls. Tools still start disabled and unreviewed; the recommendation defaults to approval-required until the owner reviews the exact schema and effect. A read-only endpoint is not a grant of access to every repository or team. No deployment token is reused automatically.

For a custom OAuth draft, enter the provider’s exact issuer URL, a pre-registered public client ID, and optional space-separated scopes, then choose Start custom OAuth. The operator must explicitly list both the MCP host and issuer host in MCP_TRUSTED_HOSTS; curated host defaults do not substitute for this custom-flow trust. Register the callback shown on the card with the provider and configure MODEL_CREDENTIAL_KEY. Follow the sign-in link in a new tab, then return and refresh connection status. Disable the server to cancel pending sign-in or revoke an existing connection. A fresh connection is required to replace an authorized account.

This custom lane discovers RFC 9728 resource metadata and RFC 8414/OIDC authorization metadata, pins the exact resource URL and chosen issuer, requires S256 PKCE and advertised public-client authentication (none), and keeps authorization, token and revocation endpoints on the issuer’s origin. Resource metadata must remain on the MCP origin. All requests reject redirects, URL credentials, IP literals and private/local hostname forms, and authorization metadata candidates are validated before fetching. The operator remains responsible for allowlisted domains and their public DNS: this is an explicit hostname trust boundary, not general DNS/IP pinning for arbitrary domains. Credential egress rechecks current host trust, so removing a host blocks exchange and refresh; local revocation still deletes stored credentials if the provider cannot be contacted. OAuth consent does not enable tools or waive approval.

Dynamic client registration, client secrets/confidential clients, cross-origin OAuth endpoints, and providers that lack the required metadata are unsupported in the custom lane. Compatibility is verified with local fixtures, not live provider accounts. Tokens and PKCE verifiers are encrypted at rest and remain in the Worker; no credential is sent to an agent container. Revocation attempts the advertised provider endpoint and always deletes local credentials; remote revocation cannot be guaranteed when that endpoint is absent, blocked or unavailable.

Use Discover / retry to fetch a bounded tool schema snapshot. Read each remote description as untrusted text. For each namespaced capability, explicitly review its effect as either read-only or “may mutate” (which requires approval for every exact call), then enable it. Grant each enabled capability only to an active MCP principal that has the mcp:external scope. A changed schema disables the affected capability and clears its principal grants until it is reviewed and enabled again. A server health error disables its capabilities; retry discovery before reconnecting.

Discovery accepts a strict JSON Schema subset: objects, strings, numbers, integers, booleans, arrays, string enums, and supported length/item/numeric bounds. Unsupported assertion keywords such as pattern, format, uniqueItems, or $ref fail discovery rather than being ignored.

The inbound /mcp gateway lists only currently enabled capabilities granted to its verified bearer principal. Policy is reloaded at invocation, and the normal durable tool receipt records the result. Calls classified as potentially mutating first create an exact-argument human approval, visible on the external-server card. The owner’s Approve and dispatch action executes only that frozen payload and settles its invocation receipt; a model retry never dispatches a write. The tool response is marked untrusted. Employee use additionally requires the selected employee’s capability and exact resource grants in Employee tools; an account connection or workspace-visibility label does not grant those permissions.

Shiba supports HTTPS Streamable HTTP, with redirects rejected, and no-auth, bearer-reference or curated/custom public-client OAuth authentication. SSE-only and stdio servers, private/local/metadata destinations, custom auth headers and private-network transport are unsupported. Network requests occur during an authenticated owner’s explicit sign-in, bearer revocation, discovery or approved invocation flow, including OAuth metadata discovery and token refresh when needed. A saved draft alone does not execute tools. These supported code paths still require live verification against each configured vendor and deployment.

Memory ownership and legacy compatibility

MCP memory facts are partitioned by the verified token principal. memory_recall searches only that principal’s facts, and memory_sessions filters to the same exact principal; a caller cannot select another agent. Dashboard compatibility routes similarly expose legacy facts and sessions only when the legacy agent name exactly matches the authenticated user identity. Loopback-only development uses the default agent partition. Owner-scoped workspace memory sources use a separate hashed identity partition.

Older facts and sessions in the shared registry have no owner field beyond their legacy agent name. Shiba does not merge those rows across identities, infer ownership from content, or silently move them into a user’s workspace. Rows with an exact identity match remain readable through the compatibility routes; unmatched legacy rows stay stored but inaccessible through user-facing memory reads. There is no automatic migration or deletion. A future migration would require an operator-reviewed mapping from a legacy agent partition to exactly one owner before moving its data.

Dashboard users can save Projects in the private workspace memory view. A project stores its name, purpose, selected employee defaults, and up to eight references to existing sources in the same owner partition; reference URLs are metadata only and are never fetched. Project edits increment a version. At approval time Shiba freezes the selected project version and a bounded snapshot of each active linked source, with source IDs, titles, kinds, update timestamps, and reference URLs. Source text remains explicitly untrusted reference data. Editing or deleting a project after approval does not change that approval or its run. Disabling, deleting, or expiring a linked source blocks new approvals until the owner edits the project to remove or restore that source. Existing approved snapshots remain available to their already approved run. Data stays in the existing owner-partitioned SQLite-backed Memory Durable Object; there is no provider dependency or background source fetch.

Pair a cloud agent with its mailbox

The --agent token principal is also the mailbox assignment key. In the dashboard Inbox settings, register an address with Cloud agent token principal set to that exact name (for example, scout). You can re-enter an existing address to change its assignment, or leave the field blank to unassign it. An unassigned mailbox is dashboard-only; MCP email tools do not see it. This is Shiba’s mailbox-scoped analogue to Goshen Email’s mailbox keys, without adding a second email service or moving mail out of your Cloudflare account.

node scripts/mint-token.mjs --agent scout --scopes email:read,email:draft,email:send \
  --host <worker-host> --namespace-id <agentTokensNamespace> --write

Give the resulting token only to that cloud agent and connect it to /mcp as above. list_mailboxes returns only addresses assigned to scout; direct mailbox calls and ID-based email/draft/thread tools also refuse other agents’ or unassigned mailboxes. The token cannot approve its own send or delete request. Agents can poll list_emails/search_emails for new mail; inbound mail does not automatically start a sandbox or execute instructions from a message. Existing mailboxes without an agent assignment must be paired before their MCP email tools can see them; dashboard access is unchanged.

For a first read-only test, grant only email:read. Configure Cloudflare Email Routing separately, send a message from another address, and verify it appears both in the Inbox and in the agent’s list_emails result. Add draft/send scopes later if needed; they still cannot bypass human approval.

The built-in chat agent also calls these tools: its run_code tool executes model-written JavaScript in an isolated Worker that dispatches back through this same gateway as the reserved principal orchestrator-agent (every scope except admin:tokens). To let the chat agent work a mailbox, register the address with Cloud agent token principal set to orchestrator-agent — no token minting needed. The same scoping rules apply: orchestrator-agent sees only mailboxes assigned to it, and sends or deletes still queue for human approval.

Two read-only tools serve the sign-in flow: extract_otp pulls a one-time code and magic links out of one email by id, and latest_verification scans the newest inbound mail for either — its mailbox arg defaults to the deployment’s agent identity address (AGENT_MAILBOX, or dev@tryshiba.dev when unset). Register that address in the Inbox and assign it to the agent principal (orchestrator-agent for the built-in chat agent, or the token principal of an external agent) and the agent can log into third-party apps end-to-end: it reads the code or link with sender and timestamp attached, while the raw body stays out of the response. Sends still queue for approval.

The gateway has no approve tool. This MCP tool accepts repository/task/branch/publish/test-command inputs, not a per-run harness or model; queued runs use the deployment’s configured defaults (AGENT_HARNESS, CODING_MODEL, and the selected harness’s model default) when approved. Harness implementation and verification status are documented in Coding Harnesses. No cloud end-to-end run is recorded; the dated local OpenCode exercise did not reach successful model inference.

Run visibility is per-principal: run_status, list_runs, and list_approvals only return records the calling token’s principal queued (queuedBy is stamped at intake). Dashboard run reads resolve the verified user’s own session; shared operator runs additionally require the configured approver authority. Slack and shared-room views enforce their own source and audience checks. A dashboard login does not grant access to every user’s runs. Pair sandbox:exec with runs:read on tokens that queue and poll runs.

4. How /mcp is authenticated

/mcp and /mcp/* are bypassed in Cloudflare Access, because an MCP client cannot complete an Access login. The Worker authenticates these requests itself with the bearer token:

  • The Worker checks the token’s shape, then looks up its SHA-256 hash in AGENT_TOKENS. A missing, unknown, revoked, or expired token gets a plain 401 before any MCP traffic is served, and so does a KV outage.
  • The verified record reaches the gateway in a Worker-set header. Client copies of that header are stripped.
  • Every tool call is checked against the token’s scopes and writes a row to the audit log (GET /api/audit). The row holds the principal, the tool, a hash of the arguments and the outcome, never the arguments themselves.

For clients using OAuth instead of a minted static token, Shiba also implements authorization-server and protected-resource metadata, public-client registration, owner consent, authorization code with PKCE, rotating refresh tokens and revocation. These authenticate the incoming MCP client; they are separate from the curated vendors’ outgoing OAuth connections above. OAuth clients still receive scoped principals and cannot approve their own actions. Client and deployment compatibility require their own live check.

See Security for the Access bypass policy and Approval Gates for how a human resolves a queued run.

Revoke a token

Delete its KV entry:

npx wrangler kv key delete --binding AGENT_TOKENS tok_<sha256> --config apps/backend/wrangler.jsonc --remote

The next request with that token gets a 401.

Type to search…

↑↓ navigate↵ selectEsc close