diff --git a/plugins/codex/scripts/lib/app-server.mjs b/plugins/codex/scripts/lib/app-server.mjs index 72b30a76..272792a1 100644 --- a/plugins/codex/scripts/lib/app-server.mjs +++ b/plugins/codex/scripts/lib/app-server.mjs @@ -54,7 +54,7 @@ function createProtocolError(message, data) { return error; } -class AppServerClientBase { +export class AppServerClientBase { constructor(cwd, options = {}) { this.cwd = cwd; this.options = options; @@ -154,6 +154,20 @@ class AppServerClientBase { } handleServerRequest(message) { + // MCP servers (e.g. ChatGPT connectors surfaced as `codex_apps`) request the + // operator's consent via an elicitation, which app-server delivers as a + // server->client request. This client runs Codex non-interactively, so there + // is no human to answer it; blanket-rejecting every server request with + // -32601 makes those tool calls fail ("user rejected MCP tool call") on the + // background runner and hang on `codex exec` / `codex mcp-server`. Accept the + // elicitation so connectors the operator has already enabled can run. + if (message.method === "mcpServer/elicitation/request") { + this.sendMessage({ + id: message.id, + result: { action: "accept", content: null, _meta: null } + }); + return; + } this.sendMessage({ id: message.id, error: buildJsonRpcError(-32601, `Unsupported server request: ${message.method}`) diff --git a/tests/app-server.test.mjs b/tests/app-server.test.mjs new file mode 100644 index 00000000..54628426 --- /dev/null +++ b/tests/app-server.test.mjs @@ -0,0 +1,36 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; + +import { AppServerClientBase } from "../plugins/codex/scripts/lib/app-server.mjs"; + +/** Minimal client that records the JSON-RPC messages it would send. */ +class CapturingClient extends AppServerClientBase { + constructor() { + super(process.cwd()); + this.sent = []; + } + sendMessage(message) { + this.sent.push(message); + } +} + +test("handleServerRequest accepts MCP elicitation requests instead of rejecting them", () => { + const client = new CapturingClient(); + client.handleServerRequest({ + id: 7, + method: "mcpServer/elicitation/request", + params: { threadId: "t1" } + }); + assert.deepEqual(client.sent, [ + { id: 7, result: { action: "accept", content: null, _meta: null } } + ]); +}); + +test("handleServerRequest still rejects unknown server requests with -32601", () => { + const client = new CapturingClient(); + client.handleServerRequest({ id: 8, method: "some/unknown/request", params: {} }); + assert.equal(client.sent.length, 1); + assert.equal(client.sent[0].id, 8); + assert.equal(client.sent[0].result, undefined); + assert.equal(client.sent[0].error.code, -32601); +});