@wix/pathgrade 0.33.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ import type { ToolEvent } from '../../tool-events.js';
2
+ export interface ClaudeDeniedMcpEventStore {
3
+ record(toolUseId: string, event: ToolEvent): void;
4
+ get(toolUseId: string | undefined): ToolEvent | undefined;
5
+ all(): ToolEvent[];
6
+ clear(): void;
7
+ }
8
+ export declare function createClaudeDeniedMcpEventStore(): ClaudeDeniedMcpEventStore;
@@ -0,0 +1,17 @@
1
+ export function createClaudeDeniedMcpEventStore() {
2
+ const byToolUseId = new Map();
3
+ return {
4
+ record(toolUseId, event) {
5
+ byToolUseId.set(toolUseId, event);
6
+ },
7
+ get(toolUseId) {
8
+ return toolUseId ? byToolUseId.get(toolUseId) : undefined;
9
+ },
10
+ all() {
11
+ return [...byToolUseId.values()];
12
+ },
13
+ clear() {
14
+ byToolUseId.clear();
15
+ },
16
+ };
17
+ }
@@ -0,0 +1,12 @@
1
+ import type { McpPolicyDenialReason } from '../../sdk/mcp-safety.js';
2
+ export type ClaudeSdkMcpToolNameParseResult = {
3
+ kind: 'non_mcp';
4
+ } | {
5
+ kind: 'mcp';
6
+ server: string;
7
+ tool: string;
8
+ } | {
9
+ kind: 'unrecognized';
10
+ reason: Extract<McpPolicyDenialReason, 'unrecognized_mcp_tool_name'>;
11
+ };
12
+ export declare function parseClaudeSdkMcpToolName(providerToolName: string, configuredServerNames: readonly string[]): ClaudeSdkMcpToolNameParseResult;
@@ -0,0 +1,22 @@
1
+ const MCP_TOOL_PREFIX = 'mcp__';
2
+ export function parseClaudeSdkMcpToolName(providerToolName, configuredServerNames) {
3
+ if (!providerToolName.startsWith(MCP_TOOL_PREFIX))
4
+ return { kind: 'non_mcp' };
5
+ const body = providerToolName.slice(MCP_TOOL_PREFIX.length);
6
+ const matches = configuredServerNames
7
+ .filter((serverName) => {
8
+ if (serverName.length === 0)
9
+ return false;
10
+ const toolStart = `${serverName}__`;
11
+ return body.startsWith(toolStart) && body.length > toolStart.length;
12
+ })
13
+ .sort((a, b) => b.length - a.length);
14
+ if (matches.length !== 1) {
15
+ return { kind: 'unrecognized', reason: 'unrecognized_mcp_tool_name' };
16
+ }
17
+ const server = matches[0];
18
+ const tool = body.slice(server.length + 2);
19
+ if (!tool)
20
+ return { kind: 'unrecognized', reason: 'unrecognized_mcp_tool_name' };
21
+ return { kind: 'mcp', server, tool };
22
+ }
@@ -18,6 +18,7 @@
18
18
  import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk';
19
19
  import type { AgentTurnResult } from '../../types.js';
20
20
  import type { AskUserAnswerStore } from './ask-user-answer-store.js';
