diff --git a/apps/code/src/renderer/components/action-selector/ActionSelector.tsx b/apps/code/src/renderer/components/action-selector/ActionSelector.tsx
index bd039e457..21cb94f8e 100644
--- a/apps/code/src/renderer/components/action-selector/ActionSelector.tsx
+++ b/apps/code/src/renderer/components/action-selector/ActionSelector.tsx
@@ -228,11 +228,19 @@ export function ActionSelector({
/>
)}
- {title && (
-
- {compactHomePath(title)}
-
- )}
+ {title &&
+ (typeof title === "string" ? (
+
+ {compactHomePath(title)}
+
+ ) : (
+
+ {title}
+
+ ))}
{pendingAction && {pendingAction}}
diff --git a/apps/code/src/renderer/components/action-selector/types.ts b/apps/code/src/renderer/components/action-selector/types.ts
index 209a57c0e..b4b7acfcf 100644
--- a/apps/code/src/renderer/components/action-selector/types.ts
+++ b/apps/code/src/renderer/components/action-selector/types.ts
@@ -18,7 +18,7 @@ export interface StepAnswer {
}
export interface ActionSelectorProps {
- title: string;
+ title: ReactNode;
pendingAction?: ReactNode;
question: ReactNode;
options: SelectorOption[];
diff --git a/apps/code/src/renderer/components/permissions/McpPermission.tsx b/apps/code/src/renderer/components/permissions/McpPermission.tsx
new file mode 100644
index 000000000..99dedb320
--- /dev/null
+++ b/apps/code/src/renderer/components/permissions/McpPermission.tsx
@@ -0,0 +1,70 @@
+import { ActionSelector } from "@components/ActionSelector";
+import { parseMcpToolKey } from "@features/mcp-apps/utils/mcp-app-host-utils";
+import {
+ getPostHogExecDisplay,
+ isPostHogExecTool,
+} from "@features/mcp-apps/utils/posthog-exec-display";
+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";
+
+export function McpPermission({
+ toolCall,
+ options,
+ onSelect,
+ onCancel,
+}: BasePermissionProps) {
+ const mcpToolName = (
+ toolCall._meta as { claudeCode?: { toolName?: string } } | undefined
+ )?.claudeCode?.toolName;
+
+ if (!mcpToolName) {
+ return (
+
+ );
+ }
+
+ const { serverName: defaultServerName, toolName: defaultToolName } =
+ parseMcpToolKey(mcpToolName);
+ const posthogDisplay = isPostHogExecTool(mcpToolName)
+ ? getPostHogExecDisplay(toolCall.rawInput)
+ : null;
+ const serverName = posthogDisplay ? "posthog" : defaultServerName;
+ const toolName = posthogDisplay?.label ?? defaultToolName;
+ const fullInput = formatInput(toolCall.rawInput);
+
+ return (
+
+ {serverName}
+ {" - "}
+ {toolName}
+ {" (MCP)"}
+ >
+ }
+ pendingAction={
+ fullInput ? (
+
+
+ {fullInput}
+
+
+ ) : undefined
+ }
+ question="Do you want to proceed?"
+ options={toSelectorOptions(options)}
+ onSelect={onSelect}
+ onCancel={onCancel}
+ />
+ );
+}
diff --git a/apps/code/src/renderer/components/permissions/PermissionSelector.stories.tsx b/apps/code/src/renderer/components/permissions/PermissionSelector.stories.tsx
index fe3fc4cb8..2db161002 100644
--- a/apps/code/src/renderer/components/permissions/PermissionSelector.stories.tsx
+++ b/apps/code/src/renderer/components/permissions/PermissionSelector.stories.tsx
@@ -477,6 +477,57 @@ export const Default: Story = {
},
};
+function buildMcpToolCallData(
+ mcpToolName: string,
+ rawInput: Record,
+) {
+ return {
+ toolCallId: `story-${Date.now()}`,
+ title: mcpToolName.split("__").slice(2).join("__") || mcpToolName,
+ kind: "other" as const,
+ rawInput,
+ content: [],
+ _meta: { claudeCode: { toolName: mcpToolName } },
+ };
+}
+
+const posthogExecInput = {
+ command: 'call execute-sql {"query":"select 1"}',
+};
+export const McpPostHogExec: Story = {
+ args: {
+ toolCall: buildMcpToolCallData("mcp__posthog__exec", posthogExecInput),
+ options: buildPermissionOptions("mcp__posthog__exec", posthogExecInput),
+ },
+};
+
+const githubIssueInput = {
+ owner: "PostHog",
+ repo: "posthog",
+ title: "Investigate intermittent flake in foo test",
+ body: "Seen on CI runs 12345 and 67890 — appears related to fixture cleanup ordering.",
+ labels: ["bug", "ci"],
+};
+export const McpGithubCreateIssue: Story = {
+ args: {
+ toolCall: buildMcpToolCallData(
+ "mcp__github__create_issue",
+ githubIssueInput,
+ ),
+ options: buildPermissionOptions(
+ "mcp__github__create_issue",
+ githubIssueInput,
+ ),
+ },
+};
+
+export const McpNoArgs: Story = {
+ args: {
+ toolCall: buildMcpToolCallData("mcp__example__ping", {}),
+ options: buildPermissionOptions("mcp__example__ping", {}),
+ },
+};
+
const exitPlanModeInput = {
plan: `# Add Dark Mode Support
diff --git a/apps/code/src/renderer/components/permissions/PermissionSelector.tsx b/apps/code/src/renderer/components/permissions/PermissionSelector.tsx
index bd4f1de54..b89ad00d0 100644
--- a/apps/code/src/renderer/components/permissions/PermissionSelector.tsx
+++ b/apps/code/src/renderer/components/permissions/PermissionSelector.tsx
@@ -4,6 +4,7 @@ import { DeletePermission } from "./DeletePermission";
import { EditPermission } from "./EditPermission";
import { ExecutePermission } from "./ExecutePermission";
import { FetchPermission } from "./FetchPermission";
+import { McpPermission } from "./McpPermission";
import { MovePermission } from "./MovePermission";
import { QuestionPermission } from "./QuestionPermission";
import { ReadPermission } from "./ReadPermission";
@@ -30,9 +31,14 @@ export function PermissionSelector({
onCancel,
}: PermissionSelectorProps) {
const props = { toolCall, options, onSelect, onCancel };
- const codeToolKind = (toolCall._meta as { codeToolKind?: string } | undefined)
- ?.codeToolKind;
- const kind = codeToolKind ?? (toolCall.kind as string);
+ const meta = toolCall._meta as
+ | { codeToolKind?: string; claudeCode?: { toolName?: string } }
+ | undefined;
+ const agentToolName = meta?.claudeCode?.toolName;
+ if (agentToolName?.startsWith("mcp__")) {
+ return ;
+ }
+ const kind = meta?.codeToolKind ?? (toolCall.kind as string);
switch (kind) {
case "execute":