Skip to content

Host MCP OAuth

Use host MCP OAuth when your agent talks to a remote MCP server that speaks OAuth, and you want credentials on the serve host (or the hosted engine) instead of a Cursor account connector. Local login writes tokens next to your Cursor credentials. --store copies them onto the deployment as secrets so prod can reconnect after a redeploy.

The companion skill is skills/mcp-auth/SKILL.md.

What can host MCP OAuth do?

  • Authorize defineConnection({ url, oauth: true }) with a browser PKCE flow (agent-sdk mcp oauth <connection>)
  • Keep tokens in ~/.config/agent-serve/mcp-auth.json, bound to that connection's resource URL
  • Upsert deployment secrets with --store so hosted engines seed the same tokens from env
  • Keep privileged servers off the model with hostOnly: true while tools still call them through ctx.host.mcp

Prefer a Cursor account MCP connection when the connector already lives in the signed-in account dashboard:

ts
defineConnection({ cursorAccount: true }) // every connected connector
// or servers: "*" / servers: ["Linear"]

Use host OAuth when the server is yours (or private to your network) and the host must hold tokens.

How do I declare a host-OAuth connection?

Add one file under agent/mcp-connections/. The filename is the connection name you pass to the CLI and to host.mcp.

ts
// agent/mcp-connections/inventory.ts
import { defineConnection } from "@cursor/july/connections";

export default defineConnection({
  url: "https://mcp.example.com/inventory",
  oauth: true,
  hostOnly: true,
  description:
    "Inventory MCP (privileged). Call only from host tools, not the model.",
});

Rules of the road:

  • oauth: true is required for agent-sdk mcp oauth
  • hostOnly: true hides the server from the model; ctx.host.mcp and channel handlers still see it
  • Declare expected secret names on the agent when you plan to --store:
ts
// agent/agent.ts
export default defineAgent({
  // …
  hosting: {
    secretNames: [
      "MCP_OAUTH_INVENTORY_ACCESS_TOKEN",
      "MCP_OAUTH_INVENTORY_REFRESH_TOKEN",
      "MCP_OAUTH_INVENTORY_CLIENT_ID",
    ],
    // Hosted engines need an explicit allowlist for non-bootstrap hosts.
    egressDomains: ["mcp.example.com"],
  },
});

Secret names follow MCP_OAUTH_<CONNECTION>_* where <CONNECTION> is the connection filename uppercased with non-alphanumerics turned into underscores (inventoryMCP_OAUTH_INVENTORY_…).

How do I authorize locally?

From the agent project (Node 22.13+, not Bun):

bash
agent-sdk mcp oauth inventory

What happens:

  1. The Agent SDK loads agent/mcp-connections/inventory.ts and checks oauth: true
  2. It opens the authorization URL in your browser
  3. The callback lands on http://localhost:8787/callback
  4. Tokens land in ~/.config/agent-serve/mcp-auth.json (override the config dir with AGENT_SERVE_CONFIG_DIR)

If you're already authorized, the command prints that and exits. Re-run it after rotating tokens on the MCP server, or after you change the connection URL (tokens are bound to the resource URL).

How do I store credentials on a hosted deployment?

Authorize once, then push secrets to the deployment:

bash
agent-sdk mcp oauth inventory --store
# optional:
#   --slug my-agent
#   --team <cursor-team-id>

--store upserts:

SecretSource
MCP_OAUTH_<NAME>_ACCESS_TOKENaccess token (required)
MCP_OAUTH_<NAME>_REFRESH_TOKENrefresh token when the server returns one
MCP_OAUTH_<NAME>_CLIENT_IDdynamic client id when registration returned one

You must be signed in (agent-sdk login) with permission to set secrets on that slug. Secrets apply on the next deploy; run agent-sdk deploy (or wait for your usual deploy path) after --store.

On the engine, when mcp-auth.json is empty, serve seeds the OAuth provider from those env vars so host MCP calls work without a browser on the pod.

How do host tools call the server?

Keep privileged calls on the host:

ts
const result = await ctx.host.mcp.callTool(
  "inventory",
  "list_warehouses",
  { region: "us-east" }
);

The model never sees hostOnly tools in its MCP namespace list. If the agent asks to "check IDE MCP" or run mcp_auth, point it at your host tool instead.

What if authorization fails?

What you seeWhat to do
must be defineConnection({ url, oauth: true })Add oauth: true on that connection, or pick the right connection name
Callback never completesKeep port 8787 free; finish the browser login on this machine
Hosted calls unauthorized after --storeConfirm secrets with agent-sdk secrets list <slug>, then redeploy
Tokens ignored after URL changeExpected: resource URL binding drops stale entries. Re-run mcp oauth

What's next