@copilotkit/runtime 1.56.5-canary.1777972218 → 1.57.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.
Files changed (56) hide show
  1. package/dist/agent/index.cjs +8 -41
  2. package/dist/agent/index.cjs.map +1 -1
  3. package/dist/agent/index.d.cts +27 -54
  4. package/dist/agent/index.d.cts.map +1 -1
  5. package/dist/agent/index.d.mts +27 -54
  6. package/dist/agent/index.d.mts.map +1 -1
  7. package/dist/agent/index.mjs +10 -43
  8. package/dist/agent/index.mjs.map +1 -1
  9. package/dist/package.cjs +1 -1
  10. package/dist/package.mjs +1 -1
  11. package/dist/v2/index.cjs +0 -2
  12. package/dist/v2/index.d.cts +5 -6
  13. package/dist/v2/index.d.mts +5 -6
  14. package/dist/v2/index.mjs +1 -2
  15. package/dist/v2/runtime/core/runtime.d.cts +0 -1
  16. package/dist/v2/runtime/core/runtime.d.cts.map +1 -1
  17. package/dist/v2/runtime/core/runtime.d.mts +0 -1
  18. package/dist/v2/runtime/core/runtime.d.mts.map +1 -1
  19. package/dist/v2/runtime/handlers/intelligence/run.cjs +0 -4
  20. package/dist/v2/runtime/handlers/intelligence/run.cjs.map +1 -1
  21. package/dist/v2/runtime/handlers/intelligence/run.mjs +0 -4
  22. package/dist/v2/runtime/handlers/intelligence/run.mjs.map +1 -1
  23. package/dist/v2/runtime/handlers/shared/agent-utils.cjs.map +1 -1
  24. package/dist/v2/runtime/handlers/shared/agent-utils.mjs.map +1 -1
  25. package/dist/v2/runtime/index.d.cts +1 -3
  26. package/dist/v2/runtime/index.d.cts.map +1 -1
  27. package/dist/v2/runtime/index.d.mts +1 -3
  28. package/dist/v2/runtime/index.d.mts.map +1 -1
  29. package/dist/v2/runtime/intelligence-platform/client.cjs +0 -53
  30. package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
  31. package/dist/v2/runtime/intelligence-platform/client.d.cts +0 -41
  32. package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
  33. package/dist/v2/runtime/intelligence-platform/client.d.mts +0 -41
  34. package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
  35. package/dist/v2/runtime/intelligence-platform/client.mjs +0 -53
  36. package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
  37. package/package.json +2 -2
  38. package/src/agent/__tests__/mcp-clients.test.ts +25 -11
  39. package/src/agent/__tests__/mcp-servers-integration.test.ts +1 -485
  40. package/src/agent/index.ts +62 -139
  41. package/src/v2/runtime/handlers/intelligence/run.ts +0 -9
  42. package/src/v2/runtime/handlers/shared/agent-utils.ts +0 -1
  43. package/src/v2/runtime/index.ts +0 -5
  44. package/src/v2/runtime/intelligence-platform/client.ts +0 -68
  45. package/dist/agent/mcp-transport.cjs +0 -94
  46. package/dist/agent/mcp-transport.cjs.map +0 -1
  47. package/dist/agent/mcp-transport.d.cts +0 -51
  48. package/dist/agent/mcp-transport.d.cts.map +0 -1
  49. package/dist/agent/mcp-transport.d.mts +0 -52
  50. package/dist/agent/mcp-transport.d.mts.map +0 -1
  51. package/dist/agent/mcp-transport.mjs +0 -92
  52. package/dist/agent/mcp-transport.mjs.map +0 -1
  53. package/dist/v2/runtime/intelligence-platform/index.d.cts +0 -2
  54. package/dist/v2/runtime/intelligence-platform/index.d.mts +0 -2
  55. package/src/agent/mcp-transport.ts +0 -190
  56. package/src/v2/runtime/intelligence-platform/__tests__/intelligence-mcp-helper.test.ts +0 -190
