@copilotkit/runtime 1.56.4-canary.1777538870 → 1.56.5

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 (74) hide show
  1. package/dist/agent/converters/tanstack.cjs +6 -0
  2. package/dist/agent/converters/tanstack.cjs.map +1 -1
  3. package/dist/agent/converters/tanstack.mjs +6 -0
  4. package/dist/agent/converters/tanstack.mjs.map +1 -1
  5. package/dist/agent/index.cjs +8 -37
  6. package/dist/agent/index.cjs.map +1 -1
  7. package/dist/agent/index.d.cts +27 -52
  8. package/dist/agent/index.d.cts.map +1 -1
  9. package/dist/agent/index.d.mts +27 -52
  10. package/dist/agent/index.d.mts.map +1 -1
  11. package/dist/agent/index.mjs +9 -38
  12. package/dist/agent/index.mjs.map +1 -1
  13. package/dist/lib/runtime/agent-integrations/langgraph/agent.cjs +8 -1
  14. package/dist/lib/runtime/agent-integrations/langgraph/agent.cjs.map +1 -1
  15. package/dist/lib/runtime/agent-integrations/langgraph/agent.d.cts.map +1 -1
  16. package/dist/lib/runtime/agent-integrations/langgraph/agent.d.mts.map +1 -1
  17. package/dist/lib/runtime/agent-integrations/langgraph/agent.mjs +8 -1
  18. package/dist/lib/runtime/agent-integrations/langgraph/agent.mjs.map +1 -1
  19. package/dist/package.cjs +5 -5
  20. package/dist/package.mjs +5 -5
  21. package/dist/v2/index.cjs +0 -2
  22. package/dist/v2/index.d.cts +5 -6
  23. package/dist/v2/index.d.mts +5 -6
  24. package/dist/v2/index.mjs +1 -2
  25. package/dist/v2/runtime/core/runtime.d.cts +0 -1
  26. package/dist/v2/runtime/core/runtime.d.cts.map +1 -1
  27. package/dist/v2/runtime/core/runtime.d.mts +0 -1
  28. package/dist/v2/runtime/core/runtime.d.mts.map +1 -1
  29. package/dist/v2/runtime/endpoints/express.cjs +5 -5
  30. package/dist/v2/runtime/endpoints/express.cjs.map +1 -1
  31. package/dist/v2/runtime/endpoints/express.mjs +5 -5
  32. package/dist/v2/runtime/endpoints/express.mjs.map +1 -1
  33. package/dist/v2/runtime/handlers/intelligence/run.cjs +0 -4
  34. package/dist/v2/runtime/handlers/intelligence/run.cjs.map +1 -1
  35. package/dist/v2/runtime/handlers/intelligence/run.mjs +0 -4
  36. package/dist/v2/runtime/handlers/intelligence/run.mjs.map +1 -1
  37. package/dist/v2/runtime/handlers/shared/agent-utils.cjs.map +1 -1
  38. package/dist/v2/runtime/handlers/shared/agent-utils.mjs.map +1 -1
  39. package/dist/v2/runtime/index.d.cts +1 -3
  40. package/dist/v2/runtime/index.d.cts.map +1 -1
  41. package/dist/v2/runtime/index.d.mts +1 -3
  42. package/dist/v2/runtime/index.d.mts.map +1 -1
  43. package/dist/v2/runtime/intelligence-platform/client.cjs +0 -52
  44. package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
  45. package/dist/v2/runtime/intelligence-platform/client.d.cts +0 -41
  46. package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
  47. package/dist/v2/runtime/intelligence-platform/client.d.mts +0 -41
  48. package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
  49. package/dist/v2/runtime/intelligence-platform/client.mjs +0 -52
  50. package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
  51. package/package.json +6 -6
  52. package/src/agent/__tests__/mcp-clients.test.ts +25 -11
  53. package/src/agent/__tests__/mcp-servers-integration.test.ts +1 -355
  54. package/src/agent/converters/tanstack.ts +18 -0
  55. package/src/agent/index.ts +65 -128
  56. package/src/lib/runtime/agent-integrations/langgraph/agent.ts +8 -1
  57. package/src/v2/runtime/__tests__/express-fetch-bridge.test.ts +1 -1
  58. package/src/v2/runtime/endpoints/express.ts +9 -3
  59. package/src/v2/runtime/handlers/intelligence/run.ts +0 -9
  60. package/src/v2/runtime/handlers/shared/agent-utils.ts +0 -1
  61. package/src/v2/runtime/index.ts +0 -5
  62. package/src/v2/runtime/intelligence-platform/client.ts +0 -67
  63. package/dist/agent/mcp-transport.cjs +0 -94
  64. package/dist/agent/mcp-transport.cjs.map +0 -1
  65. package/dist/agent/mcp-transport.d.cts +0 -51
  66. package/dist/agent/mcp-transport.d.cts.map +0 -1
  67. package/dist/agent/mcp-transport.d.mts +0 -52
  68. package/dist/agent/mcp-transport.d.mts.map +0 -1
  69. package/dist/agent/mcp-transport.mjs +0 -92
  70. package/dist/agent/mcp-transport.mjs.map +0 -1
  71. package/dist/v2/runtime/intelligence-platform/index.d.cts +0 -2
  72. package/dist/v2/runtime/intelligence-platform/index.d.mts +0 -2
  73. package/src/agent/mcp-transport.ts +0 -190
  74. package/src/v2/runtime/intelligence-platform/__tests__/intelligence-mcp-helper.test.ts +0 -188
@@ -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,188 +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("throws when no user has been resolved (runtime missing identifyUser)", async () => {
140
- const { url, server } = await startMcpMock();
141
- llm = server;
142
-
143
- const intelligence = new CopilotKitIntelligence({
144
- apiUrl: url,
145
- wsUrl: "wss://unused.example.com/socket",
146
- apiKey: "cpk-proj_short_long",
147
- });
148
-
149
- const agent = new BasicAgent({
150
- model: "openai/gpt-4o",
151
- mcpServers: [intelligence.toMCPServer()],
152
- });
153
- // Deliberately leave `agent.user` unset — this is the case the helper
154
- // must reject loudly so a misconfigured runtime doesn't silently collapse
155
- // every browser session into one shared bash sandbox.
156
-
157
- vi.mocked(streamText).mockReturnValue(
158
- mockStreamTextResponse([finish()]) as any,
159
- );
160
-
161
- const events: any[] = [];
162
- try {
163
- await new Promise((resolve, reject) => {
164
- agent["run"](baseInput).subscribe({
165
- next: (event) => events.push(event),
166
- error: (err) => reject(err),
167
- complete: () => resolve(events),
168
- });
169
- });
170
- } catch {
171
- // expected
172
- }
173
-
174
- const runError = events.find((e: any) => e.type === EventType.RUN_ERROR);
175
- expect(runError).toBeDefined();
176
- expect(runError?.message).toContain("no user resolved");
177
- expect(runError?.message).toContain("identifyUser");
178
- });
179
-
180
- it("URL is composed from apiUrl + /mcp (trailing slash on apiUrl is normalized)", () => {
181
- const intelligence = new CopilotKitIntelligence({
182
- apiUrl: "https://api.example.com/",
183
- wsUrl: "wss://ws.example.com",
184
- apiKey: "k",
185
- });
186
- expect(intelligence.toMCPServer().url).toBe("https://api.example.com/mcp");
187
- });
188
- });