From 0fef66283a9de61de5c37e586e86f765bde9a48b Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Tue, 16 Jun 2026 20:53:23 +0800 Subject: [PATCH] Fix pending permission MCP listing (#1561) * Fix pending permission MCP responses * Tighten MCP permission sanitizer typing --- .../src/server/agent/mcp-server.test.ts | 61 +++++++++++++++++++ .../server/src/server/agent/mcp-server.ts | 2 +- .../server/src/server/agent/mcp-shared.ts | 10 +++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/packages/server/src/server/agent/mcp-server.test.ts b/packages/server/src/server/agent/mcp-server.test.ts index b60dbdb04..ee831db99 100644 --- a/packages/server/src/server/agent/mcp-server.test.ts +++ b/packages/server/src/server/agent/mcp-server.test.ts @@ -17,6 +17,7 @@ import type { AgentMode, AgentProvider, ProviderSnapshotEntry } from "./agent-sd import { createProviderSnapshotManagerStub } from "../test-utils/session-stubs.js"; import { AgentListItemPayloadSchema, + AgentPermissionRequestPayloadSchema, AgentSnapshotPayloadSchema, } from "@getpaseo/protocol/messages"; import type { PersistedProjectRecord, PersistedWorkspaceRecord } from "../workspace-registry.js"; @@ -3739,6 +3740,66 @@ describe("agent snapshot MCP serialization", () => { } }); + it("emits list_pending_permissions payloads that satisfy the declared output schema", async () => { + const { agentManager, agentStorage, spies } = createTestDeps(); + spies.agentManager.listAgents.mockReturnValue([ + createManagedAgent({ + id: "agent-with-permission", + provider: "codex", + pendingPermissions: new Map([ + [ + "perm-minimal", + { + id: "perm-minimal", + provider: "codex", + name: "request_user_input", + kind: "question", + title: "Need input", + input: { prompt: "Pick one" }, + detail: undefined, + metadata: { source: "test" }, + }, + ], + ]), + }), + ]); + + const server = await createAgentMcpServer({ + agentManager, + agentStorage, + logger, + providerSnapshotManager: createClaudeOnlyManager(), + }); + const tool = registeredTool(server, "list_pending_permissions"); + const response = await tool.handler({}); + + const permissions = z + .array( + z.object({ + agentId: z.string(), + status: z.string(), + request: AgentPermissionRequestPayloadSchema, + }), + ) + .parse(response.structuredContent.permissions); + expect(permissions).toEqual([ + { + agentId: "agent-with-permission", + status: "idle", + request: { + id: "perm-minimal", + provider: "codex", + name: "request_user_input", + kind: "question", + title: "Need input", + input: { prompt: "Pick one" }, + metadata: { source: "test" }, + }, + }, + ]); + expectOutputSchemaAccepts(tool, response.structuredContent); + }); + it("loads archived agents before reading get_agent_activity", async () => { const { agentManager, agentStorage, spies } = createTestDeps(); const record = createStoredRecord({ id: "archived-activity-agent" }); diff --git a/packages/server/src/server/agent/mcp-server.ts b/packages/server/src/server/agent/mcp-server.ts index 90f293628..976082698 100644 --- a/packages/server/src/server/agent/mcp-server.ts +++ b/packages/server/src/server/agent/mcp-server.ts @@ -2371,7 +2371,7 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom return payload.pendingPermissions.map((request) => ({ agentId: agent.id, status: payload.status, - request, + request: sanitizePermissionRequest(request), })); }); diff --git a/packages/server/src/server/agent/mcp-shared.ts b/packages/server/src/server/agent/mcp-shared.ts index db545fc06..f3c6e9fc8 100644 --- a/packages/server/src/server/agent/mcp-shared.ts +++ b/packages/server/src/server/agent/mcp-shared.ts @@ -158,6 +158,13 @@ export async function waitForAgentWithTimeout( } } +export function sanitizePermissionRequest( + permission: AgentPermissionRequest, +): AgentPermissionRequest; +export function sanitizePermissionRequest(permission: null | undefined): null; +export function sanitizePermissionRequest( + permission: AgentPermissionRequest | null | undefined, +): AgentPermissionRequest | null; export function sanitizePermissionRequest( permission: AgentPermissionRequest | null | undefined, ): AgentPermissionRequest | null { @@ -174,6 +181,9 @@ export function sanitizePermissionRequest( if (sanitized.input === undefined) { delete sanitized.input; } + if (sanitized.detail === undefined) { + delete sanitized.detail; + } if (sanitized.suggestions === undefined) { delete sanitized.suggestions; }