@agent-inspect/mcp 6.29.2 → 6.29.3

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.
package/README.md CHANGED
@@ -38,6 +38,8 @@ await inspectRun("my-agent", async () => {
38
38
 
39
39
  Call wrapped MCP operations **inside** `inspectRun` (or an active inspector run). Outside an active run, `step()` executes without instrumentation.
40
40
 
41
+ `wrapMcpClient` returns a Proxy over the original client so prototype methods (`connect`, `close`, `listResources`, …) and private SDK state remain available. Do not discard the original client reference solely to work around missing methods.
42
+
41
43
  ## Privacy
42
44
 
43
45
  - Local JSONL only; no MCP traffic sent to AgentInspect cloud (there is none)
package/dist/index.cjs CHANGED
@@ -51,28 +51,25 @@ function baseMetadata(options, overrides = {}) {
51
51
  }
52
52
  function wrapMcpClient(client, options = {}) {
53
53
  const maxSummaryLength = options.maxSummaryLength ?? 240;
54
- const wrapped = { ...client };
55
- if (typeof client.listTools === "function") {
56
- const listTools = client.listTools.bind(client);
57
- wrapped.listTools = async (params) => agentInspect.step(
58
- "mcp:tools/list",
59
- async () => {
60
- const result = await listTools(params);
61
- return result;
62
- },
63
- {
64
- type: "tool",
65
- metadata: baseMetadata(options, {
66
- toolName: "tools/list",
67
- toolCallId: nextToolCallId(options.toolCallIdPrefix)
68
- })
69
- }
70
- );
71
- }
72
- const callTool = client.callTool.bind(client);
73
- wrapped.callTool = async (params) => agentInspect.step(
54
+ const listToolsImpl = typeof client.listTools === "function" ? client.listTools.bind(client) : void 0;
55
+ const callToolImpl = client.callTool.bind(client);
56
+ const wrappedListTools = listToolsImpl ? async (params) => agentInspect.step(
57
+ "mcp:tools/list",
58
+ async () => {
59
+ const result = await listToolsImpl(params);
60
+ return result;
61
+ },
62
+ {
63
+ type: "tool",
64
+ metadata: baseMetadata(options, {
65
+ toolName: "tools/list",
66
+ toolCallId: nextToolCallId(options.toolCallIdPrefix)
67
+ })
68
+ }
69
+ ) : void 0;
70
+ const wrappedCallTool = async (params) => agentInspect.step(
74
71
  `mcp:${params.name}`,
75
- async () => callTool(params),
72
+ async () => callToolImpl(params),
76
73
  {
77
74
  type: "tool",
78
75
  metadata: baseMetadata(options, {
@@ -85,7 +82,21 @@ function wrapMcpClient(client, options = {}) {
85
82
  })
86
83
  }
87
84
  );
88
- return wrapped;
85
+ return new Proxy(client, {
86
+ get(target, property, receiver) {
87
+ if (property === "listTools" && wrappedListTools !== void 0) {
88
+ return wrappedListTools;
89
+ }
90
+ if (property === "callTool") {
91
+ return wrappedCallTool;
92
+ }
93
+ const value = Reflect.get(target, property, receiver);
94
+ if (typeof value === "function") {
95
+ return value.bind(target);
96
+ }
97
+ return value;
98
+ }
99
+ });
89
100
  }
90
101
  function resetMcpToolCallIdsForTests() {
91
102
  toolCallCounter = 0;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/summarize.ts","../src/wrap.ts"],"names":["step"],"mappings":"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","file":"index.cjs","sourcesContent":["const DEFAULT_MAX_SUMMARY_LENGTH = 240;\n\nfunction fallbackString(value: unknown): string {\n try {\n return String(value);\n } catch {\n return \"[unserializable]\";\n }\n}\n\nexport function summarizeMcpValue(\n value: unknown,\n maxLength = DEFAULT_MAX_SUMMARY_LENGTH,\n): string {\n let text: string;\n try {\n if (typeof value === \"string\") {\n text = value;\n } else {\n // JSON.stringify returns undefined (not a string) for undefined,\n // functions, and symbols; falling through with undefined would crash\n // instrumentation at text.length below.\n const serialized = JSON.stringify(value);\n text = serialized === undefined ? fallbackString(value) : serialized;\n }\n } catch {\n text = fallbackString(value);\n }\n if (text.length <= maxLength) return text;\n return `${text.slice(0, Math.max(0, maxLength - 3))}...`;\n}\n\nexport function hashServerUrl(url: string): string {\n let hash = 0;\n for (let i = 0; i < url.length; i += 1) {\n hash = (hash * 31 + url.charCodeAt(i)) >>> 0;\n }\n return hash.toString(16).padStart(8, \"0\");\n}\n","import { step } from \"agent-inspect\";\n\nimport { hashServerUrl, summarizeMcpValue } from \"./summarize.js\";\nimport type {\n McpClientLike,\n McpClientTracerOptions,\n McpTraceMetadata,\n} from \"./types.js\";\n\nlet toolCallCounter = 0;\n\nfunction nextToolCallId(prefix?: string): string {\n toolCallCounter += 1;\n const base = prefix?.trim() || \"mcp\";\n return `${base}-${toolCallCounter}`;\n}\n\nfunction baseMetadata(\n options: McpClientTracerOptions,\n overrides: Partial<McpTraceMetadata> = {},\n): Record<string, unknown> {\n const metadata: McpTraceMetadata = {\n source: { type: \"mcp-client\" },\n ...(options.serverName ? { mcpServerName: options.serverName } : {}),\n ...(options.serverUrl\n ? { mcpServerUrlHash: hashServerUrl(options.serverUrl) }\n : {}),\n ...(options.sessionId ? { sessionId: options.sessionId } : {}),\n ...overrides,\n };\n return { ...(options.metadata ?? {}), ...metadata };\n}\n\n/**\n * Wraps an MCP client so tools/list and tools/call emit local tool steps.\n * Telemetry only — does not proxy network behavior beyond the wrapped client.\n */\nexport function wrapMcpClient<T extends McpClientLike>(\n client: T,\n options: McpClientTracerOptions = {},\n): T {\n const maxSummaryLength = options.maxSummaryLength ?? 240;\n const wrapped = { ...client } as T;\n\n if (typeof client.listTools === \"function\") {\n const listTools = client.listTools.bind(client);\n wrapped.listTools = async (params?: unknown) =>\n step(\n \"mcp:tools/list\",\n async () => {\n const result = await listTools(params);\n return result;\n },\n {\n type: \"tool\",\n metadata: baseMetadata(options, {\n toolName: \"tools/list\",\n toolCallId: nextToolCallId(options.toolCallIdPrefix),\n }),\n },\n );\n }\n\n const callTool = client.callTool.bind(client);\n wrapped.callTool = async (params) =>\n step(\n `mcp:${params.name}`,\n async () => callTool(params),\n {\n type: \"tool\",\n metadata: baseMetadata(options, {\n toolName: params.name,\n toolCallId: nextToolCallId(options.toolCallIdPrefix),\n argumentSummary: summarizeMcpValue(\n params.arguments ?? {},\n maxSummaryLength,\n ),\n }),\n },\n );\n\n return wrapped;\n}\n\n/** @internal resets tool call ids for deterministic tests */\nexport function resetMcpToolCallIdsForTests(): void {\n toolCallCounter = 0;\n}\n"]}
1
+ {"version":3,"sources":["../src/summarize.ts","../src/wrap.ts"],"names":["step"],"mappings":";;;;;AAAA,IAAM,0BAAA,GAA6B,GAAA;AAEnC,SAAS,eAAe,KAAA,EAAwB;AAC9C,EAAA,IAAI;AACF,IAAA,OAAO,OAAO,KAAK,CAAA;AAAA,EACrB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,kBAAA;AAAA,EACT;AACF;AAEO,SAAS,iBAAA,CACd,KAAA,EACA,SAAA,GAAY,0BAAA,EACJ;AACR,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI;AACF,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,MAAA,IAAA,GAAO,KAAA;AAAA,IACT,CAAA,MAAO;AAIL,MAAA,MAAM,UAAA,GAAa,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA;AACvC,MAAA,IAAA,GAAO,UAAA,KAAe,KAAA,CAAA,GAAY,cAAA,CAAe,KAAK,CAAA,GAAI,UAAA;AAAA,IAC5D;AAAA,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,IAAA,GAAO,eAAe,KAAK,CAAA;AAAA,EAC7B;AACA,EAAA,IAAI,IAAA,CAAK,MAAA,IAAU,SAAA,EAAW,OAAO,IAAA;AACrC,EAAA,OAAO,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,EAAG,SAAA,GAAY,CAAC,CAAC,CAAC,CAAA,GAAA,CAAA;AACrD;AAEO,SAAS,cAAc,GAAA,EAAqB;AACjD,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,GAAA,CAAI,MAAA,EAAQ,KAAK,CAAA,EAAG;AACtC,IAAA,IAAA,GAAQ,IAAA,GAAO,EAAA,GAAK,GAAA,CAAI,UAAA,CAAW,CAAC,CAAA,KAAO,CAAA;AAAA,EAC7C;AACA,EAAA,OAAO,KAAK,QAAA,CAAS,EAAE,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AAC1C;AC7BA,IAAI,eAAA,GAAkB,CAAA;AAEtB,SAAS,eAAe,MAAA,EAAyB;AAC/C,EAAA,eAAA,IAAmB,CAAA;AACnB,EAAA,MAAM,IAAA,GAAO,MAAA,EAAQ,IAAA,EAAK,IAAK,KAAA;AAC/B,EAAA,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,eAAe,CAAA,CAAA;AACnC;AAEA,SAAS,YAAA,CACP,OAAA,EACA,SAAA,GAAuC,EAAC,EACf;AACzB,EAAA,MAAM,QAAA,GAA6B;AAAA,IACjC,MAAA,EAAQ,EAAE,IAAA,EAAM,YAAA,EAAa;AAAA,IAC7B,GAAI,QAAQ,UAAA,GAAa,EAAE,eAAe,OAAA,CAAQ,UAAA,KAAe,EAAC;AAAA,IAClE,GAAI,OAAA,CAAQ,SAAA,GACR,EAAE,gBAAA,EAAkB,cAAc,OAAA,CAAQ,SAAS,CAAA,EAAE,GACrD,EAAC;AAAA,IACL,GAAI,QAAQ,SAAA,GAAY,EAAE,WAAW,OAAA,CAAQ,SAAA,KAAc,EAAC;AAAA,IAC5D,GAAG;AAAA,GACL;AACA,EAAA,OAAO,EAAE,GAAI,OAAA,CAAQ,YAAY,EAAC,EAAI,GAAG,QAAA,EAAS;AACpD;AAUO,SAAS,aAAA,CACd,MAAA,EACA,OAAA,GAAkC,EAAC,EAChC;AACH,EAAA,MAAM,gBAAA,GAAmB,QAAQ,gBAAA,IAAoB,GAAA;AAErD,EAAA,MAAM,aAAA,GACJ,OAAO,MAAA,CAAO,SAAA,KAAc,aACxB,MAAA,CAAO,SAAA,CAAU,IAAA,CAAK,MAAM,CAAA,GAC5B,MAAA;AACN,EAAA,MAAM,YAAA,GAAe,MAAA,CAAO,QAAA,CAAS,IAAA,CAAK,MAAM,CAAA;AAEhD,EAAA,MAAM,gBAAA,GAAmB,aAAA,GACrB,OAAO,MAAA,KACLA,iBAAA;AAAA,IACE,gBAAA;AAAA,IACA,YAAY;AACV,MAAA,MAAM,MAAA,GAAS,MAAM,aAAA,CAAc,MAAM,CAAA;AACzC,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAAA,IACA;AAAA,MACE,IAAA,EAAM,MAAA;AAAA,MACN,QAAA,EAAU,aAAa,OAAA,EAAS;AAAA,QAC9B,QAAA,EAAU,YAAA;AAAA,QACV,UAAA,EAAY,cAAA,CAAe,OAAA,CAAQ,gBAAgB;AAAA,OACpD;AAAA;AACH,GACF,GACF,MAAA;AAEJ,EAAA,MAAM,eAAA,GAAkB,OACtB,MAAA,KAEAA,iBAAA;AAAA,IACE,CAAA,IAAA,EAAO,OAAO,IAAI,CAAA,CAAA;AAAA,IAClB,YAAY,aAAa,MAAM,CAAA;AAAA,IAC/B;AAAA,MACE,IAAA,EAAM,MAAA;AAAA,MACN,QAAA,EAAU,aAAa,OAAA,EAAS;AAAA,QAC9B,UAAU,MAAA,CAAO,IAAA;AAAA,QACjB,UAAA,EAAY,cAAA,CAAe,OAAA,CAAQ,gBAAgB,CAAA;AAAA,QACnD,eAAA,EAAiB,iBAAA;AAAA,UACf,MAAA,CAAO,aAAa,EAAC;AAAA,UACrB;AAAA;AACF,OACD;AAAA;AACH,GACF;AAEF,EAAA,OAAO,IAAI,MAAM,MAAA,EAAQ;AAAA,IACvB,GAAA,CAAI,MAAA,EAAQ,QAAA,EAAU,QAAA,EAAU;AAC9B,MAAA,IAAI,QAAA,KAAa,WAAA,IAAe,gBAAA,KAAqB,MAAA,EAAW;AAC9D,QAAA,OAAO,gBAAA;AAAA,MACT;AACA,MAAA,IAAI,aAAa,UAAA,EAAY;AAC3B,QAAA,OAAO,eAAA;AAAA,MACT;AACA,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,UAAU,QAAQ,CAAA;AACpD,MAAA,IAAI,OAAO,UAAU,UAAA,EAAY;AAC/B,QAAA,OAAO,KAAA,CAAM,KAAK,MAAM,CAAA;AAAA,MAC1B;AACA,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,GACD,CAAA;AACH;AAGO,SAAS,2BAAA,GAAoC;AAClD,EAAA,eAAA,GAAkB,CAAA;AACpB","file":"index.cjs","sourcesContent":["const DEFAULT_MAX_SUMMARY_LENGTH = 240;\n\nfunction fallbackString(value: unknown): string {\n try {\n return String(value);\n } catch {\n return \"[unserializable]\";\n }\n}\n\nexport function summarizeMcpValue(\n value: unknown,\n maxLength = DEFAULT_MAX_SUMMARY_LENGTH,\n): string {\n let text: string;\n try {\n if (typeof value === \"string\") {\n text = value;\n } else {\n // JSON.stringify returns undefined (not a string) for undefined,\n // functions, and symbols; falling through with undefined would crash\n // instrumentation at text.length below.\n const serialized = JSON.stringify(value);\n text = serialized === undefined ? fallbackString(value) : serialized;\n }\n } catch {\n text = fallbackString(value);\n }\n if (text.length <= maxLength) return text;\n return `${text.slice(0, Math.max(0, maxLength - 3))}...`;\n}\n\nexport function hashServerUrl(url: string): string {\n let hash = 0;\n for (let i = 0; i < url.length; i += 1) {\n hash = (hash * 31 + url.charCodeAt(i)) >>> 0;\n }\n return hash.toString(16).padStart(8, \"0\");\n}\n","import { step } from \"agent-inspect\";\n\nimport { hashServerUrl, summarizeMcpValue } from \"./summarize.js\";\nimport type {\n McpClientLike,\n McpClientTracerOptions,\n McpTraceMetadata,\n} from \"./types.js\";\n\nlet toolCallCounter = 0;\n\nfunction nextToolCallId(prefix?: string): string {\n toolCallCounter += 1;\n const base = prefix?.trim() || \"mcp\";\n return `${base}-${toolCallCounter}`;\n}\n\nfunction baseMetadata(\n options: McpClientTracerOptions,\n overrides: Partial<McpTraceMetadata> = {},\n): Record<string, unknown> {\n const metadata: McpTraceMetadata = {\n source: { type: \"mcp-client\" },\n ...(options.serverName ? { mcpServerName: options.serverName } : {}),\n ...(options.serverUrl\n ? { mcpServerUrlHash: hashServerUrl(options.serverUrl) }\n : {}),\n ...(options.sessionId ? { sessionId: options.sessionId } : {}),\n ...overrides,\n };\n return { ...(options.metadata ?? {}), ...metadata };\n}\n\n/**\n * Wraps an MCP client so tools/list and tools/call emit local tool steps.\n * Telemetry only — does not proxy network behavior beyond the wrapped client.\n *\n * Uses a Proxy over the original client so prototype methods (`connect`,\n * `close`, `listResources`, …), private SDK state, and `instanceof` stay intact.\n * A shallow object spread would drop prototype methods and copy private fields.\n */\nexport function wrapMcpClient<T extends McpClientLike>(\n client: T,\n options: McpClientTracerOptions = {},\n): T {\n const maxSummaryLength = options.maxSummaryLength ?? 240;\n\n const listToolsImpl =\n typeof client.listTools === \"function\"\n ? client.listTools.bind(client)\n : undefined;\n const callToolImpl = client.callTool.bind(client);\n\n const wrappedListTools = listToolsImpl\n ? async (params?: unknown) =>\n step(\n \"mcp:tools/list\",\n async () => {\n const result = await listToolsImpl(params);\n return result;\n },\n {\n type: \"tool\",\n metadata: baseMetadata(options, {\n toolName: \"tools/list\",\n toolCallId: nextToolCallId(options.toolCallIdPrefix),\n }),\n },\n )\n : undefined;\n\n const wrappedCallTool = async (\n params: Parameters<McpClientLike[\"callTool\"]>[0],\n ) =>\n step(\n `mcp:${params.name}`,\n async () => callToolImpl(params),\n {\n type: \"tool\",\n metadata: baseMetadata(options, {\n toolName: params.name,\n toolCallId: nextToolCallId(options.toolCallIdPrefix),\n argumentSummary: summarizeMcpValue(\n params.arguments ?? {},\n maxSummaryLength,\n ),\n }),\n },\n );\n\n return new Proxy(client, {\n get(target, property, receiver) {\n if (property === \"listTools\" && wrappedListTools !== undefined) {\n return wrappedListTools;\n }\n if (property === \"callTool\") {\n return wrappedCallTool;\n }\n const value = Reflect.get(target, property, receiver);\n if (typeof value === \"function\") {\n return value.bind(target);\n }\n return value;\n },\n }) as T;\n}\n\n/** @internal resets tool call ids for deterministic tests */\nexport function resetMcpToolCallIdsForTests(): void {\n toolCallCounter = 0;\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -53,6 +53,10 @@ declare function hashServerUrl(url: string): string;
53
53
  /**
54
54
  * Wraps an MCP client so tools/list and tools/call emit local tool steps.
55
55
  * Telemetry only — does not proxy network behavior beyond the wrapped client.
56
+ *
57
+ * Uses a Proxy over the original client so prototype methods (`connect`,
58
+ * `close`, `listResources`, …), private SDK state, and `instanceof` stay intact.
59
+ * A shallow object spread would drop prototype methods and copy private fields.
56
60
  */
57
61
  declare function wrapMcpClient<T extends McpClientLike>(client: T, options?: McpClientTracerOptions): T;
58
62
  /** @internal resets tool call ids for deterministic tests */
package/dist/index.d.ts CHANGED
@@ -53,6 +53,10 @@ declare function hashServerUrl(url: string): string;
53
53
  /**
54
54
  * Wraps an MCP client so tools/list and tools/call emit local tool steps.
55
55
  * Telemetry only — does not proxy network behavior beyond the wrapped client.
56
+ *
57
+ * Uses a Proxy over the original client so prototype methods (`connect`,
58
+ * `close`, `listResources`, …), private SDK state, and `instanceof` stay intact.
59
+ * A shallow object spread would drop prototype methods and copy private fields.
56
60
  */
57
61
  declare function wrapMcpClient<T extends McpClientLike>(client: T, options?: McpClientTracerOptions): T;
58
62
  /** @internal resets tool call ids for deterministic tests */
package/dist/index.mjs CHANGED
@@ -49,28 +49,25 @@ function baseMetadata(options, overrides = {}) {
49
49
  }
50
50
  function wrapMcpClient(client, options = {}) {
51
51
  const maxSummaryLength = options.maxSummaryLength ?? 240;
52
- const wrapped = { ...client };
53
- if (typeof client.listTools === "function") {
54
- const listTools = client.listTools.bind(client);
55
- wrapped.listTools = async (params) => step(
56
- "mcp:tools/list",
57
- async () => {
58
- const result = await listTools(params);
59
- return result;
60
- },
61
- {
62
- type: "tool",
63
- metadata: baseMetadata(options, {
64
- toolName: "tools/list",
65
- toolCallId: nextToolCallId(options.toolCallIdPrefix)
66
- })
67
- }
68
- );
69
- }
70
- const callTool = client.callTool.bind(client);
71
- wrapped.callTool = async (params) => step(
52
+ const listToolsImpl = typeof client.listTools === "function" ? client.listTools.bind(client) : void 0;
53
+ const callToolImpl = client.callTool.bind(client);
54
+ const wrappedListTools = listToolsImpl ? async (params) => step(
55
+ "mcp:tools/list",
56
+ async () => {
57
+ const result = await listToolsImpl(params);
58
+ return result;
59
+ },
60
+ {
61
+ type: "tool",
62
+ metadata: baseMetadata(options, {
63
+ toolName: "tools/list",
64
+ toolCallId: nextToolCallId(options.toolCallIdPrefix)
65
+ })
66
+ }
67
+ ) : void 0;
68
+ const wrappedCallTool = async (params) => step(
72
69
  `mcp:${params.name}`,
73
- async () => callTool(params),
70
+ async () => callToolImpl(params),
74
71
  {
75
72
  type: "tool",
76
73
  metadata: baseMetadata(options, {
@@ -83,7 +80,21 @@ function wrapMcpClient(client, options = {}) {
83
80
  })
84
81
  }
85
82
  );
86
- return wrapped;
83
+ return new Proxy(client, {
84
+ get(target, property, receiver) {
85
+ if (property === "listTools" && wrappedListTools !== void 0) {
86
+ return wrappedListTools;
87
+ }
88
+ if (property === "callTool") {
89
+ return wrappedCallTool;
90
+ }
91
+ const value = Reflect.get(target, property, receiver);
92
+ if (typeof value === "function") {
93
+ return value.bind(target);
94
+ }
95
+ return value;
96
+ }
97
+ });
87
98
  }
88
99
  function resetMcpToolCallIdsForTests() {
89
100
  toolCallCounter = 0;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/summarize.ts","../src/wrap.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["const DEFAULT_MAX_SUMMARY_LENGTH = 240;\n\nfunction fallbackString(value: unknown): string {\n try {\n return String(value);\n } catch {\n return \"[unserializable]\";\n }\n}\n\nexport function summarizeMcpValue(\n value: unknown,\n maxLength = DEFAULT_MAX_SUMMARY_LENGTH,\n): string {\n let text: string;\n try {\n if (typeof value === \"string\") {\n text = value;\n } else {\n // JSON.stringify returns undefined (not a string) for undefined,\n // functions, and symbols; falling through with undefined would crash\n // instrumentation at text.length below.\n const serialized = JSON.stringify(value);\n text = serialized === undefined ? fallbackString(value) : serialized;\n }\n } catch {\n text = fallbackString(value);\n }\n if (text.length <= maxLength) return text;\n return `${text.slice(0, Math.max(0, maxLength - 3))}...`;\n}\n\nexport function hashServerUrl(url: string): string {\n let hash = 0;\n for (let i = 0; i < url.length; i += 1) {\n hash = (hash * 31 + url.charCodeAt(i)) >>> 0;\n }\n return hash.toString(16).padStart(8, \"0\");\n}\n","import { step } from \"agent-inspect\";\n\nimport { hashServerUrl, summarizeMcpValue } from \"./summarize.js\";\nimport type {\n McpClientLike,\n McpClientTracerOptions,\n McpTraceMetadata,\n} from \"./types.js\";\n\nlet toolCallCounter = 0;\n\nfunction nextToolCallId(prefix?: string): string {\n toolCallCounter += 1;\n const base = prefix?.trim() || \"mcp\";\n return `${base}-${toolCallCounter}`;\n}\n\nfunction baseMetadata(\n options: McpClientTracerOptions,\n overrides: Partial<McpTraceMetadata> = {},\n): Record<string, unknown> {\n const metadata: McpTraceMetadata = {\n source: { type: \"mcp-client\" },\n ...(options.serverName ? { mcpServerName: options.serverName } : {}),\n ...(options.serverUrl\n ? { mcpServerUrlHash: hashServerUrl(options.serverUrl) }\n : {}),\n ...(options.sessionId ? { sessionId: options.sessionId } : {}),\n ...overrides,\n };\n return { ...(options.metadata ?? {}), ...metadata };\n}\n\n/**\n * Wraps an MCP client so tools/list and tools/call emit local tool steps.\n * Telemetry only — does not proxy network behavior beyond the wrapped client.\n */\nexport function wrapMcpClient<T extends McpClientLike>(\n client: T,\n options: McpClientTracerOptions = {},\n): T {\n const maxSummaryLength = options.maxSummaryLength ?? 240;\n const wrapped = { ...client } as T;\n\n if (typeof client.listTools === \"function\") {\n const listTools = client.listTools.bind(client);\n wrapped.listTools = async (params?: unknown) =>\n step(\n \"mcp:tools/list\",\n async () => {\n const result = await listTools(params);\n return result;\n },\n {\n type: \"tool\",\n metadata: baseMetadata(options, {\n toolName: \"tools/list\",\n toolCallId: nextToolCallId(options.toolCallIdPrefix),\n }),\n },\n );\n }\n\n const callTool = client.callTool.bind(client);\n wrapped.callTool = async (params) =>\n step(\n `mcp:${params.name}`,\n async () => callTool(params),\n {\n type: \"tool\",\n metadata: baseMetadata(options, {\n toolName: params.name,\n toolCallId: nextToolCallId(options.toolCallIdPrefix),\n argumentSummary: summarizeMcpValue(\n params.arguments ?? {},\n maxSummaryLength,\n ),\n }),\n },\n );\n\n return wrapped;\n}\n\n/** @internal resets tool call ids for deterministic tests */\nexport function resetMcpToolCallIdsForTests(): void {\n toolCallCounter = 0;\n}\n"]}
1
+ {"version":3,"sources":["../src/summarize.ts","../src/wrap.ts"],"names":[],"mappings":";;;AAAA,IAAM,0BAAA,GAA6B,GAAA;AAEnC,SAAS,eAAe,KAAA,EAAwB;AAC9C,EAAA,IAAI;AACF,IAAA,OAAO,OAAO,KAAK,CAAA;AAAA,EACrB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,kBAAA;AAAA,EACT;AACF;AAEO,SAAS,iBAAA,CACd,KAAA,EACA,SAAA,GAAY,0BAAA,EACJ;AACR,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI;AACF,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,MAAA,IAAA,GAAO,KAAA;AAAA,IACT,CAAA,MAAO;AAIL,MAAA,MAAM,UAAA,GAAa,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA;AACvC,MAAA,IAAA,GAAO,UAAA,KAAe,KAAA,CAAA,GAAY,cAAA,CAAe,KAAK,CAAA,GAAI,UAAA;AAAA,IAC5D;AAAA,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,IAAA,GAAO,eAAe,KAAK,CAAA;AAAA,EAC7B;AACA,EAAA,IAAI,IAAA,CAAK,MAAA,IAAU,SAAA,EAAW,OAAO,IAAA;AACrC,EAAA,OAAO,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,EAAG,SAAA,GAAY,CAAC,CAAC,CAAC,CAAA,GAAA,CAAA;AACrD;AAEO,SAAS,cAAc,GAAA,EAAqB;AACjD,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,GAAA,CAAI,MAAA,EAAQ,KAAK,CAAA,EAAG;AACtC,IAAA,IAAA,GAAQ,IAAA,GAAO,EAAA,GAAK,GAAA,CAAI,UAAA,CAAW,CAAC,CAAA,KAAO,CAAA;AAAA,EAC7C;AACA,EAAA,OAAO,KAAK,QAAA,CAAS,EAAE,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AAC1C;AC7BA,IAAI,eAAA,GAAkB,CAAA;AAEtB,SAAS,eAAe,MAAA,EAAyB;AAC/C,EAAA,eAAA,IAAmB,CAAA;AACnB,EAAA,MAAM,IAAA,GAAO,MAAA,EAAQ,IAAA,EAAK,IAAK,KAAA;AAC/B,EAAA,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,eAAe,CAAA,CAAA;AACnC;AAEA,SAAS,YAAA,CACP,OAAA,EACA,SAAA,GAAuC,EAAC,EACf;AACzB,EAAA,MAAM,QAAA,GAA6B;AAAA,IACjC,MAAA,EAAQ,EAAE,IAAA,EAAM,YAAA,EAAa;AAAA,IAC7B,GAAI,QAAQ,UAAA,GAAa,EAAE,eAAe,OAAA,CAAQ,UAAA,KAAe,EAAC;AAAA,IAClE,GAAI,OAAA,CAAQ,SAAA,GACR,EAAE,gBAAA,EAAkB,cAAc,OAAA,CAAQ,SAAS,CAAA,EAAE,GACrD,EAAC;AAAA,IACL,GAAI,QAAQ,SAAA,GAAY,EAAE,WAAW,OAAA,CAAQ,SAAA,KAAc,EAAC;AAAA,IAC5D,GAAG;AAAA,GACL;AACA,EAAA,OAAO,EAAE,GAAI,OAAA,CAAQ,YAAY,EAAC,EAAI,GAAG,QAAA,EAAS;AACpD;AAUO,SAAS,aAAA,CACd,MAAA,EACA,OAAA,GAAkC,EAAC,EAChC;AACH,EAAA,MAAM,gBAAA,GAAmB,QAAQ,gBAAA,IAAoB,GAAA;AAErD,EAAA,MAAM,aAAA,GACJ,OAAO,MAAA,CAAO,SAAA,KAAc,aACxB,MAAA,CAAO,SAAA,CAAU,IAAA,CAAK,MAAM,CAAA,GAC5B,MAAA;AACN,EAAA,MAAM,YAAA,GAAe,MAAA,CAAO,QAAA,CAAS,IAAA,CAAK,MAAM,CAAA;AAEhD,EAAA,MAAM,gBAAA,GAAmB,aAAA,GACrB,OAAO,MAAA,KACL,IAAA;AAAA,IACE,gBAAA;AAAA,IACA,YAAY;AACV,MAAA,MAAM,MAAA,GAAS,MAAM,aAAA,CAAc,MAAM,CAAA;AACzC,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAAA,IACA;AAAA,MACE,IAAA,EAAM,MAAA;AAAA,MACN,QAAA,EAAU,aAAa,OAAA,EAAS;AAAA,QAC9B,QAAA,EAAU,YAAA;AAAA,QACV,UAAA,EAAY,cAAA,CAAe,OAAA,CAAQ,gBAAgB;AAAA,OACpD;AAAA;AACH,GACF,GACF,MAAA;AAEJ,EAAA,MAAM,eAAA,GAAkB,OACtB,MAAA,KAEA,IAAA;AAAA,IACE,CAAA,IAAA,EAAO,OAAO,IAAI,CAAA,CAAA;AAAA,IAClB,YAAY,aAAa,MAAM,CAAA;AAAA,IAC/B;AAAA,MACE,IAAA,EAAM,MAAA;AAAA,MACN,QAAA,EAAU,aAAa,OAAA,EAAS;AAAA,QAC9B,UAAU,MAAA,CAAO,IAAA;AAAA,QACjB,UAAA,EAAY,cAAA,CAAe,OAAA,CAAQ,gBAAgB,CAAA;AAAA,QACnD,eAAA,EAAiB,iBAAA;AAAA,UACf,MAAA,CAAO,aAAa,EAAC;AAAA,UACrB;AAAA;AACF,OACD;AAAA;AACH,GACF;AAEF,EAAA,OAAO,IAAI,MAAM,MAAA,EAAQ;AAAA,IACvB,GAAA,CAAI,MAAA,EAAQ,QAAA,EAAU,QAAA,EAAU;AAC9B,MAAA,IAAI,QAAA,KAAa,WAAA,IAAe,gBAAA,KAAqB,MAAA,EAAW;AAC9D,QAAA,OAAO,gBAAA;AAAA,MACT;AACA,MAAA,IAAI,aAAa,UAAA,EAAY;AAC3B,QAAA,OAAO,eAAA;AAAA,MACT;AACA,MAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,UAAU,QAAQ,CAAA;AACpD,MAAA,IAAI,OAAO,UAAU,UAAA,EAAY;AAC/B,QAAA,OAAO,KAAA,CAAM,KAAK,MAAM,CAAA;AAAA,MAC1B;AACA,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,GACD,CAAA;AACH;AAGO,SAAS,2BAAA,GAAoC;AAClD,EAAA,eAAA,GAAkB,CAAA;AACpB","file":"index.mjs","sourcesContent":["const DEFAULT_MAX_SUMMARY_LENGTH = 240;\n\nfunction fallbackString(value: unknown): string {\n try {\n return String(value);\n } catch {\n return \"[unserializable]\";\n }\n}\n\nexport function summarizeMcpValue(\n value: unknown,\n maxLength = DEFAULT_MAX_SUMMARY_LENGTH,\n): string {\n let text: string;\n try {\n if (typeof value === \"string\") {\n text = value;\n } else {\n // JSON.stringify returns undefined (not a string) for undefined,\n // functions, and symbols; falling through with undefined would crash\n // instrumentation at text.length below.\n const serialized = JSON.stringify(value);\n text = serialized === undefined ? fallbackString(value) : serialized;\n }\n } catch {\n text = fallbackString(value);\n }\n if (text.length <= maxLength) return text;\n return `${text.slice(0, Math.max(0, maxLength - 3))}...`;\n}\n\nexport function hashServerUrl(url: string): string {\n let hash = 0;\n for (let i = 0; i < url.length; i += 1) {\n hash = (hash * 31 + url.charCodeAt(i)) >>> 0;\n }\n return hash.toString(16).padStart(8, \"0\");\n}\n","import { step } from \"agent-inspect\";\n\nimport { hashServerUrl, summarizeMcpValue } from \"./summarize.js\";\nimport type {\n McpClientLike,\n McpClientTracerOptions,\n McpTraceMetadata,\n} from \"./types.js\";\n\nlet toolCallCounter = 0;\n\nfunction nextToolCallId(prefix?: string): string {\n toolCallCounter += 1;\n const base = prefix?.trim() || \"mcp\";\n return `${base}-${toolCallCounter}`;\n}\n\nfunction baseMetadata(\n options: McpClientTracerOptions,\n overrides: Partial<McpTraceMetadata> = {},\n): Record<string, unknown> {\n const metadata: McpTraceMetadata = {\n source: { type: \"mcp-client\" },\n ...(options.serverName ? { mcpServerName: options.serverName } : {}),\n ...(options.serverUrl\n ? { mcpServerUrlHash: hashServerUrl(options.serverUrl) }\n : {}),\n ...(options.sessionId ? { sessionId: options.sessionId } : {}),\n ...overrides,\n };\n return { ...(options.metadata ?? {}), ...metadata };\n}\n\n/**\n * Wraps an MCP client so tools/list and tools/call emit local tool steps.\n * Telemetry only — does not proxy network behavior beyond the wrapped client.\n *\n * Uses a Proxy over the original client so prototype methods (`connect`,\n * `close`, `listResources`, …), private SDK state, and `instanceof` stay intact.\n * A shallow object spread would drop prototype methods and copy private fields.\n */\nexport function wrapMcpClient<T extends McpClientLike>(\n client: T,\n options: McpClientTracerOptions = {},\n): T {\n const maxSummaryLength = options.maxSummaryLength ?? 240;\n\n const listToolsImpl =\n typeof client.listTools === \"function\"\n ? client.listTools.bind(client)\n : undefined;\n const callToolImpl = client.callTool.bind(client);\n\n const wrappedListTools = listToolsImpl\n ? async (params?: unknown) =>\n step(\n \"mcp:tools/list\",\n async () => {\n const result = await listToolsImpl(params);\n return result;\n },\n {\n type: \"tool\",\n metadata: baseMetadata(options, {\n toolName: \"tools/list\",\n toolCallId: nextToolCallId(options.toolCallIdPrefix),\n }),\n },\n )\n : undefined;\n\n const wrappedCallTool = async (\n params: Parameters<McpClientLike[\"callTool\"]>[0],\n ) =>\n step(\n `mcp:${params.name}`,\n async () => callToolImpl(params),\n {\n type: \"tool\",\n metadata: baseMetadata(options, {\n toolName: params.name,\n toolCallId: nextToolCallId(options.toolCallIdPrefix),\n argumentSummary: summarizeMcpValue(\n params.arguments ?? {},\n maxSummaryLength,\n ),\n }),\n },\n );\n\n return new Proxy(client, {\n get(target, property, receiver) {\n if (property === \"listTools\" && wrappedListTools !== undefined) {\n return wrappedListTools;\n }\n if (property === \"callTool\") {\n return wrappedCallTool;\n }\n const value = Reflect.get(target, property, receiver);\n if (typeof value === \"function\") {\n return value.bind(target);\n }\n return value;\n },\n }) as T;\n}\n\n/** @internal resets tool call ids for deterministic tests */\nexport function resetMcpToolCallIdsForTests(): void {\n toolCallCounter = 0;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-inspect/mcp",
3
- "version": "6.29.2",
3
+ "version": "6.29.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Local MCP client tool-call tracing for AgentInspect",
@@ -34,7 +34,7 @@
34
34
  "dist"
35
35
  ],
36
36
  "dependencies": {
37
- "agent-inspect": "6.29.2"
37
+ "agent-inspect": "6.29.3"
38
38
  },
39
39
  "publishConfig": {
40
40
  "access": "public"