@@ -1,92 +0,0 @@
1
- import "reflect-metadata";
2
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
3
-
4
- //#region src/agent/mcp-transport.ts
5
- /**
6
- * Thrown when an MCP {@link MCPClientConfigHTTP.getHeaders} resolver throws.
7
- * Wraps the underlying error so `RUN_ERROR` carries clear attribution instead
8
- * of a generic transport failure. The original error is preserved on the
9
- * standard ES2022 `Error.cause` chain.
10
- */
11
- var MCPHeaderResolverError = class extends Error {
12
- constructor(message, cause) {
13
- super(message, { cause });
14
- this.name = "MCPHeaderResolverError";
15
- }
16
- };
17
- /**
18
- * MCP transport for CopilotKit's BuiltInAgent that adds per-call header
19
- * resolution on top of the standard Streamable HTTP transport.
20
- *
21
- * Implements `@ai-sdk/mcp`'s {@link MCPTransport} interface so it can be
22
- * passed straight to `createMCPClient({ transport })`. Internally delegates
23
- * to `@modelcontextprotocol/sdk`'s `StreamableHTTPClientTransport` with a
24
- * wrapped `fetch` that runs the static-header + per-call resolver pipeline
25
- * before each outbound request.
26
- */
27
- var CopilotKitMCPTransport = class {
28
- constructor(options) {
29
- const transportOptions = {
30
- ...options.options,
31
- fetch: buildWrappedFetch(options)
32
- };
33
- this.inner = new StreamableHTTPClientTransport(new URL(options.url), transportOptions);
34
- }
35
- get onclose() {
36
- return this.inner.onclose;
37
- }
38
- set onclose(handler) {
39
- this.inner.onclose = handler;
40
- }
41
- get onerror() {
42
- return this.inner.onerror;
43
- }
44
- set onerror(handler) {
45
- this.inner.onerror = handler;
46
- }
47
- get onmessage() {
48
- return this.inner.onmessage;
49
- }
50
- set onmessage(handler) {
51
- this.inner.onmessage = handler;
52
- }
53
- start() {
54
- return this.inner.start();
55
- }
56
- send(message) {
57
- return this.inner.send(message);
58
- }
59
- close() {
60
- return this.inner.close();
61
- }
62
- };
63
- function buildWrappedFetch(options) {
64
- const { headers: staticHeaders, getHeaders, requestHeaders, input, url: mcpServerUrl, user, options: transportOptions } = options;
65
- const baseFetch = transportOptions?.fetch ?? globalThis.fetch;
66
- return async (fetchUrl, init) => {
67
- const merged = new Headers(init?.headers);
68
- if (staticHeaders) for (const [key, value] of Object.entries(staticHeaders)) merged.set(key, value);
69
- if (getHeaders) {
70
- let resolved;
71
- try {
72
- resolved = await getHeaders({
73
- requestHeaders,
74
- input,
75
- mcpServerUrl,
76
- user
77
- });
78
- } catch (err) {
79
- throw new MCPHeaderResolverError(`MCP header resolver for ${mcpServerUrl} threw: ${err instanceof Error ? err.message : String(err)}`, err);
80
- }
81
- for (const [key, value] of Object.entries(resolved)) merged.set(key, value);
82
- }
83
- return baseFetch(fetchUrl, {
84
- ...init,
85
- headers: merged
86
- });
87
- };
88
- }
89
-
90
- //#endregion
91
- export { CopilotKitMCPTransport, MCPHeaderResolverError };
92
- //# sourceMappingURL=mcp-transport.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mcp-transport.mjs","names":[],"sources":["../../src/agent/mcp-transport.ts"],"sourcesContent":["import type { RunAgentInput } from \"@ag-ui/client\";\nimport type { JSONRPCMessage, MCPTransport } from \"@ai-sdk/mcp\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport type { StreamableHTTPClientTransportOptions } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport type { FetchLike } from \"@modelcontextprotocol/sdk/shared/transport.js\";\n\n/**\n * The end-user identity resolved by the runtime's `identifyUser` callback for\n * the current request. Surfaced on {@link MCPRequestContext} so MCP header\n * resolvers can read it without re-doing auth work.\n */\nexport interface MCPRuntimeUser {\n id: string;\n name: string;\n}\n\n/**\n * Context handed to {@link MCPClientConfigHTTP.getHeaders} on every outbound\n * MCP HTTP request. The resolver is invoked fresh per request — initialize,\n * tools/list, tools/call, and reconnects — so values it depends on are never\n * cached across calls.\n */\nexport interface MCPRequestContext {\n /**\n * Headers forwarded onto the agent for this run. Populated by the runtime's\n * `extractForwardableHeaders` (`authorization` + every `x-*` header from the\n * incoming HTTP request). Keys are lower-cased.\n */\n requestHeaders: Record<string, string>;\n /** The {@link RunAgentInput} the agent is currently running. */\n input: RunAgentInput;\n /** URL of the MCP server this request is going to. */\n mcpServerUrl: string;\n /**\n * The end-user identity for this run, resolved by the runtime's\n * `identifyUser` callback (only populated when the agent is run via a\n * `CopilotRuntime` configured with `identifyUser`). Snapshotted at\n * run-start.\n */\n user?: MCPRuntimeUser;\n}\n\n/**\n * Thrown when an MCP {@link MCPClientConfigHTTP.getHeaders} resolver throws.\n * Wraps the underlying error so `RUN_ERROR` carries clear attribution instead\n * of a generic transport failure. The original error is preserved on the\n * standard ES2022 `Error.cause` chain.\n */\nexport class MCPHeaderResolverError extends Error {\n constructor(message: string, cause: unknown) {\n super(message, { cause });\n this.name = \"MCPHeaderResolverError\";\n }\n}\n\nexport interface CopilotKitMCPTransportOptions {\n /** URL of the MCP server. */\n url: string;\n /** Static HTTP headers, merged into every outbound request. */\n headers?: Record<string, string>;\n /**\n * Per-call header resolver. Invoked on **every** outbound HTTP request to\n * this server (initialize, tools/list, tools/call, reconnects). Returned\n * headers are merged on top of `headers`, so a resolver can override either.\n */\n getHeaders?: (\n ctx: MCPRequestContext,\n ) => Record<string, string> | Promise<Record<string, string>>;\n /**\n * Pre-existing escape hatch: low-level options for the underlying\n * `StreamableHTTPClientTransport`. Forwarded as-is, except `fetch` is\n * wrapped so static `headers` and `getHeaders` resolution still apply.\n */\n options?: StreamableHTTPClientTransportOptions;\n /** Snapshot of the agent's per-run forwarded headers, captured at run-start. */\n requestHeaders: Record<string, string>;\n /** RunAgentInput for the current run, exposed to the resolver via context. */\n input: RunAgentInput;\n /** Per-run end-user identity from the runtime's `identifyUser`, if set. */\n user?: MCPRuntimeUser;\n}\n\n/**\n * MCP transport for CopilotKit's BuiltInAgent that adds per-call header\n * resolution on top of the standard Streamable HTTP transport.\n *\n * Implements `@ai-sdk/mcp`'s {@link MCPTransport} interface so it can be\n * passed straight to `createMCPClient({ transport })`. Internally delegates\n * to `@modelcontextprotocol/sdk`'s `StreamableHTTPClientTransport` with a\n * wrapped `fetch` that runs the static-header + per-call resolver pipeline\n * before each outbound request.\n */\nexport class CopilotKitMCPTransport implements MCPTransport {\n private readonly inner: StreamableHTTPClientTransport;\n\n constructor(options: CopilotKitMCPTransportOptions) {\n const transportOptions: StreamableHTTPClientTransportOptions = {\n ...options.options,\n fetch: buildWrappedFetch(options),\n };\n this.inner = new StreamableHTTPClientTransport(\n new URL(options.url),\n transportOptions,\n );\n }\n\n get onclose(): (() => void) | undefined {\n return this.inner.onclose;\n }\n set onclose(handler: (() => void) | undefined) {\n this.inner.onclose = handler;\n }\n\n get onerror(): ((error: Error) => void) | undefined {\n return this.inner.onerror;\n }\n set onerror(handler: ((error: Error) => void) | undefined) {\n this.inner.onerror = handler;\n }\n\n get onmessage(): ((message: JSONRPCMessage) => void) | undefined {\n return this.inner.onmessage as\n | ((message: JSONRPCMessage) => void)\n | undefined;\n }\n set onmessage(handler: ((message: JSONRPCMessage) => void) | undefined) {\n this.inner.onmessage = handler as\n | ((message: JSONRPCMessage) => void)\n | undefined;\n }\n\n start(): Promise<void> {\n return this.inner.start();\n }\n\n send(message: JSONRPCMessage): Promise<void> {\n return this.inner.send(message as Parameters<typeof this.inner.send>[0]);\n }\n\n close(): Promise<void> {\n return this.inner.close();\n }\n}\n\nfunction buildWrappedFetch(options: CopilotKitMCPTransportOptions): FetchLike {\n const {\n headers: staticHeaders,\n getHeaders,\n requestHeaders,\n input,\n url: mcpServerUrl,\n user,\n options: transportOptions,\n } = options;\n const baseFetch: FetchLike = transportOptions?.fetch ?? globalThis.fetch;\n\n return async (fetchUrl, init) => {\n // SDK passes a Headers instance via init.headers — start fresh from it so\n // we don't mutate the SDK's object, then layer headers on top via .set()\n // (last write wins).\n const merged = new Headers(init?.headers);\n if (staticHeaders) {\n for (const [key, value] of Object.entries(staticHeaders)) {\n merged.set(key, value);\n }\n }\n if (getHeaders) {\n let resolved: Record<string, string>;\n try {\n resolved = await getHeaders({\n requestHeaders,\n input,\n mcpServerUrl,\n user,\n });\n } catch (err) {\n throw new MCPHeaderResolverError(\n `MCP header resolver for ${mcpServerUrl} threw: ${\n err instanceof Error ? err.message : String(err)\n }`,\n err,\n );\n }\n for (const [key, value] of Object.entries(resolved)) {\n merged.set(key, value);\n }\n }\n return baseFetch(fetchUrl, { ...init, headers: merged });\n };\n}\n"],"mappings":";;;;;;;;;;AAgDA,IAAa,yBAAb,cAA4C,MAAM;CAChD,YAAY,SAAiB,OAAgB;AAC3C,QAAM,SAAS,EAAE,OAAO,CAAC;AACzB,OAAK,OAAO;;;;;;;;;;;;;AAyChB,IAAa,yBAAb,MAA4D;CAG1D,YAAY,SAAwC;EAClD,MAAM,mBAAyD;GAC7D,GAAG,QAAQ;GACX,OAAO,kBAAkB,QAAQ;GAClC;AACD,OAAK,QAAQ,IAAI,8BACf,IAAI,IAAI,QAAQ,IAAI,EACpB,iBACD;;CAGH,IAAI,UAAoC;AACtC,SAAO,KAAK,MAAM;;CAEpB,IAAI,QAAQ,SAAmC;AAC7C,OAAK,MAAM,UAAU;;CAGvB,IAAI,UAAgD;AAClD,SAAO,KAAK,MAAM;;CAEpB,IAAI,QAAQ,SAA+C;AACzD,OAAK,MAAM,UAAU;;CAGvB,IAAI,YAA6D;AAC/D,SAAO,KAAK,MAAM;;CAIpB,IAAI,UAAU,SAA0D;AACtE,OAAK,MAAM,YAAY;;CAKzB,QAAuB;AACrB,SAAO,KAAK,MAAM,OAAO;;CAG3B,KAAK,SAAwC;AAC3C,SAAO,KAAK,MAAM,KAAK,QAAiD;;CAG1E,QAAuB;AACrB,SAAO,KAAK,MAAM,OAAO;;;AAI7B,SAAS,kBAAkB,SAAmD;CAC5E,MAAM,EACJ,SAAS,eACT,YACA,gBACA,OACA,KAAK,cACL,MACA,SAAS,qBACP;CACJ,MAAM,YAAuB,kBAAkB,SAAS,WAAW;AAEnE,QAAO,OAAO,UAAU,SAAS;EAI/B,MAAM,SAAS,IAAI,QAAQ,MAAM,QAAQ;AACzC,MAAI,cACF,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,cAAc,CACtD,QAAO,IAAI,KAAK,MAAM;AAG1B,MAAI,YAAY;GACd,IAAI;AACJ,OAAI;AACF,eAAW,MAAM,WAAW;KAC1B;KACA;KACA;KACA;KACD,CAAC;YACK,KAAK;AACZ,UAAM,IAAI,uBACR,2BAA2B,aAAa,UACtC,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,IAElD,IACD;;AAEH,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,CACjD,QAAO,IAAI,KAAK,MAAM;;AAG1B,SAAO,UAAU,UAAU;GAAE,GAAG;GAAM,SAAS;GAAQ,CAAC"}
@@ -1,2 +0,0 @@
1
-
2
- import { CopilotKitIntelligence, CopilotKitIntelligenceConfig, CreateThreadRequest, ListThreadsResponse, SubscribeToThreadsRequest, SubscribeToThreadsResponse, ThreadSummary, UpdateThreadRequest } from "./client.cjs";
@@ -1,2 +0,0 @@
1
- import "reflect-metadata";
2
- import { CopilotKitIntelligence, CopilotKitIntelligenceConfig, CreateThreadRequest, ListThreadsResponse, SubscribeToThreadsRequest, SubscribeToThreadsResponse, ThreadSummary, UpdateThreadRequest } from "./client.mjs";
@@ -1,190 +0,0 @@
1
- import type { RunAgentInput } from "@ag-ui/client";
2
- import type { JSONRPCMessage, MCPTransport } from "@ai-sdk/mcp";
3
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
4
- import type { StreamableHTTPClientTransportOptions } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
5
- import type { FetchLike } from "@modelcontextprotocol/sdk/shared/transport.js";
6
-
7
- /**
8
- * The end-user identity resolved by the runtime's `identifyUser` callback for
9
- * the current request. Surfaced on {@link MCPRequestContext} so MCP header
10
- * resolvers can read it without re-doing auth work.
11
- */
12
- export interface MCPRuntimeUser {
13
- id: string;
14
- name: string;
15
- }
16
-
17
- /**
18
- * Context handed to {@link MCPClientConfigHTTP.getHeaders} on every outbound
19
- * MCP HTTP request. The resolver is invoked fresh per request — initialize,
20
- * tools/list, tools/call, and reconnects — so values it depends on are never
21
- * cached across calls.
22
- */
23
- export interface MCPRequestContext {
24
- /**
25
- * Headers forwarded onto the agent for this run. Populated by the runtime's
26
- * `extractForwardableHeaders` (`authorization` + every `x-*` header from the
27
- * incoming HTTP request). Keys are lower-cased.
28
- */
29
- requestHeaders: Record<string, string>;
30
- /** The {@link RunAgentInput} the agent is currently running. */
31
- input: RunAgentInput;
32
- /** URL of the MCP server this request is going to. */
33
- mcpServerUrl: string;
34
- /**
35
- * The end-user identity for this run, resolved by the runtime's
36
- * `identifyUser` callback (only populated when the agent is run via a
37
- * `CopilotRuntime` configured with `identifyUser`). Snapshotted at
38
- * run-start.
39
- */
40
- user?: MCPRuntimeUser;
41
- }
42
-
43
- /**
44
- * Thrown when an MCP {@link MCPClientConfigHTTP.getHeaders} resolver throws.
45
- * Wraps the underlying error so `RUN_ERROR` carries clear attribution instead
46
- * of a generic transport failure. The original error is preserved on the
47
- * standard ES2022 `Error.cause` chain.
48
- */
49
- export class MCPHeaderResolverError extends Error {
50
- constructor(message: string, cause: unknown) {
51
- super(message, { cause });
52
- this.name = "MCPHeaderResolverError";
53
- }
54
- }
55
-
56
- export interface CopilotKitMCPTransportOptions {
57
- /** URL of the MCP server. */
58
- url: string;
59
- /** Static HTTP headers, merged into every outbound request. */
60
- headers?: Record<string, string>;
61
- /**
62
- * Per-call header resolver. Invoked on **every** outbound HTTP request to
63
- * this server (initialize, tools/list, tools/call, reconnects). Returned
64
- * headers are merged on top of `headers`, so a resolver can override either.
65
- */
66
- getHeaders?: (
67
- ctx: MCPRequestContext,
68
- ) => Record<string, string> | Promise<Record<string, string>>;
69
- /**
70
- * Pre-existing escape hatch: low-level options for the underlying
71
- * `StreamableHTTPClientTransport`. Forwarded as-is, except `fetch` is
72
- * wrapped so static `headers` and `getHeaders` resolution still apply.
73
- */
74
- options?: StreamableHTTPClientTransportOptions;
75
- /** Snapshot of the agent's per-run forwarded headers, captured at run-start. */
76
- requestHeaders: Record<string, string>;
77
- /** RunAgentInput for the current run, exposed to the resolver via context. */
78
- input: RunAgentInput;
79
- /** Per-run end-user identity from the runtime's `identifyUser`, if set. */
80
- user?: MCPRuntimeUser;
81
- }
82
-
83
- /**
84
- * MCP transport for CopilotKit's BuiltInAgent that adds per-call header
85
- * resolution on top of the standard Streamable HTTP transport.
86
- *
87
- * Implements `@ai-sdk/mcp`'s {@link MCPTransport} interface so it can be
88
- * passed straight to `createMCPClient({ transport })`. Internally delegates
89
- * to `@modelcontextprotocol/sdk`'s `StreamableHTTPClientTransport` with a
90
- * wrapped `fetch` that runs the static-header + per-call resolver pipeline
91
- * before each outbound request.
92
- */
93
- export class CopilotKitMCPTransport implements MCPTransport {
94
- private readonly inner: StreamableHTTPClientTransport;
95
-
96
- constructor(options: CopilotKitMCPTransportOptions) {
97
- const transportOptions: StreamableHTTPClientTransportOptions = {
98
- ...options.options,
99
- fetch: buildWrappedFetch(options),
100
- };
101
- this.inner = new StreamableHTTPClientTransport(
102
- new URL(options.url),
103
- transportOptions,
104
- );
105
- }
106
-
107
- get onclose(): (() => void) | undefined {
108
- return this.inner.onclose;
109
- }
110
- set onclose(handler: (() => void) | undefined) {
111
- this.inner.onclose = handler;
112
- }
113
-
114
- get onerror(): ((error: Error) => void) | undefined {
115
- return this.inner.onerror;
116
- }
117
- set onerror(handler: ((error: Error) => void) | undefined) {
118
- this.inner.onerror = handler;
119
- }
120
-
121
- get onmessage(): ((message: JSONRPCMessage) => void) | undefined {
122
- return this.inner.onmessage as
123
- | ((message: JSONRPCMessage) => void)
124
- | undefined;
125
- }
126
- set onmessage(handler: ((message: JSONRPCMessage) => void) | undefined) {
127
- this.inner.onmessage = handler as
128
- | ((message: JSONRPCMessage) => void)
129
- | undefined;
130
- }
131
-
132
- start(): Promise<void> {
133
- return this.inner.start();
134
- }
135
-
136
- send(message: JSONRPCMessage): Promise<void> {
137
- return this.inner.send(message as Parameters<typeof this.inner.send>[0]);
138
- }
139
-
140
- close(): Promise<void> {
141
- return this.inner.close();
142
- }
143
- }
144
-
145
- function buildWrappedFetch(options: CopilotKitMCPTransportOptions): FetchLike {
146
- const {
147
- headers: staticHeaders,
148
- getHeaders,
149
- requestHeaders,
150
- input,
151
- url: mcpServerUrl,
152
- user,
153
- options: transportOptions,
154
- } = options;
155
- const baseFetch: FetchLike = transportOptions?.fetch ?? globalThis.fetch;
156
-
157
- return async (fetchUrl, init) => {
158
- // SDK passes a Headers instance via init.headers — start fresh from it so
159
- // we don't mutate the SDK's object, then layer headers on top via .set()
160
- // (last write wins).
161
- const merged = new Headers(init?.headers);
162
- if (staticHeaders) {
163
- for (const [key, value] of Object.entries(staticHeaders)) {
164
- merged.set(key, value);
165
- }
166
- }
167
- if (getHeaders) {
168
- let resolved: Record<string, string>;
169
- try {
170
- resolved = await getHeaders({
171
- requestHeaders,
172
- input,
173
- mcpServerUrl,
174
- user,
175
- });
176
- } catch (err) {
177
- throw new MCPHeaderResolverError(
178
- `MCP header resolver for ${mcpServerUrl} threw: ${
179
- err instanceof Error ? err.message : String(err)
180
- }`,
181
- err,
182
- );
183
- }
184
- for (const [key, value] of Object.entries(resolved)) {
185
- merged.set(key, value);
186
- }
187
- }
188
- return baseFetch(fetchUrl, { ...init, headers: merged });
189
- };
190
- }
@@ -1,190 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { CopilotKitIntelligence } from "../client";
3
- import { BasicAgent } from "../../../../agent";
4
- import { EventType } from "@ag-ui/client";
5
- import { LLMock, MCPMock } from "@copilotkit/aimock";
6
- import { streamText } from "ai";
7
- import {
8
- mockStreamTextResponse,
9
- textDelta,
10
- finish,
11
- collectEvents,
12
- } from "../../../../agent/__tests__/test-helpers";
13
-
14
- vi.mock("ai", () => ({
15
- streamText: vi.fn(),
16
- tool: vi.fn((config) => config),
17
- stepCountIs: vi.fn((count: number) => ({ type: "stepCount", count })),
18
- }));
19
-
20
- vi.mock("@ai-sdk/openai", () => ({
21
- createOpenAI: vi.fn(() => (modelId: string) => ({
22
- modelId,
23
- provider: "openai",
24
- })),
25
- }));
26
-
27
- async function startMcpMock(): Promise<{ url: string; server: LLMock }> {
28
- const mock = new MCPMock();
29
- mock.addTool({
30
- name: "bash",
31
- description: "Run a bash command",
32
- inputSchema: {
33
- type: "object",
34
- properties: { command: { type: "string" } },
35
- },
36
- });
37
- mock.onToolCall("bash", () => "ok");
38
- const server = new LLMock({ port: 0 });
39
- server.mount("/mcp", mock);
40
- await server.start();
41
- return { url: server.url, server };
42
- }
43
-
44
- /**
45
- * aimock redacts `Authorization` to `[REDACTED]` in its journal. Spy on
46
- * `globalThis.fetch` to read unredacted headers off each outbound request to
47
- * `mcpUrl`. The spy delegates to the real fetch so the round-trip completes.
48
- */
49
- function spyOnFetch(mcpUrl: string): {
50
- records: Array<Record<string, string>>;
51
- restore: () => void;
52
- } {
53
- const records: Array<Record<string, string>> = [];
54
- const realFetch = globalThis.fetch;
55
- const spy = vi
56
- .spyOn(globalThis, "fetch")
57
- .mockImplementation(async (input, init) => {
58
- const url =
59
- typeof input === "string"
60
- ? input
61
- : input instanceof URL
62
- ? input.toString()
63
- : input.url;
64
- if (url.startsWith(mcpUrl)) {
65
- const seen: Record<string, string> = {};
66
- new Headers(init?.headers ?? {}).forEach((value, key) => {
67
- seen[key.toLowerCase()] = value;
68
- });
69
- records.push(seen);
70
- }
71
- return realFetch(input, init);
72
- });
73
- return { records, restore: () => spy.mockRestore() };
74
- }
75
-
76
- describe("CopilotKitIntelligence.toMCPServer()", () => {
77
- const baseInput = {
78
- threadId: "thread1",
79
- runId: "run1",
80
- messages: [],
81
- tools: [],
82
- context: [],
83
- state: {},
84
- };
85
-
86
- let llm: LLMock | undefined;
87
- const originalEnv = process.env;
88
-
89
- beforeEach(() => {
90
- vi.clearAllMocks();
91
- process.env = { ...originalEnv };
92
- process.env.OPENAI_API_KEY = "test-key";
93
- });
94
-
95
- afterEach(async () => {
96
- process.env = originalEnv;
97
- if (llm) {
98
- await llm.stop().catch(() => {});
99
- llm = undefined;
100
- }
101
- });
102
-
103
- it("emits Authorization (Bearer apiKey) and X-Cpki-User-Id (resolved user) on every MCP request", async () => {
104
- const { url, server } = await startMcpMock();
105
- llm = server;
106
-
107
- const intelligence = new CopilotKitIntelligence({
108
- apiUrl: url,
109
- wsUrl: "wss://unused.example.com/socket",
110
- apiKey: "cpk-proj_short_long",
111
- });
112
-
113
- const recorder = spyOnFetch(url);
114
- try {
115
- const agent = new BasicAgent({
116
- model: "openai/gpt-4o",
117
- mcpServers: [intelligence.toMCPServer()],
118
- });
119
- // The runtime would normally populate this via `identifyUser` after
120
- // resolveIntelligenceUser. Simulate the same outcome here.
121
- agent.user = { id: "jordan-beamson", name: "Jordan Beamson" };
122
-
123
- vi.mocked(streamText).mockReturnValue(
124
- mockStreamTextResponse([textDelta("hi"), finish()]) as any,
125
- );
126
-
127
- await collectEvents(agent["run"](baseInput));
128
-
129
- expect(recorder.records.length).toBeGreaterThan(0);
130
- for (const headers of recorder.records) {
131
- expect(headers["authorization"]).toBe("Bearer cpk-proj_short_long");
132
- expect(headers["x-cpki-user-id"]).toBe("jordan-beamson");
133
- }
134
- } finally {
135
- recorder.restore();
136
- }
137
- });
138
-
139
- it("returned config has requiresUser=true so user-less sub-runs skip the server", () => {
140
- const intelligence = new CopilotKitIntelligence({
141
- apiUrl: "https://api.example.com",
142
- wsUrl: "wss://ws.example.com",
143
- apiKey: "k",
144
- });
145
-
146
- expect(intelligence.toMCPServer().requiresUser).toBe(true);
147
- });
148
-
149
- it("agent run with intelligence.toMCPServer() and no user completes without error (the thread-naming sub-run shape)", async () => {
150
- const { url, server } = await startMcpMock();
151
- llm = server;
152
-
153
- const intelligence = new CopilotKitIntelligence({
154
- apiUrl: url,
155
- wsUrl: "wss://unused.example.com/socket",
156
- apiKey: "cpk-proj_short_long",
157
- });
158
-
159
- const recorder = spyOnFetch(url);
160
- try {
161
- const agent = new BasicAgent({
162
- model: "openai/gpt-4o",
163
- mcpServers: [intelligence.toMCPServer()],
164
- });
165
- // No agent.user — mirrors the cloned naming sub-run.
166
-
167
- vi.mocked(streamText).mockReturnValue(
168
- mockStreamTextResponse([finish()]) as any,
169
- );
170
-
171
- const events = await collectEvents(agent["run"](baseInput));
172
-
173
- expect(
174
- events.find((e: any) => e.type === EventType.RUN_ERROR),
175
- ).toBeUndefined();
176
- expect(recorder.records.length).toBe(0);
177
- } finally {
178
- recorder.restore();
179
- }
180
- });
181
-
182
- it("URL is composed from apiUrl + /mcp (trailing slash on apiUrl is normalized)", () => {
183
- const intelligence = new CopilotKitIntelligence({
184
- apiUrl: "https://api.example.com/",
185
- wsUrl: "wss://ws.example.com",
186
- apiKey: "k",
187
- });
188
- expect(intelligence.toMCPServer().url).toBe("https://api.example.com/mcp");
189
- });
190
- });