MCP servers passed via `session/new` don't work in ACP mode

, ,

Where does the bug appear (feature/product)?

Cursor CLI

Describe the Bug

MCP servers passed via session/new don’t work in ACP mode. The approval middleware blocks them silently, and there’s no ACP-compatible way to approve.

  • --approve-mcps is not wired to ACP (only affects the interactive CLI path)
  • --yolo/--force bypasses tool permissions but not MCP approval

Steps to Reproduce

You can pre-create the approval file. The key is ${name}-${sha256(JSON.stringify({path: cwd, server: {url}})).hex.slice(0, 16)}, written to ~/.cursor/projects/<slug>/mcp-approvals.json where slug is the cwd with non-alphanum replaced by -.

curl https://cursor.com/install -fsS | bash
npm install @agentclientprotocol/sdk

Run below as node connect-mcp-server.mjs:

import * as acp from "@agentclientprotocol/sdk";
import { spawn } from "node:child_process";
import { mkdirSync, writeFileSync, realpathSync } from "node:fs";
import { join } from "node:path";
import { tmpdir, homedir } from "node:os";
import { Readable, Writable } from "node:stream";
import { createHash } from "node:crypto";

const mcpUrl = "https://mcp.kiwi.com";
const mcpName = "kiwi";

const cwd = realpathSync(mkdirSync(join(tmpdir(), `cursor-mcp-${Date.now()}`), { recursive: true }) || join(tmpdir(), `cursor-mcp-${Date.now()}`));

// pre-approve the MCP server
const slug = cwd.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-|-$/g, "");
const hash = createHash("sha256").update(JSON.stringify({ path: cwd, server: { url: mcpUrl } })).digest("hex").substring(0, 16);
const projectDir = join(homedir(), ".cursor", "projects", slug);
mkdirSync(projectDir, { recursive: true });
writeFileSync(join(projectDir, "mcp-approvals.json"), JSON.stringify([`${mcpName}-${hash}`]));

const child = spawn("cursor-agent", ["acp"], { stdio: ["pipe", "pipe", "inherit"] });
const stream = acp.ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));

let toolCalled = false;
const client = new acp.ClientSideConnection(() => ({
  sessionUpdate: async (p) => { if (p.update.sessionUpdate === "tool_call" && p.update.title?.includes("kiwi")) toolCalled = true; },
  requestPermission: async (p) => ({ outcome: { outcome: "selected", optionId: p.options.find((o) => o.kind === "allow_once")?.optionId || p.options[0]?.optionId } }),
}), stream);

await client.initialize({ protocolVersion: acp.PROTOCOL_VERSION, clientCapabilities: { fs: { readTextFile: false, writeTextFile: false } }, clientInfo: { name: "test", version: "1.0.0" } });
const { sessionId } = await client.newSession({ cwd, mcpServers: [{ type: "http", name: mcpName, url: mcpUrl, headers: [] }] });
await client.prompt({ sessionId, prompt: [{ type: "text", text: "Use kiwi to search for flights from BKI to SYD tomorrow." }] });

console.log(`Kiwi tool called: ${toolCalled ? "YES" : "NO"}`);
child.kill(); process.exit(0);

With the pre-approval: Kiwi tool called: YES. Without it: agent says kiwi tools don’t exist.

Expected Behavior

Clients shouldn’t have to reverse-engineer the approval key format. Ideally either:

  • Wire --approve-mcps to ACP (it already works in interactive CLI)
  • Auto-approve servers the client explicitly passes in session/new
  • Have --yolo also bypass MCP approval, not just tool permissions

Operating System

MacOS

Version Information

2026.02.27-e7d2ef6

Additional Information

Does this stop you from using Cursor

Sometimes - I can sometimes use Cursor

Hey, thanks for the detailed report and the repro script. Super helpful.

This is a known gap. MCP servers passed via session/new in ACP mode aren’t properly wired into the approval flow. You’re right that --approve-mcps only works for the interactive CLI flow, and --yolo doesn’t cover MCP approval.

The team is aware of this.

For now, the pre-approval workaround you found is the only option, unfortunately. I also saw your related report about permission rejection not being reported to the client, and I flagged that too.

I’ll update here if there’s any news on this.

Hey @Adrian_Cole,

Update as promised. MCP servers passed via session/new in ACP mode aren’t blocked by the file-based approval step anymore. Since June, the ACP loader only applies team policy and the disabled list, so editor-provided servers can connect without pre-seeding mcp-approvals.json. I checked the current CLI build, and the approval middleware is gone from that path.

If you still have the pre-approval block in your script, you can drop it. If the kiwi tools still don’t show up on the latest CLI, let me know and I’ll take another look. Thanks again for the repro, it made this one easy to pin down.