21
+ import type { ClaudeDeniedMcpEventStore } from './denied-mcp-event-store.js';
21
22
  export interface ProjectTurnInput {
22
23
  /** Buffered typed-message stream from one `query()` call. */
23
24
  messages: SDKMessage[];
@@ -34,6 +35,10 @@ export interface ProjectTurnInput {
34
35
  * `answerSource: 'unknown'` it stamped before the bridge existed.
35
36
  */
36
37
  answerStore?: AskUserAnswerStore;
38
+ /** Staged MCP server names from the Claude SDK mount boundary. */
39
+ mcpServerNames?: readonly string[];
40
+ /** Policy-denied MCP events recorded by the per-turn permission bridge. */
41
+ deniedMcpEvents?: ClaudeDeniedMcpEventStore;
37
42
  }
38
43
  export interface ProjectedTurn {
39
44
  result: AgentTurnResult;
@@ -22,6 +22,7 @@ const SDK_ERROR_SUBTYPES = [
22
22
  'error_max_structured_output_retries',
23
23
  ];
24
24
  import { TOOL_NAME_MAP, buildSummary, enrichSkillEvents } from '../../tool-events.js';
25
+ import { parseClaudeSdkMcpToolName } from './mcp-tool-name.js';
25
26
  export function projectSdkMessages(input) {
26
27
  let sessionId;
27
28
  let initSkills;
@@ -59,7 +60,9 @@ export function projectSdkMessages(input) {
59
60
  continue;
60
61
  }
61
62
  if (block.type === 'tool_use') {
62
- toolEvents.push(buildToolEvent(block, input.turnNumber, input.answerStore));
63
+ const event = buildToolEvent(block, input.turnNumber, input.answerStore, input.mcpServerNames ?? [], input.deniedMcpEvents);
64
+ if (event)
65
+ toolEvents.push(event);
63
66
  }
64
67
  }
65
68
  break;
@@ -109,7 +112,10 @@ export function projectSdkMessages(input) {
109
112
  const trimmedResult = resultText.trim();
110
113
  const visible = isError ? '' : (trimmedAssistant || trimmedResult);
111
114
  const rawOutput = resultText || assistantText;
112
- const enriched = enrichSkillEvents(toolEvents);
115
+ const enriched = enrichSkillEvents([
116
+ ...toolEvents,
117
+ ...(input.deniedMcpEvents?.all() ?? []),
118
+ ]);
113
119
  const finalToolEvents = prependSlashCommandSkillEvent(enriched, input.firstMessage, initSkills);
114
120
  const traceOutput = input.messages.map((m) => JSON.stringify(m)).join('\n');
115
121
  const result = {
@@ -161,12 +167,14 @@ function prependSlashCommandSkillEvent(events, firstMessage, initSkills) {
161
167
  ...events,
162
168
  ];
163
169
  }
164
- function buildToolEvent(block, turnNumber, answerStore) {
170
+ function buildToolEvent(block, turnNumber, answerStore, mcpServerNames, deniedMcpEvents) {
165
171
  const providerToolName = String(block.name || 'unknown');
166
172
  const rawInput = block.input ?? undefined;
167
173
  const toolUseId = typeof block.id === 'string' ? block.id : undefined;
168
- const mcpTool = parseClaudeSdkMcpToolName(providerToolName);
169
- if (mcpTool) {
174
+ const mcpTool = parseClaudeSdkMcpToolName(providerToolName, mcpServerNames);
175
+ if (mcpTool.kind === 'mcp') {
176
+ if (deniedMcpEvents?.get(toolUseId))
177
+ return undefined;
170
178
  const normalizedProviderToolName = `${mcpTool.server}.${mcpTool.tool}`;
171
179
  const args = {
172
180
  ...(rawInput ?? {}),
@@ -202,12 +210,6 @@ function buildToolEvent(block, turnNumber, answerStore) {
202
210
  rawSnippet,
203
211
  };
204
212
  }
205
- function parseClaudeSdkMcpToolName(providerToolName) {
206
- const match = providerToolName.match(/^mcp__([^_].*?)__(.+)$/);
207
- if (!match)
208
- return undefined;
209
- return { server: match[1], tool: match[2] };
210
- }
211
213
  /**
212
214
  * Boundary with the ask-user bridge.
213
215
  *
@@ -0,0 +1,18 @@
1
+ import type { CanUseTool } from '@anthropic-ai/claude-agent-sdk';
2
+ import type { AskBus } from '../../sdk/ask-bus/types.js';
3
+ import { type McpSafetyOptions } from '../../sdk/mcp-safety.js';
4
+ import type { AskUserAnswerStore } from './ask-user-answer-store.js';
5
+ import type { ClaudeDeniedMcpEventStore } from './denied-mcp-event-store.js';
6
+ export interface ClaudeToolPermissionBridgeDeps {
7
+ askBus: AskBus;
8
+ getTurnNumber: () => number;
9
+ answerStore: AskUserAnswerStore;
10
+ mcpServerNames: readonly string[];
11
+ mcpSafety?: McpSafetyOptions;
12
+ deniedMcpEvents?: ClaudeDeniedMcpEventStore;
13
+ }
14
+ export type ClaudeToolPermissionBridge = CanUseTool & {
15
+ lastError(): Error | null;
16
+ clearLastError(): void;
17
+ };
18
+ export declare function createClaudeToolPermissionBridge(deps: ClaudeToolPermissionBridgeDeps): ClaudeToolPermissionBridge;
@@ -0,0 +1,86 @@
1
+ import { decideMcpToolCall, redactMcpSecrets, } from '../../sdk/mcp-safety.js';
2
+ import { createAskUserBridge, } from './ask-user-bridge.js';
3
+ import { parseClaudeSdkMcpToolName } from './mcp-tool-name.js';
4
+ export function createClaudeToolPermissionBridge(deps) {
5
+ const askUserBridge = createAskUserBridge({
6
+ askBus: deps.askBus,
7
+ getTurnNumber: deps.getTurnNumber,
8
+ answerStore: deps.answerStore,
9
+ });
10
+ const canUseTool = async (toolName, input, options) => {
11
+ if (toolName === 'AskUserQuestion') {
12
+ return askUserBridge(toolName, input, options);
13
+ }
14
+ const runMode = deps.mcpSafety?.runMode ?? 'mock';
15
+ const parsed = parseClaudeSdkMcpToolName(toolName, deps.mcpServerNames);
16
+ if (parsed.kind === 'non_mcp') {
17
+ return allow(input);
18
+ }
19
+ if (runMode === 'mock') {
20
+ return allow(input);
21
+ }
22
+ if (parsed.kind === 'unrecognized') {
23
+ return {
24
+ behavior: 'deny',
25
+ message: `unrecognized Claude SDK MCP tool name: ${toolName}`,
26
+ };
27
+ }
28
+ const decision = decideMcpToolCall(deps.mcpSafety, {
29
+ serverName: parsed.server,
30
+ toolName: parsed.tool,
31
+ arguments: asRecord(input),
32
+ });
33
+ if (decision.action === 'allow')
34
+ return allow(input);
35
+ recordDeniedMcpEvent({
36
+ store: deps.deniedMcpEvents,
37
+ toolUseId: options.toolUseID,
38
+ serverName: parsed.server,
39
+ toolName: parsed.tool,
40
+ input: asRecord(input),
41
+ decision,
42
+ });
43
+ return { behavior: 'deny', message: decision.message };
44
+ };
45
+ const bridge = canUseTool;
46
+ bridge.lastError = () => askUserBridge.lastError();
47
+ bridge.clearLastError = () => askUserBridge.clearLastError();
48
+ return bridge;
49
+ }
50
+ function allow(input) {
51
+ return { behavior: 'allow', updatedInput: asRecord(input) };
52
+ }
53
+ function asRecord(value) {
54
+ return value && typeof value === 'object' && !Array.isArray(value)
55
+ ? value
56
+ : {};
57
+ }
58
+ function recordDeniedMcpEvent(opts) {
59
+ if (!opts.store)
60
+ return;
61
+ const providerToolName = `${opts.serverName}.${opts.toolName}`;
62
+ const args = redactMcpSecrets(opts.input);
63
+ const event = {
64
+ action: 'mcp_tool_call',
65
+ provider: 'claude',
66
+ providerToolName,
67
+ arguments: {
68
+ ...args,
69
+ server: opts.serverName,
70
+ tool: opts.toolName,
71
+ status: 'policy_denied',
72
+ policyResult: {
73
+ action: 'deny',
74
+ reason: opts.decision.reason,
75
+ message: opts.decision.message,
76
+ },
77
+ },
78
+ summary: `MCP tool ${providerToolName} policy_denied`,
79
+ confidence: 'high',
80
+ rawSnippet: JSON.stringify(redactMcpSecrets({
81
+ toolUseID: opts.toolUseId,
82
+ input: opts.input,
83
+ })).slice(0, 200),
84
+ };
85
+ opts.store.record(opts.toolUseId, event);
86
+ }
@@ -24,11 +24,12 @@
24
24
  import { query as sdkQuery, } from '@anthropic-ai/claude-agent-sdk';
25
25
  import { BaseAgent, getRuntimeEnv, getWorkspacePath, } from '../types.js';
26
26
  import { createSandboxedClaudeSpawn } from '../providers/sandboxed-claude-spawn.js';
27
- import { assertStdioMcpServersStartForClaudeSdk, mountMcpForClaudeSdk, } from '../providers/mcp-runtime-mounting.js';
27
+ import { assertClaudeLiveMcpSafetyPreflight, assertStdioMcpServersStartForClaudeSdk, mountMcpForClaudeSdk, } from '../providers/mcp-runtime-mounting.js';
28
28
  import { buildClaudeSdkOptions, resolveClaudeCodeExecutable, } from './claude/sdk-options.js';
29
29
  import { projectSdkMessages } from './claude/sdk-message-projector.js';
30
- import { createAskUserBridge } from './claude/ask-user-bridge.js';
31
30
  import { createAskUserAnswerStore } from './claude/ask-user-answer-store.js';
31
+ import { createClaudeToolPermissionBridge } from './claude/tool-permission-bridge.js';
32
+ import { createClaudeDeniedMcpEventStore } from './claude/denied-mcp-event-store.js';
32
33
  import { requireAskBusForLiveBatches } from '../sdk/ask-bus/bus.js';
33
34
  function createLinkedAbortController(signal) {
34
35
  const controller = new AbortController();
@@ -76,6 +77,10 @@ export class ClaudeAgent extends BaseAgent {
76
77
  mcpConfigPath: sessionOptions?.mcpConfigPath,
77
78
  runtimeEnv: getRuntimeEnv(runtime),
78
79
  };
80
+ await assertClaudeLiveMcpSafetyPreflight({
81
+ ...mcpMountOptions,
82
+ mcpSafety: sessionOptions?.mcpSafety,
83
+ });
79
84
  await assertStdioMcpServersStartForClaudeSdk(mcpMountOptions);
80
85
  const mcpServers = await mountMcpForClaudeSdk(mcpMountOptions);
81
86
  let priorSessionId;
@@ -87,14 +92,24 @@ export class ClaudeAgent extends BaseAgent {
87
92
  // turn 2 cannot read a stale answer from turn 1 even on toolUseID
88
93
  // collisions.
89
94
  let answerStore = createAskUserAnswerStore();
90
- const bridge = createAskUserBridge({
95
+ let deniedMcpEvents = createClaudeDeniedMcpEventStore();
96
+ const bridge = createClaudeToolPermissionBridge({
91
97
  askBus,
92
98
  getTurnNumber: () => turnNumber,
93
99
  answerStore: { record: (id, e) => answerStore.record(id, e), get: (id) => answerStore.get(id) },
100
+ mcpServerNames: mcpServers ? Object.keys(mcpServers) : [],
101
+ mcpSafety: sessionOptions?.mcpSafety,
102
+ deniedMcpEvents: {
103
+ record: (id, event) => deniedMcpEvents.record(id, event),
104
+ get: (id) => deniedMcpEvents.get(id),
105
+ all: () => deniedMcpEvents.all(),
106
+ clear: () => deniedMcpEvents.clear(),
107
+ },
94
108
  });
95
109
  const runTurn = async (message) => {
96
110
  turnNumber += 1;
97
111
  answerStore = createAskUserAnswerStore();
112
+ deniedMcpEvents = createClaudeDeniedMcpEventStore();
98
113
  // Clear any ask-bus rejection captured on a prior turn so a
99
114
  // stale error never causes a spurious result on this turn.
100
115
  bridge.clearLastError();
@@ -128,6 +143,8 @@ export class ClaudeAgent extends BaseAgent {
128
143
  turnNumber,
129
144
  firstMessage: projectorFirstMessage,
130
145
  answerStore,
146
+ mcpServerNames: mcpServers ? Object.keys(mcpServers) : [],
147
+ deniedMcpEvents,
131
148
  });
132
149
  // Capture the SDK-reported session id BEFORE checking for a bus
133
150
  // rejection so the next turn's `Options.resume` points at this
@@ -2,7 +2,7 @@ import { BaseAgent, getWorkspacePath } from '../types.js';
2
2
  import { TOOL_NAME_MAP, buildSummary, enrichSkillEvents } from '../tool-events.js';
3
3
  import { getVisibleAssistantMessage } from '../sdk/visible-turn.js';
4
4
  import { prependRuntimePolicies } from '../sdk/runtime-policy.js';
5
- import { mountMcpForCursor } from '../providers/mcp-runtime-mounting.js';
5
+ import { assertUnsupportedLiveMcpSafetyRuntime, mountMcpForCursor, } from '../providers/mcp-runtime-mounting.js';
6
6
  /**
7
7
  * Cursor tool_call discriminants observed in the discovery spike. Probed in
8
8
  * order — first present key wins. Explicit probing (vs. `Object.keys(…)[0]`)
@@ -170,6 +170,12 @@ export class CursorAgent extends BaseAgent {
170
170
  return getVisibleAssistantMessage(result);
171
171
  }
172
172
  async runTurn(instruction, workspacePath, runCommand, sessionId, options) {
173
+ await assertUnsupportedLiveMcpSafetyRuntime({
174
+ runtimeName: 'Cursor',
175
+ workspacePath,
176
+ mcpConfigPath: options?.mcpConfigPath,
177
+ mcpSafety: options?.mcpSafety,
178
+ });
173
179
  // Runtime policies are injected into the first-turn prompt only.
174
180
  // On resumed turns (sessionId set) the policy was already delivered,
175
181
  // so we skip re-prepending. The Claude SDK driver does not prepend
@@ -1,4 +1,5 @@
1
1
  import type { McpStdioEntry } from './mcp-config.js';
2
+ import type { McpSafetyOptions } from '../sdk/mcp-safety.js';
2
3
  export type ClaudeSdkMcpServerEntry = McpStdioEntry | {
3
4
  type: 'http';
4
5
  url: string;
@@ -25,9 +26,18 @@ interface WorkspaceMcpRuntimeMountingOptions extends McpRuntimeMountingOptions {
25
26
  workspacePath: string;
26
27
  runtimeEnv?: Record<string, string>;
27
28
  }
29
+ interface ClaudeLiveMcpSafetyPreflightOptions extends WorkspaceMcpRuntimeMountingOptions {
30
+ mcpSafety?: McpSafetyOptions;
31
+ }
32
+ interface UnsupportedLiveMcpSafetyRuntimeOptions extends WorkspaceMcpRuntimeMountingOptions {
33
+ runtimeName: string;
34
+ mcpSafety?: McpSafetyOptions;
35
+ }
28
36
  export interface CursorMcpRuntimeMount {
29
37
  approveMcps: boolean;
30
38
  }
39
+ export declare function assertClaudeLiveMcpSafetyPreflight(options: ClaudeLiveMcpSafetyPreflightOptions): Promise<void>;
40
+ export declare function assertUnsupportedLiveMcpSafetyRuntime(options: UnsupportedLiveMcpSafetyRuntimeOptions): Promise<void>;
31
41
  export declare function assertStdioMcpServersStartForClaudeSdk(options: WorkspaceMcpRuntimeMountingOptions): Promise<void>;
32
42
  export declare function mountMcpForClaudeSdk(options: WorkspaceMcpRuntimeMountingOptions): Promise<McpServersObject | undefined>;
33
43
  export declare function mountMcpForCodexAppServer(options: WorkspaceMcpRuntimeMountingOptions): Promise<CodexAppServerMcpConfig | undefined>;
@@ -33,6 +33,23 @@ function toCodexAppServerMcpServerEntry(entry) {
33
33
  tool_timeout_sec: entry.tool_timeout_sec,
34
34
  });
35
35
  }
36
+ function isLiveMcpSafetyMode(options) {
37
+ const runMode = options?.runMode ?? 'mock';
38
+ return runMode === 'live-readonly' || runMode === 'live-sandbox' || runMode === 'live';
39
+ }
40
+ function describeClaudeIncompatibleFields(serverName, entry) {
41
+ if (isStdioEntry(entry))
42
+ return [];
43
+ const fields = [];
44
+ if (entry.env_http_headers !== undefined)
45
+ fields.push('env_http_headers');
46
+ if (entry.bearer_token_env_var !== undefined)
47
+ fields.push('bearer_token_env_var');
48
+ if (entry.headers !== undefined && entry.http_headers !== undefined) {
49
+ fields.push('headers and http_headers');
50
+ }
51
+ return fields.map((field) => `${serverName}: ${field}`);
52
+ }
36
53
  function formatMcpStartupError(serverName, error, stderr) {
37
54
  const message = error instanceof Error ? error.message : String(error);
38
55
  const stderrSuffix = stderr.trim() ? `\nstderr:\n${stderr.trim()}` : '';
@@ -67,6 +84,28 @@ async function assertStdioMcpServerStarts(workspacePath, serverName, entry, runt
67
84
  await transport.close().catch(() => undefined);
68
85
  }
69
86
  }
87
+ export async function assertClaudeLiveMcpSafetyPreflight(options) {
88
+ if (!options.mcpConfigPath || !isLiveMcpSafetyMode(options.mcpSafety))
89
+ return;
90
+ const mcpServers = await readStagedMcpServers(options.workspacePath, options.mcpConfigPath);
91
+ if (!mcpServers)
92
+ return;
93
+ if (options.mcpSafety?.liveOptIn !== true) {
94
+ throw new Error('Claude live MCP safety requires mcpSafety.liveOptIn: true before MCP runtime mounting.');
95
+ }
96
+ const unsupported = Object.entries(mcpServers).flatMap(([serverName, entry]) => describeClaudeIncompatibleFields(serverName, entry));
97
+ if (unsupported.length > 0) {
98
+ throw new Error(`Claude live MCP safety does not support runtime-incompatible MCP config fields: ${unsupported.join('; ')}`);
99
+ }
100
+ }
101
+ export async function assertUnsupportedLiveMcpSafetyRuntime(options) {
102
+ if (!options.mcpConfigPath || !isLiveMcpSafetyMode(options.mcpSafety))
103
+ return;
104
+ const mcpServers = await readStagedMcpServers(options.workspacePath, options.mcpConfigPath);
105
+ if (!mcpServers)
106
+ return;
107
+ throw new Error(`${options.runtimeName} live MCP safety enforcement is not supported. Use mock mode or a runtime that enforces mcpSafety before live MCP mounting.`);
108
+ }
70
109
  export async function assertStdioMcpServersStartForClaudeSdk(options) {
71
110
  const mcpServers = await readStagedMcpServers(options.workspacePath, options.mcpConfigPath);
72
111
  if (!mcpServers)
@@ -29,7 +29,8 @@ export async function createSandbox(spec) {
29
29
  const skillName = path.basename(skillPath);
30
30
  const skillFilter = (src) => {
31
31
  const rel = path.relative(skillPath, src);
32
- if (rel === 'test' || rel.startsWith(`test${path.sep}`)) {
32
+ const [topLevelDir] = rel.split(path.sep);
33
+ if (topLevelDir === 'test' || topLevelDir === 'tests') {
33
34
  return false;
34
35
  }
35
36
  return copyFilter(src);
@@ -18,7 +18,7 @@ export interface McpToolCallRequest {
18
18
  toolName: string;
19
19
  arguments?: Record<string, unknown>;
20
20
  }
21
- export type McpPolicyDenialReason = 'missing_live_opt_in' | 'missing_mcp_tool_policy' | 'denylisted' | 'not_allowlisted' | 'not_marked_readonly';
21
+ export type McpPolicyDenialReason = 'missing_live_opt_in' | 'missing_mcp_tool_policy' | 'denylisted' | 'not_allowlisted' | 'not_marked_readonly' | 'unrecognized_mcp_tool_name';
22
22
  export type McpToolPolicyDecision = {
23
23
  action: 'allow';
24
24
  } | {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/pathgrade",
3
- "version": "0.33.0",
3
+ "version": "0.34.0",
4
4
  "description": "Evaluate whether AI agents discover and use your skills correctly",
5
5
  "main": "./dist/sdk/index.js",
6
6
  "types": "./dist/sdk/index.d.ts",
@@ -80,7 +80,6 @@
80
80
  "vitest": "^4.0.0"
81
81
  },
82
82
  "devDependencies": {
83
- "@modelcontextprotocol/sdk": "1.29.0",
84
83
  "@types/fs-extra": "^11.0.4",
85
84
  "@types/picomatch": "^4.0.2",
86
85
  "@vitest/coverage-v8": "^4.0.18",
@@ -90,6 +89,7 @@
90
89
  },
91
90
  "dependencies": {
92
91
  "@anthropic-ai/claude-agent-sdk": "0.2.85",
92
+ "@modelcontextprotocol/sdk": "1.29.0",
93
93
  "@types/node": "^25.3.1",
94
94
  "fs-extra": "^11.3.3",
95
95
  "jiti": "^2.6.1",
@@ -97,5 +97,5 @@
97
97
  "typescript": "^5.9.3",
98
98
  "zod": "4.3.6"
99
99
  },
100
- "falconPackageHash": "579fcca96c4242bf10bf90e92a5dd56295670fa81ccd17a0b2ca1bb7"
100
+ "falconPackageHash": "77ac8633dc5bff7dea81e34b2bad4228699443f0788ce2eb56a2bc36"
101
101
  }