From 3a5d03057aa35206e1fa793d4740d327c5ef23e0 Mon Sep 17 00:00:00 2001
From: GHX5T-SOL <200635707+GHX5T-SOL@users.noreply.github.com>
Date: Mon, 18 May 2026 15:05:42 +0200
Subject: [PATCH 1/3] Fix MCP exec permission display metadata
---
.../components/permissions/McpPermission.tsx | 23 ++++++++--
.../permissions/PermissionSelector.test.tsx | 42 +++++++++++++++++++
.../permissions/PermissionSelector.tsx | 4 +-
.../permissions/permission-handlers.test.ts | 27 ++++++++++++
.../claude/permissions/permission-handlers.ts | 1 +
5 files changed, 91 insertions(+), 6 deletions(-)
create mode 100644 apps/code/src/renderer/components/permissions/PermissionSelector.test.tsx
diff --git a/apps/code/src/renderer/components/permissions/McpPermission.tsx b/apps/code/src/renderer/components/permissions/McpPermission.tsx
index 375926665..0f272a83b 100644
--- a/apps/code/src/renderer/components/permissions/McpPermission.tsx
+++ b/apps/code/src/renderer/components/permissions/McpPermission.tsx
@@ -8,7 +8,24 @@ import {
import { formatInput } from "@features/sessions/components/session-update/toolCallUtils";
import { Box, Code } from "@radix-ui/themes";
import { DefaultPermission } from "./DefaultPermission";
-import { type BasePermissionProps, toSelectorOptions } from "./types";
+import {
+ type BasePermissionProps,
+ type PermissionToolCall,
+ toSelectorOptions,
+} from "./types";
+
+export function getMcpPermissionToolName(
+ toolCall: PermissionToolCall,
+): string | undefined {
+ const metaToolName = (
+ toolCall._meta as { claudeCode?: { toolName?: unknown } } | undefined
+ )?.claudeCode?.toolName;
+ if (typeof metaToolName === "string") return metaToolName;
+
+ const rawToolName = (toolCall.rawInput as { toolName?: unknown } | undefined)
+ ?.toolName;
+ return typeof rawToolName === "string" ? rawToolName : undefined;
+}
export function McpPermission({
toolCall,
@@ -16,9 +33,7 @@ export function McpPermission({
onSelect,
onCancel,
}: BasePermissionProps) {
- const mcpToolName = (
- toolCall._meta as { claudeCode?: { toolName?: string } } | undefined
- )?.claudeCode?.toolName;
+ const mcpToolName = getMcpPermissionToolName(toolCall);
if (!mcpToolName) {
return (
diff --git a/apps/code/src/renderer/components/permissions/PermissionSelector.test.tsx b/apps/code/src/renderer/components/permissions/PermissionSelector.test.tsx
new file mode 100644
index 000000000..ace911295
--- /dev/null
+++ b/apps/code/src/renderer/components/permissions/PermissionSelector.test.tsx
@@ -0,0 +1,42 @@
+import { Theme } from "@radix-ui/themes";
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import { PermissionSelector } from "./PermissionSelector";
+
+describe("PermissionSelector", () => {
+ it("renders MCP permissions from rawInput toolName when metadata is missing", () => {
+ render(
+
+
+ ,
+ );
+
+ expect(
+ screen.getByText(
+ (_, element) =>
+ element?.textContent === "posthog - Read execute-sql (MCP)",
+ ),
+ ).toBeInTheDocument();
+ expect(screen.queryByText(/^exec$/)).not.toBeInTheDocument();
+ });
+});
diff --git a/apps/code/src/renderer/components/permissions/PermissionSelector.tsx b/apps/code/src/renderer/components/permissions/PermissionSelector.tsx
index b89ad00d0..81d499e2b 100644
--- a/apps/code/src/renderer/components/permissions/PermissionSelector.tsx
+++ b/apps/code/src/renderer/components/permissions/PermissionSelector.tsx
@@ -4,7 +4,7 @@ import { DeletePermission } from "./DeletePermission";
import { EditPermission } from "./EditPermission";
import { ExecutePermission } from "./ExecutePermission";
import { FetchPermission } from "./FetchPermission";
-import { McpPermission } from "./McpPermission";
+import { getMcpPermissionToolName, McpPermission } from "./McpPermission";
import { MovePermission } from "./MovePermission";
import { QuestionPermission } from "./QuestionPermission";
import { ReadPermission } from "./ReadPermission";
@@ -34,7 +34,7 @@ export function PermissionSelector({
const meta = toolCall._meta as
| { codeToolKind?: string; claudeCode?: { toolName?: string } }
| undefined;
- const agentToolName = meta?.claudeCode?.toolName;
+ const agentToolName = getMcpPermissionToolName(toolCall);
if (agentToolName?.startsWith("mcp__")) {
return ;
}
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 15a3ee00b..dfcc425b1 100644
--- a/packages/agent/src/adapters/claude/permissions/permission-handlers.test.ts
+++ b/packages/agent/src/adapters/claude/permissions/permission-handlers.test.ts
@@ -74,6 +74,33 @@ describe("canUseTool MCP approval enforcement", () => {
expect.objectContaining({
toolCall: expect.objectContaining({
title: "The agent wants to call search_crm_objects (HubSpot)",
+ _meta: {
+ claudeCode: { toolName: "mcp__HubSpot__search_crm_objects" },
+ },
+ }),
+ }),
+ );
+ });
+
+ it("passes metadata through generic PostHog exec approval requests", async () => {
+ setMcpToolApprovalStates({
+ mcp__posthog__exec: "needs_approval",
+ });
+
+ const context = createContext("mcp__posthog__exec", {
+ toolInput: { command: "info execute-sql" },
+ });
+ const result = await canUseTool(context);
+
+ expect(result.behavior).toBe("allow");
+ expect(context.client.requestPermission).toHaveBeenCalledWith(
+ expect.objectContaining({
+ toolCall: expect.objectContaining({
+ rawInput: expect.objectContaining({
+ command: "info execute-sql",
+ toolName: "mcp__posthog__exec",
+ }),
+ _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 ec071bd93..6d1075b46 100644
--- a/packages/agent/src/adapters/claude/permissions/permission-handlers.ts
+++ b/packages/agent/src/adapters/claude/permissions/permission-handlers.ts
@@ -457,6 +457,7 @@ async function handleMcpApprovalFlow(
? [{ type: "content" as const, content: text(description) }]
: [],
rawInput: { ...(toolInput as Record), toolName },
+ _meta: { claudeCode: { toolName } },
},
});
From 113578d2f58e8d8ec9c3ff012b061891585a6d42 Mon Sep 17 00:00:00 2001
From: GHX5T-SOL <200635707+GHX5T-SOL@users.noreply.github.com>
Date: Mon, 18 May 2026 15:19:01 +0200
Subject: [PATCH 2/3] Simplify MCP permission selector metadata type
---
.../renderer/components/permissions/PermissionSelector.tsx | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/apps/code/src/renderer/components/permissions/PermissionSelector.tsx b/apps/code/src/renderer/components/permissions/PermissionSelector.tsx
index 81d499e2b..c571e6150 100644
--- a/apps/code/src/renderer/components/permissions/PermissionSelector.tsx
+++ b/apps/code/src/renderer/components/permissions/PermissionSelector.tsx
@@ -31,9 +31,7 @@ export function PermissionSelector({
onCancel,
}: PermissionSelectorProps) {
const props = { toolCall, options, onSelect, onCancel };
- const meta = toolCall._meta as
- | { codeToolKind?: string; claudeCode?: { toolName?: string } }
- | undefined;
+ const meta = toolCall._meta as { codeToolKind?: string } | undefined;
const agentToolName = getMcpPermissionToolName(toolCall);
if (agentToolName?.startsWith("mcp__")) {
return ;
From 9aa9e3befc31809b1238ddcdbf6531ef863ba5d9 Mon Sep 17 00:00:00 2001
From: JonathanLab
Date: Tue, 19 May 2026 16:31:59 +0200
Subject: [PATCH 3/3] refactor: move getMcpPermissionToolName to types; drop
rawInput fallback
Project policy (CLAUDE.md) bans reading from Claude Code SDK's rawInput
for metadata. The handler now writes _meta.claudeCode.toolName on every
MCP approval request, so the rawInput compatibility fallback is no
longer needed.
Generated-By: PostHog Code
Task-Id: c5674c04-c95c-4bfe-bfcf-248a921b4aae
---
.../components/permissions/McpPermission.tsx | 15 +--------------
.../permissions/PermissionSelector.test.tsx | 8 +++-----
.../components/permissions/PermissionSelector.tsx | 4 ++--
.../src/renderer/components/permissions/types.ts | 9 +++++++++
4 files changed, 15 insertions(+), 21 deletions(-)
diff --git a/apps/code/src/renderer/components/permissions/McpPermission.tsx b/apps/code/src/renderer/components/permissions/McpPermission.tsx
index 0f272a83b..0bc12d1e5 100644
--- a/apps/code/src/renderer/components/permissions/McpPermission.tsx
+++ b/apps/code/src/renderer/components/permissions/McpPermission.tsx
@@ -10,23 +10,10 @@ import { Box, Code } from "@radix-ui/themes";
import { DefaultPermission } from "./DefaultPermission";
import {
type BasePermissionProps,
- type PermissionToolCall,
+ getMcpPermissionToolName,
toSelectorOptions,
} from "./types";
-export function getMcpPermissionToolName(
- toolCall: PermissionToolCall,
-): string | undefined {
- const metaToolName = (
- toolCall._meta as { claudeCode?: { toolName?: unknown } } | undefined
- )?.claudeCode?.toolName;
- if (typeof metaToolName === "string") return metaToolName;
-
- const rawToolName = (toolCall.rawInput as { toolName?: unknown } | undefined)
- ?.toolName;
- return typeof rawToolName === "string" ? rawToolName : undefined;
-}
-
export function McpPermission({
toolCall,
options,
diff --git a/apps/code/src/renderer/components/permissions/PermissionSelector.test.tsx b/apps/code/src/renderer/components/permissions/PermissionSelector.test.tsx
index ace911295..7aabae858 100644
--- a/apps/code/src/renderer/components/permissions/PermissionSelector.test.tsx
+++ b/apps/code/src/renderer/components/permissions/PermissionSelector.test.tsx
@@ -4,7 +4,7 @@ import { describe, expect, it, vi } from "vitest";
import { PermissionSelector } from "./PermissionSelector";
describe("PermissionSelector", () => {
- it("renders MCP permissions from rawInput toolName when metadata is missing", () => {
+ it("renders MCP permissions using claudeCode.toolName metadata", () => {
render(
{
toolCallId: "tool-1",
title: "exec",
kind: "other",
- rawInput: {
- command: "info execute-sql",
- toolName: "mcp__posthog__exec",
- },
+ rawInput: { command: "info execute-sql" },
+ _meta: { claudeCode: { toolName: "mcp__posthog__exec" } },
}}
options={[
{ kind: "allow_once", optionId: "allow", name: "Yes" },
diff --git a/apps/code/src/renderer/components/permissions/PermissionSelector.tsx b/apps/code/src/renderer/components/permissions/PermissionSelector.tsx
index c571e6150..74954d9af 100644
--- a/apps/code/src/renderer/components/permissions/PermissionSelector.tsx
+++ b/apps/code/src/renderer/components/permissions/PermissionSelector.tsx
@@ -4,14 +4,14 @@ import { DeletePermission } from "./DeletePermission";
import { EditPermission } from "./EditPermission";
import { ExecutePermission } from "./ExecutePermission";
import { FetchPermission } from "./FetchPermission";
-import { getMcpPermissionToolName, McpPermission } from "./McpPermission";
+import { McpPermission } from "./McpPermission";
import { MovePermission } from "./MovePermission";
import { QuestionPermission } from "./QuestionPermission";
import { ReadPermission } from "./ReadPermission";
import { SearchPermission } from "./SearchPermission";
import { SwitchModePermission } from "./SwitchModePermission";
import { ThinkPermission } from "./ThinkPermission";
-import type { PermissionToolCall } from "./types";
+import { getMcpPermissionToolName, type PermissionToolCall } from "./types";
interface PermissionSelectorProps {
toolCall: PermissionToolCall;
diff --git a/apps/code/src/renderer/components/permissions/types.ts b/apps/code/src/renderer/components/permissions/types.ts
index ba7cd12c8..f09ec034b 100644
--- a/apps/code/src/renderer/components/permissions/types.ts
+++ b/apps/code/src/renderer/components/permissions/types.ts
@@ -22,6 +22,15 @@ export interface BasePermissionProps {
onCancel: () => void;
}
+export function getMcpPermissionToolName(
+ toolCall: PermissionToolCall,
+): string | undefined {
+ const toolName = (
+ toolCall._meta as { claudeCode?: { toolName?: unknown } } | undefined
+ )?.claudeCode?.toolName;
+ return typeof toolName === "string" ? toolName : undefined;
+}
+
export function toSelectorOptions(
options: PermissionOption[],
): SelectorOption[] {