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.

This topic was automatically closed 22 days after the last reply. New replies are no longer allowed.