From 50424265762d08ad2480b9c68148f2acff0ea29e Mon Sep 17 00:00:00 2001 From: Georgiy Tarasov Date: Thu, 7 May 2026 09:35:54 +0200 Subject: [PATCH 1/2] feat(code): show PostHog exec payload in permission dialog The PostHog exec destructive flow was rendering only "The agent wants to run X on PostHog" with no payload because the agent didn't tag the permission request with `_meta.claudeCode.toolName`, so the renderer fell through to DefaultPermission. Setting that meta routes the dialog to McpPermission, which unwraps the `call ` command and shows the JSON args pretty-printed. Also moves the PostHog-specific exec-display helpers out of the generic mcp-apps feature into their own posthog-mcp feature, and adds a formatPosthogExecBody helper so the dialog body matches the chat row's unwrapped preview. --- .../components/permissions/McpPermission.tsx | 7 ++-- .../PermissionSelector.stories.tsx | 33 +++++++++++++++++++ .../mcp-apps/components/McpToolView.tsx | 8 ++--- .../utils/posthog-exec-display.test.ts | 29 ++++++++++++++++ .../utils/posthog-exec-display.ts | 20 +++++++++++ .../permissions/permission-handlers.test.ts | 1 + .../claude/permissions/permission-handlers.ts | 1 + 7 files changed, 93 insertions(+), 6 deletions(-) rename apps/code/src/renderer/features/{mcp-apps => posthog-mcp}/utils/posthog-exec-display.test.ts (86%) rename apps/code/src/renderer/features/{mcp-apps => posthog-mcp}/utils/posthog-exec-display.ts (87%) diff --git a/apps/code/src/renderer/components/permissions/McpPermission.tsx b/apps/code/src/renderer/components/permissions/McpPermission.tsx index 99dedb320..375926665 100644 --- a/apps/code/src/renderer/components/permissions/McpPermission.tsx +++ b/apps/code/src/renderer/components/permissions/McpPermission.tsx @@ -1,9 +1,10 @@ import { ActionSelector } from "@components/ActionSelector"; import { parseMcpToolKey } from "@features/mcp-apps/utils/mcp-app-host-utils"; import { + formatPosthogExecBody, getPostHogExecDisplay, isPostHogExecTool, -} from "@features/mcp-apps/utils/posthog-exec-display"; +} from "@features/posthog-mcp/utils/posthog-exec-display"; import { formatInput } from "@features/sessions/components/session-update/toolCallUtils"; import { Box, Code } from "@radix-ui/themes"; import { DefaultPermission } from "./DefaultPermission"; @@ -37,7 +38,9 @@ export function McpPermission({ : null; const serverName = posthogDisplay ? "posthog" : defaultServerName; const toolName = posthogDisplay?.label ?? defaultToolName; - const fullInput = formatInput(toolCall.rawInput); + const fullInput = posthogDisplay + ? formatPosthogExecBody(posthogDisplay.input) + : formatInput(toolCall.rawInput); return ( { }); }); }); + +describe("formatPosthogExecBody", () => { + it("returns undefined for empty input", () => { + expect(formatPosthogExecBody(undefined)).toBeUndefined(); + expect(formatPosthogExecBody("")).toBeUndefined(); + }); + + it("pretty-prints JSON object payloads", () => { + expect(formatPosthogExecBody('{"id":3}')).toBe('{\n "id": 3\n}'); + }); + + it("pretty-prints JSON array payloads", () => { + expect(formatPosthogExecBody('[1,2]')).toBe("[\n 1,\n 2\n]"); + }); + + it("returns non-JSON strings unchanged (e.g. search regex)", () => { + expect(formatPosthogExecBody("query-")).toBe("query-"); + }); + + it("returns malformed JSON unchanged", () => { + expect(formatPosthogExecBody('{"id":')).toBe('{"id":'); + }); + + it("returns JSON primitives unchanged (not pretty-printable)", () => { + expect(formatPosthogExecBody("42")).toBe("42"); + expect(formatPosthogExecBody('"hello"')).toBe('"hello"'); + }); +}); diff --git a/apps/code/src/renderer/features/mcp-apps/utils/posthog-exec-display.ts b/apps/code/src/renderer/features/posthog-mcp/utils/posthog-exec-display.ts similarity index 87% rename from apps/code/src/renderer/features/mcp-apps/utils/posthog-exec-display.ts rename to apps/code/src/renderer/features/posthog-mcp/utils/posthog-exec-display.ts index f77f1e3c2..7642bb7ed 100644 --- a/apps/code/src/renderer/features/mcp-apps/utils/posthog-exec-display.ts +++ b/apps/code/src/renderer/features/posthog-mcp/utils/posthog-exec-display.ts @@ -108,3 +108,23 @@ function readExplicitInput(value: unknown): string | undefined { return undefined; } } + +/** + * Pretty-prints the unwrapped exec args for display in the permission dialog + * body — JSON payloads (the `call` case) render multi-line; non-JSON args + * (e.g. a `search` regex) pass through unchanged. + */ +export function formatPosthogExecBody( + input: string | undefined, +): string | undefined { + if (!input) return undefined; + try { + const parsed = JSON.parse(input); + if (parsed && typeof parsed === "object") { + return JSON.stringify(parsed, null, 2); + } + } catch { + // not JSON — fall through and show raw + } + return input; +} diff --git a/packages/agent/src/adapters/claude/permissions/permission-handlers.test.ts b/packages/agent/src/adapters/claude/permissions/permission-handlers.test.ts index 5d138c2f1..15a3ee00b 100644 --- a/packages/agent/src/adapters/claude/permissions/permission-handlers.test.ts +++ b/packages/agent/src/adapters/claude/permissions/permission-handlers.test.ts @@ -237,6 +237,7 @@ describe("canUseTool MCP approval enforcement", () => { expect.objectContaining({ toolCall: expect.objectContaining({ title: "The agent wants to run `notebooks-destroy` on PostHog", + _meta: { claudeCode: { toolName: "mcp__posthog__exec" } }, }), }), ); diff --git a/packages/agent/src/adapters/claude/permissions/permission-handlers.ts b/packages/agent/src/adapters/claude/permissions/permission-handlers.ts index f8bb8d5d5..ec071bd93 100644 --- a/packages/agent/src/adapters/claude/permissions/permission-handlers.ts +++ b/packages/agent/src/adapters/claude/permissions/permission-handlers.ts @@ -528,6 +528,7 @@ async function handlePostHogExecApprovalFlow( }, ], rawInput: { ...(toolInput as Record), toolName }, + _meta: { claudeCode: { toolName } }, }, }); From 10f378d6f234a9f8a65687ac7f6ee207721ebfe1 Mon Sep 17 00:00:00 2001 From: Georgiy Tarasov Date: Thu, 7 May 2026 09:46:58 +0200 Subject: [PATCH 2/2] fix --- .../features/posthog-mcp/utils/posthog-exec-display.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/code/src/renderer/features/posthog-mcp/utils/posthog-exec-display.test.ts b/apps/code/src/renderer/features/posthog-mcp/utils/posthog-exec-display.test.ts index 7b62ca099..08aa1b6f7 100644 --- a/apps/code/src/renderer/features/posthog-mcp/utils/posthog-exec-display.test.ts +++ b/apps/code/src/renderer/features/posthog-mcp/utils/posthog-exec-display.test.ts @@ -212,7 +212,7 @@ describe("formatPosthogExecBody", () => { }); it("pretty-prints JSON array payloads", () => { - expect(formatPosthogExecBody('[1,2]')).toBe("[\n 1,\n 2\n]"); + expect(formatPosthogExecBody("[1,2]")).toBe("[\n 1,\n 2\n]"); }); it("returns non-JSON strings unchanged (e.g. search regex)", () => {