feat(mcp-store): agent request permission for tool calling#1784
feat(mcp-store): agent request permission for tool calling#1784cvolzer3 wants to merge 9 commits intofeat/refactor-mcp-storefrom
Conversation
MCP tools with approval_state "do_not_use" are now blocked with a message directing users to Settings > MCP Servers in PostHog Code. Tools with "needs_approval" trigger the standard permission dialog. Previously, the agent had no awareness of PostHog-managed approval states and Claude would incorrectly tell users to check "Claude Code settings." - Fetch tool approval states from PostHog API during session creation - Check approval state in canUseTool() before isToolAllowedForMode - Preserve approval state across MCP metadata cache updates - Skip read-only auto-approval for needs_approval tools - Add system prompt guidance to relay denial messages verbatim
The Claude Agent SDK sanitizes MCP server names (replacing non-alphanumeric characters with underscores) when building tool names like `mcp__<server>__<tool>`. Our approval key construction used the raw server name from the PostHog API, causing key mismatches for servers with spaces or special characters (e.g. HubSpot). This made `needs_approval` tools fall through to the default permission flow instead of being intercepted.
The permission dialog for needs_approval MCP tools now shows "The agent wants to call <tool> (<server>)" instead of just the raw tool name, giving users clear context about what's being requested.
…oval tool When a user approves a needs_approval tool in the permission dialog, PATCH the PostHog API to set approval_state to "approved" before the tool executes. Without this, the server-side proxy rejects the call even after local approval. Also fixes the system prompt instruction to stop Claude from directing users to Settings for every MCP tool error — only relay explicit denial messages.
Vite resolves @posthog/agent subpath imports by mapping to the source tree. The shorthand "mcp/tool-metadata" resolved to the wrong path. Use "adapters/claude/mcp/tool-metadata" to match the actual source location, consistent with other deep imports in the codebase.
|
fyi, this builds on the other mcp store pr: #1747 |
|
| approvalState?: McpToolApprovalState; | ||
| } | ||
|
|
||
| const mcpToolMetadataCache: Map<string, McpToolMetadata> = new Map(); |
There was a problem hiding this comment.
Module-level cache shared across all concurrent sessions
mcpToolMetadataCache is a module-level singleton. Each new session calls setMcpToolApprovalStates(meta.mcpToolApprovals) in claude-agent.ts, which mutates this shared map. If two sessions run concurrently for the same tool key (e.g. mcp__posthog__search), the second session's setMcpToolApprovalStates call overwrites the state set by the first — and canUseTool reads from this same global cache. A session that had "approved" could see "needs_approval" (or "do_not_use") after another session starts.
The per-session mcpToolApprovals field already exists on ManagedSession in service.ts, but the permission enforcement path in permission-handlers.ts bypasses it entirely. Approval states should either be keyed by session, or the cache should be keyed by (sessionId, toolKey) to prevent cross-session contamination.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/agent/src/adapters/claude/mcp/tool-metadata.ts
Line: 16
Comment:
**Module-level cache shared across all concurrent sessions**
`mcpToolMetadataCache` is a module-level singleton. Each new session calls `setMcpToolApprovalStates(meta.mcpToolApprovals)` in `claude-agent.ts`, which mutates this shared map. If two sessions run concurrently for the same tool key (e.g. `mcp__posthog__search`), the second session's `setMcpToolApprovalStates` call overwrites the state set by the first — and `canUseTool` reads from this same global cache. A session that had `"approved"` could see `"needs_approval"` (or `"do_not_use"`) after another session starts.
The per-session `mcpToolApprovals` field already exists on `ManagedSession` in `service.ts`, but the permission enforcement path in `permission-handlers.ts` bypasses it entirely. Approval states should either be keyed by session, or the cache should be keyed by `(sessionId, toolKey)` to prevent cross-session contamination.
How can I resolve this? If you propose a fix, please make it concise.
Problem
When the agent uses a blocked or approval-required MCP tool, Claude incorrectly tells users to check "Claude Code settings." PostHog Code owns MCP installations, and needs_approval tools should show an interactive approval dialog instead of a static denial.
Changes
do_not_useare denied and the user is directed to "Settings > MCP Servers";needs_approvaltools are shown permission dialog_metainto the tool metadata cacheneeds_approvaltool, PATCH the backend to setapprovedbefore execution, otherwise the server-side proxy still rejects the callneeds_approvaltoolsHow did you test this?