toolcraft 0.0.5 → 0.0.7

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 (149) hide show
  1. package/README.md +1 -0
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.js +77 -59
  4. package/node_modules/@poe-code/agent-defs/dist/agents/claude-code.d.ts +2 -0
  5. package/node_modules/@poe-code/agent-defs/dist/agents/claude-code.js +15 -0
  6. package/node_modules/@poe-code/agent-defs/dist/agents/claude-desktop.d.ts +2 -0
  7. package/node_modules/@poe-code/agent-defs/dist/agents/claude-desktop.js +13 -0
  8. package/node_modules/@poe-code/agent-defs/dist/agents/codex.d.ts +2 -0
  9. package/node_modules/@poe-code/agent-defs/dist/agents/codex.js +14 -0
  10. package/node_modules/@poe-code/agent-defs/dist/agents/goose.d.ts +2 -0
  11. package/node_modules/@poe-code/agent-defs/dist/agents/goose.js +14 -0
  12. package/node_modules/@poe-code/agent-defs/dist/agents/index.d.ts +7 -0
  13. package/node_modules/@poe-code/agent-defs/dist/agents/index.js +7 -0
  14. package/node_modules/@poe-code/agent-defs/dist/agents/kimi.d.ts +2 -0
  15. package/node_modules/@poe-code/agent-defs/dist/agents/kimi.js +15 -0
  16. package/node_modules/@poe-code/agent-defs/dist/agents/opencode.d.ts +2 -0
  17. package/node_modules/@poe-code/agent-defs/dist/agents/opencode.js +14 -0
  18. package/node_modules/@poe-code/agent-defs/dist/agents/poe-agent.d.ts +2 -0
  19. package/node_modules/@poe-code/agent-defs/dist/agents/poe-agent.js +13 -0
  20. package/node_modules/@poe-code/agent-defs/dist/index.d.ts +5 -0
  21. package/node_modules/@poe-code/agent-defs/dist/index.js +3 -0
  22. package/node_modules/@poe-code/agent-defs/dist/registry.d.ts +3 -0
  23. package/node_modules/@poe-code/agent-defs/dist/registry.js +26 -0
  24. package/node_modules/@poe-code/agent-defs/dist/specifier.d.ts +7 -0
  25. package/node_modules/@poe-code/agent-defs/dist/specifier.js +27 -0
  26. package/node_modules/@poe-code/agent-defs/dist/types.d.ts +16 -0
  27. package/node_modules/@poe-code/agent-defs/dist/types.js +1 -0
  28. package/node_modules/@poe-code/agent-defs/package.json +20 -0
  29. package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.d.ts +5 -0
  30. package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +552 -0
  31. package/node_modules/@poe-code/config-mutations/dist/execution/path-utils.d.ts +17 -0
  32. package/node_modules/@poe-code/config-mutations/dist/execution/path-utils.js +58 -0
  33. package/node_modules/@poe-code/config-mutations/dist/execution/run-mutations.d.ts +7 -0
  34. package/node_modules/@poe-code/config-mutations/dist/execution/run-mutations.js +46 -0
  35. package/node_modules/@poe-code/config-mutations/dist/formats/index.d.ts +13 -0
  36. package/node_modules/@poe-code/config-mutations/dist/formats/index.js +49 -0
  37. package/node_modules/@poe-code/config-mutations/dist/formats/json.d.ts +31 -0
  38. package/node_modules/@poe-code/config-mutations/dist/formats/json.js +140 -0
  39. package/node_modules/@poe-code/config-mutations/dist/formats/toml.d.ts +2 -0
  40. package/node_modules/@poe-code/config-mutations/dist/formats/toml.js +72 -0
  41. package/node_modules/@poe-code/config-mutations/dist/formats/yaml.d.ts +2 -0
  42. package/node_modules/@poe-code/config-mutations/dist/formats/yaml.js +73 -0
  43. package/node_modules/@poe-code/config-mutations/dist/fs-utils.d.ts +18 -0
  44. package/node_modules/@poe-code/config-mutations/dist/fs-utils.js +45 -0
  45. package/node_modules/@poe-code/config-mutations/dist/index.d.ts +8 -0
  46. package/node_modules/@poe-code/config-mutations/dist/index.js +8 -0
  47. package/node_modules/@poe-code/config-mutations/dist/mutations/config-mutation.d.ts +47 -0
  48. package/node_modules/@poe-code/config-mutations/dist/mutations/config-mutation.js +34 -0
  49. package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.d.ts +52 -0
  50. package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.js +46 -0
  51. package/node_modules/@poe-code/config-mutations/dist/mutations/template-mutation.d.ts +40 -0
  52. package/node_modules/@poe-code/config-mutations/dist/mutations/template-mutation.js +32 -0
  53. package/node_modules/@poe-code/config-mutations/dist/template/render.d.ts +7 -0
  54. package/node_modules/@poe-code/config-mutations/dist/template/render.js +28 -0
  55. package/node_modules/@poe-code/config-mutations/dist/testing/format-utils.d.ts +7 -0
  56. package/node_modules/@poe-code/config-mutations/dist/testing/format-utils.js +21 -0
  57. package/node_modules/@poe-code/config-mutations/dist/testing/index.d.ts +3 -0
  58. package/node_modules/@poe-code/config-mutations/dist/testing/index.js +2 -0
  59. package/node_modules/@poe-code/config-mutations/dist/testing/mock-fs.d.ts +25 -0
  60. package/node_modules/@poe-code/config-mutations/dist/testing/mock-fs.js +170 -0
  61. package/node_modules/@poe-code/config-mutations/dist/types.d.ts +156 -0
  62. package/node_modules/@poe-code/config-mutations/dist/types.js +6 -0
  63. package/node_modules/@poe-code/config-mutations/package.json +33 -0
  64. package/node_modules/@poe-code/file-lock/README.md +52 -0
  65. package/node_modules/@poe-code/file-lock/dist/index.d.ts +1 -0
  66. package/node_modules/@poe-code/file-lock/dist/index.js +1 -0
  67. package/node_modules/@poe-code/file-lock/dist/lock.d.ts +27 -0
  68. package/node_modules/@poe-code/file-lock/dist/lock.js +203 -0
  69. package/node_modules/@poe-code/file-lock/package.json +23 -0
  70. package/node_modules/auth-store/README.md +47 -0
  71. package/node_modules/auth-store/dist/create-secret-store.d.ts +2 -0
  72. package/node_modules/auth-store/dist/create-secret-store.js +35 -0
  73. package/node_modules/auth-store/dist/encrypted-file-store.d.ts +39 -0
  74. package/node_modules/auth-store/dist/encrypted-file-store.js +156 -0
  75. package/node_modules/auth-store/dist/index.d.ts +7 -0
  76. package/node_modules/auth-store/dist/index.js +4 -0
  77. package/node_modules/auth-store/dist/keychain-store.d.ts +22 -0
  78. package/node_modules/auth-store/dist/keychain-store.js +111 -0
  79. package/node_modules/auth-store/dist/provider-store.d.ts +10 -0
  80. package/node_modules/auth-store/dist/provider-store.js +28 -0
  81. package/node_modules/auth-store/dist/types.d.ts +20 -0
  82. package/node_modules/auth-store/dist/types.js +1 -0
  83. package/node_modules/auth-store/package.json +25 -0
  84. package/node_modules/mcp-oauth/README.md +31 -0
  85. package/node_modules/mcp-oauth/dist/client/auth-store-session-store.d.ts +14 -0
  86. package/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +97 -0
  87. package/node_modules/mcp-oauth/dist/client/authorization-state.d.ts +8 -0
  88. package/node_modules/mcp-oauth/dist/client/authorization-state.js +34 -0
  89. package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.d.ts +3 -0
  90. package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +491 -0
  91. package/node_modules/mcp-oauth/dist/client/loopback-authorization.d.ts +20 -0
  92. package/node_modules/mcp-oauth/dist/client/loopback-authorization.js +169 -0
  93. package/node_modules/mcp-oauth/dist/client/pkce.d.ts +2 -0
  94. package/node_modules/mcp-oauth/dist/client/pkce.js +7 -0
  95. package/node_modules/mcp-oauth/dist/client/token-endpoint.d.ts +40 -0
  96. package/node_modules/mcp-oauth/dist/client/token-endpoint.js +143 -0
  97. package/node_modules/mcp-oauth/dist/client/types.d.ts +113 -0
  98. package/node_modules/mcp-oauth/dist/client/types.js +1 -0
  99. package/node_modules/mcp-oauth/dist/index.d.ts +10 -0
  100. package/node_modules/mcp-oauth/dist/index.js +7 -0
  101. package/node_modules/mcp-oauth/dist/resource-indicator.d.ts +1 -0
  102. package/node_modules/mcp-oauth/dist/resource-indicator.js +11 -0
  103. package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.d.ts +27 -0
  104. package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +259 -0
  105. package/node_modules/mcp-oauth/dist/types.compile-check.d.ts +1 -0
  106. package/node_modules/mcp-oauth/dist/types.compile-check.js +22 -0
  107. package/node_modules/mcp-oauth/package.json +31 -0
  108. package/node_modules/tiny-mcp-client/.turbo/turbo-build.log +4 -0
  109. package/node_modules/tiny-mcp-client/dist/index.d.ts +2 -0
  110. package/node_modules/tiny-mcp-client/dist/index.js +1 -0
  111. package/node_modules/tiny-mcp-client/dist/internal.d.ts +547 -0
  112. package/node_modules/tiny-mcp-client/dist/internal.js +2404 -0
  113. package/node_modules/tiny-mcp-client/dist/jsonrpc-types.compile-check.d.ts +1 -0
  114. package/node_modules/tiny-mcp-client/dist/jsonrpc-types.compile-check.js +37 -0
  115. package/node_modules/tiny-mcp-client/dist/mcp-lifecycle-types.compile-check.d.ts +1 -0
  116. package/node_modules/tiny-mcp-client/dist/mcp-lifecycle-types.compile-check.js +50 -0
  117. package/node_modules/tiny-mcp-client/dist/mcp-prompt-types.compile-check.d.ts +1 -0
  118. package/node_modules/tiny-mcp-client/dist/mcp-prompt-types.compile-check.js +50 -0
  119. package/node_modules/tiny-mcp-client/dist/mcp-resource-types.compile-check.d.ts +1 -0
  120. package/node_modules/tiny-mcp-client/dist/mcp-resource-types.compile-check.js +51 -0
  121. package/node_modules/tiny-mcp-client/dist/mcp-tool-types.compile-check.d.ts +1 -0
  122. package/node_modules/tiny-mcp-client/dist/mcp-tool-types.compile-check.js +89 -0
  123. package/node_modules/tiny-mcp-client/dist/mcp-transport-types.compile-check.d.ts +1 -0
  124. package/node_modules/tiny-mcp-client/dist/mcp-transport-types.compile-check.js +56 -0
  125. package/node_modules/tiny-mcp-client/dist/mcp-utility-types.compile-check.d.ts +1 -0
  126. package/node_modules/tiny-mcp-client/dist/mcp-utility-types.compile-check.js +145 -0
  127. package/node_modules/tiny-mcp-client/dist/oauth-discovery.d.ts +24 -0
  128. package/node_modules/tiny-mcp-client/dist/oauth-discovery.js +385 -0
  129. package/node_modules/tiny-mcp-client/package.json +22 -0
  130. package/node_modules/tiny-mcp-client/src/http-oauth.integration.test.ts +823 -0
  131. package/node_modules/tiny-mcp-client/src/http-oauth.test.ts +882 -0
  132. package/node_modules/tiny-mcp-client/src/index.ts +94 -0
  133. package/node_modules/tiny-mcp-client/src/internal.ts +3566 -0
  134. package/node_modules/tiny-mcp-client/src/jsonrpc-types.compile-check.ts +66 -0
  135. package/node_modules/tiny-mcp-client/src/mcp-client-http-transport.integration.test.ts +222 -0
  136. package/node_modules/tiny-mcp-client/src/mcp-client-sdk.test.ts +1294 -0
  137. package/node_modules/tiny-mcp-client/src/mcp-client-tiny-stdio-test-server-tools.test.ts +143 -0
  138. package/node_modules/tiny-mcp-client/src/mcp-lifecycle-types.compile-check.ts +65 -0
  139. package/node_modules/tiny-mcp-client/src/mcp-prompt-types.compile-check.ts +66 -0
  140. package/node_modules/tiny-mcp-client/src/mcp-resource-types.compile-check.ts +70 -0
  141. package/node_modules/tiny-mcp-client/src/mcp-tool-types.compile-check.ts +117 -0
  142. package/node_modules/tiny-mcp-client/src/mcp-transport-types.compile-check.ts +75 -0
  143. package/node_modules/tiny-mcp-client/src/mcp-utility-types.compile-check.ts +181 -0
  144. package/node_modules/tiny-mcp-client/src/mock-servers.test.ts +980 -0
  145. package/node_modules/tiny-mcp-client/src/oauth-discovery.ts +583 -0
  146. package/node_modules/tiny-mcp-client/src/transports.test.ts +8139 -0
  147. package/node_modules/tiny-mcp-client/src/utilities.test.ts +372 -0
  148. package/node_modules/tiny-mcp-client/tsconfig.json +11 -0
  149. package/package.json +24 -11
@@ -0,0 +1,980 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
3
+ import {
4
+ CreateMessageRequestSchema,
5
+ ErrorCode,
6
+ ListRootsRequestSchema,
7
+ LoggingMessageNotificationSchema,
8
+ ProgressNotificationSchema,
9
+ ResourceListChangedNotificationSchema,
10
+ ResourceUpdatedNotificationSchema,
11
+ } from "@modelcontextprotocol/sdk/types.js";
12
+ import { describe, expect, it } from "vitest";
13
+ import {
14
+ createMockCompletionServer,
15
+ createMockEchoToolServer,
16
+ createMockErrorServer,
17
+ createMockFullFeaturedServer,
18
+ createMockLoggingServer,
19
+ createMockMultiToolServer,
20
+ createMockPaginatedToolsServer,
21
+ createMockProgressServer,
22
+ createMockPromptServer,
23
+ createMockResourceServer,
24
+ createMockRootsServer,
25
+ createMockSamplingServer,
26
+ createMockSlowToolServer,
27
+ createMockSubscribableResourceServer,
28
+ } from "./internal.js";
29
+
30
+ const waitFor = async (predicate: () => boolean): Promise<void> => {
31
+ const timeoutAt = Date.now() + 1_000;
32
+
33
+ while (!predicate()) {
34
+ if (Date.now() >= timeoutAt) {
35
+ throw new Error("Timed out waiting");
36
+ }
37
+
38
+ await new Promise((resolve) => setTimeout(resolve, 5));
39
+ }
40
+ };
41
+
42
+ describe("createMockCompletionServer", () => {
43
+ it("declares completions capability and filters prompt completions", async () => {
44
+ const server = await createMockCompletionServer();
45
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
46
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
47
+ const serverPromise = server.connect(serverTransport);
48
+ await client.connect(clientTransport);
49
+
50
+ try {
51
+ expect(client.getServerCapabilities()?.completions).toBeDefined();
52
+
53
+ const result = await client.complete({
54
+ ref: {
55
+ type: "ref/prompt",
56
+ name: "code_review",
57
+ },
58
+ argument: {
59
+ name: "language",
60
+ value: "py",
61
+ },
62
+ });
63
+
64
+ expect(result.completion).toEqual({
65
+ values: ["python", "pydantic", "pytest"],
66
+ hasMore: true,
67
+ total: 5,
68
+ });
69
+ } finally {
70
+ await client.close();
71
+ await clientTransport.close();
72
+ await serverTransport.close();
73
+ await serverPromise;
74
+ }
75
+ });
76
+ });
77
+
78
+ describe("createMockEchoToolServer", () => {
79
+ it("responds to tools/list with the echo tool schema", async () => {
80
+ const server = await createMockEchoToolServer();
81
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
82
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
83
+ const serverPromise = server.connect(serverTransport);
84
+ await client.connect(clientTransport);
85
+
86
+ try {
87
+ const result = await client.listTools();
88
+
89
+ expect(result.tools).toHaveLength(1);
90
+ expect(result.tools[0]).toMatchObject({
91
+ name: "echo",
92
+ inputSchema: {
93
+ type: "object",
94
+ properties: {
95
+ message: {
96
+ type: "string",
97
+ },
98
+ },
99
+ required: ["message"],
100
+ },
101
+ });
102
+ } finally {
103
+ await client.close();
104
+ await clientTransport.close();
105
+ await serverTransport.close();
106
+ await serverPromise;
107
+ }
108
+ });
109
+
110
+ it("responds to tools/call with echoed message text", async () => {
111
+ const server = await createMockEchoToolServer();
112
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
113
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
114
+ const serverPromise = server.connect(serverTransport);
115
+ await client.connect(clientTransport);
116
+
117
+ try {
118
+ const result = await client.callTool({
119
+ name: "echo",
120
+ arguments: {
121
+ message: "hello from test",
122
+ },
123
+ });
124
+
125
+ expect(result).toMatchObject({
126
+ content: [{ type: "text", text: "hello from test" }],
127
+ });
128
+ } finally {
129
+ await client.close();
130
+ await clientTransport.close();
131
+ await serverTransport.close();
132
+ await serverPromise;
133
+ }
134
+ });
135
+ });
136
+
137
+ describe("createMockErrorServer", () => {
138
+ it("responds to tools/list with tools for invalid params, isError, and internal error", async () => {
139
+ const server = await createMockErrorServer();
140
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
141
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
142
+ const serverPromise = server.connect(serverTransport);
143
+ await client.connect(clientTransport);
144
+
145
+ try {
146
+ const result = await client.listTools();
147
+ expect(result.tools).toHaveLength(3);
148
+
149
+ const toolNames = result.tools.map((tool) => tool.name).sort();
150
+ expect(toolNames).toEqual(["internal_error", "invalid_params", "is_error"]);
151
+ } finally {
152
+ await client.close();
153
+ await clientTransport.close();
154
+ await serverTransport.close();
155
+ await serverPromise;
156
+ }
157
+ });
158
+
159
+ it("returns each error type from its corresponding tool", async () => {
160
+ const server = await createMockErrorServer();
161
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
162
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
163
+ const serverPromise = server.connect(serverTransport);
164
+ await client.connect(clientTransport);
165
+
166
+ try {
167
+ await expect(
168
+ client.callTool({
169
+ name: "invalid_params",
170
+ })
171
+ ).rejects.toMatchObject({
172
+ code: ErrorCode.InvalidParams,
173
+ message: expect.stringContaining("Intentional invalid params error"),
174
+ });
175
+
176
+ const toolErrorResult = await client.callTool({
177
+ name: "is_error",
178
+ });
179
+ expect(toolErrorResult).toMatchObject({
180
+ isError: true,
181
+ content: [{ type: "text", text: "Intentional isError tool failure." }],
182
+ });
183
+
184
+ await expect(
185
+ client.callTool({
186
+ name: "internal_error",
187
+ })
188
+ ).rejects.toMatchObject({
189
+ code: ErrorCode.InternalError,
190
+ message: expect.stringContaining("Intentional internal error"),
191
+ });
192
+ } finally {
193
+ await client.close();
194
+ await clientTransport.close();
195
+ await serverTransport.close();
196
+ await serverPromise;
197
+ }
198
+ });
199
+ });
200
+
201
+ describe("createMockFullFeaturedServer", () => {
202
+ it("initializes with all core capabilities and exposes tool/resource/prompt entries", async () => {
203
+ const server = await createMockFullFeaturedServer();
204
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
205
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
206
+ const serverPromise = server.connect(serverTransport);
207
+ await client.connect(clientTransport);
208
+
209
+ try {
210
+ expect(client.getServerCapabilities()).toMatchObject({
211
+ tools: {},
212
+ resources: {},
213
+ prompts: {},
214
+ logging: {},
215
+ completions: {},
216
+ });
217
+
218
+ const toolsResult = await client.listTools();
219
+ const resourcesResult = await client.listResources();
220
+ const promptsResult = await client.listPrompts();
221
+
222
+ expect(toolsResult.tools).toHaveLength(1);
223
+ expect(resourcesResult.resources).toHaveLength(1);
224
+ expect(promptsResult.prompts).toHaveLength(1);
225
+ } finally {
226
+ await client.close();
227
+ await clientTransport.close();
228
+ await serverTransport.close();
229
+ await serverPromise;
230
+ }
231
+ });
232
+ });
233
+
234
+ describe("createMockLoggingServer", () => {
235
+ it("accepts setLevel and emits filtered notifications/message logs from a tool call", async () => {
236
+ const server = await createMockLoggingServer();
237
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
238
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
239
+ const receivedLogs: Array<{ level: string; logger?: string; data: unknown }> = [];
240
+
241
+ client.setNotificationHandler(LoggingMessageNotificationSchema, (notification) => {
242
+ receivedLogs.push(notification.params);
243
+ });
244
+
245
+ const serverPromise = server.connect(serverTransport);
246
+ await client.connect(clientTransport);
247
+
248
+ try {
249
+ expect(client.getServerCapabilities()?.logging).toBeDefined();
250
+
251
+ await client.setLoggingLevel("info");
252
+
253
+ const result = await client.callTool({
254
+ name: "emit_logs",
255
+ });
256
+
257
+ expect(result).toMatchObject({
258
+ content: [{ type: "text", text: "Emitted log messages." }],
259
+ });
260
+
261
+ await waitFor(() => receivedLogs.length >= 2);
262
+
263
+ expect(receivedLogs).toEqual([
264
+ {
265
+ level: "info",
266
+ logger: "mock-logging-server",
267
+ data: {
268
+ message: "Info message",
269
+ },
270
+ },
271
+ {
272
+ level: "error",
273
+ logger: "mock-logging-server",
274
+ data: {
275
+ message: "Error message",
276
+ },
277
+ },
278
+ ]);
279
+ } finally {
280
+ await client.close();
281
+ await clientTransport.close();
282
+ await serverTransport.close();
283
+ await serverPromise;
284
+ }
285
+ });
286
+ });
287
+
288
+ describe("createMockMultiToolServer", () => {
289
+ it("responds to tools/list with add, greet, and fail schemas", async () => {
290
+ const server = await createMockMultiToolServer();
291
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
292
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
293
+ const serverPromise = server.connect(serverTransport);
294
+ await client.connect(clientTransport);
295
+
296
+ try {
297
+ const result = await client.listTools();
298
+ expect(result.tools).toHaveLength(3);
299
+
300
+ const toolsByName = new Map(result.tools.map((tool) => [tool.name, tool]));
301
+
302
+ expect(toolsByName.get("add")).toMatchObject({
303
+ name: "add",
304
+ inputSchema: {
305
+ type: "object",
306
+ properties: {
307
+ a: { type: "number" },
308
+ b: { type: "number" },
309
+ },
310
+ required: ["a", "b"],
311
+ },
312
+ });
313
+
314
+ expect(toolsByName.get("greet")).toMatchObject({
315
+ name: "greet",
316
+ inputSchema: {
317
+ type: "object",
318
+ properties: {
319
+ name: { type: "string" },
320
+ formal: { type: "boolean" },
321
+ },
322
+ required: ["name"],
323
+ },
324
+ });
325
+
326
+ expect(toolsByName.get("fail")).toMatchObject({
327
+ name: "fail",
328
+ inputSchema: {
329
+ type: "object",
330
+ properties: {},
331
+ },
332
+ });
333
+ } finally {
334
+ await client.close();
335
+ await clientTransport.close();
336
+ await serverTransport.close();
337
+ await serverPromise;
338
+ }
339
+ });
340
+
341
+ it("allows calling add, greet, and fail tools", async () => {
342
+ const server = await createMockMultiToolServer();
343
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
344
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
345
+ const serverPromise = server.connect(serverTransport);
346
+ await client.connect(clientTransport);
347
+
348
+ try {
349
+ const addResult = await client.callTool({
350
+ name: "add",
351
+ arguments: { a: 7, b: 5 },
352
+ });
353
+ expect(addResult).toMatchObject({
354
+ content: [{ type: "text", text: "12" }],
355
+ });
356
+
357
+ const greetResult = await client.callTool({
358
+ name: "greet",
359
+ arguments: { name: "Ada", formal: true },
360
+ });
361
+ expect(greetResult).toMatchObject({
362
+ content: [{ type: "text", text: "Good day, Ada." }],
363
+ });
364
+
365
+ const failResult = await client.callTool({
366
+ name: "fail",
367
+ });
368
+ expect(failResult).toMatchObject({
369
+ isError: true,
370
+ content: [{ type: "text", text: "Intentional tool failure." }],
371
+ });
372
+ } finally {
373
+ await client.close();
374
+ await clientTransport.close();
375
+ await serverTransport.close();
376
+ await serverPromise;
377
+ }
378
+ });
379
+ });
380
+
381
+ describe("createMockPaginatedToolsServer", () => {
382
+ it("returns 5 tools per page with nextCursor only on non-final pages", async () => {
383
+ const server = await createMockPaginatedToolsServer();
384
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
385
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
386
+ const serverPromise = server.connect(serverTransport);
387
+ await client.connect(clientTransport);
388
+
389
+ try {
390
+ const firstPage = await client.listTools();
391
+ expect(firstPage.tools).toHaveLength(5);
392
+ expect(firstPage.nextCursor).toBe("5");
393
+
394
+ const secondPage = await client.listTools({ cursor: firstPage.nextCursor });
395
+ expect(secondPage.tools).toHaveLength(5);
396
+ expect(secondPage.nextCursor).toBe("10");
397
+
398
+ const thirdPage = await client.listTools({ cursor: secondPage.nextCursor });
399
+ expect(thirdPage.tools).toHaveLength(5);
400
+ expect(thirdPage.nextCursor).toBe("15");
401
+
402
+ const fourthPage = await client.listTools({ cursor: thirdPage.nextCursor });
403
+ expect(fourthPage.tools).toHaveLength(5);
404
+ expect(fourthPage.nextCursor).toBeUndefined();
405
+ } finally {
406
+ await client.close();
407
+ await clientTransport.close();
408
+ await serverTransport.close();
409
+ await serverPromise;
410
+ }
411
+ });
412
+
413
+ it("iterates all pages and collects all 20 tools", async () => {
414
+ const server = await createMockPaginatedToolsServer();
415
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
416
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
417
+ const serverPromise = server.connect(serverTransport);
418
+ await client.connect(clientTransport);
419
+
420
+ try {
421
+ const collectedNames: string[] = [];
422
+ let cursor: string | undefined;
423
+
424
+ do {
425
+ const page =
426
+ cursor === undefined
427
+ ? await client.listTools()
428
+ : await client.listTools({ cursor });
429
+ collectedNames.push(...page.tools.map((tool) => tool.name));
430
+ cursor = page.nextCursor;
431
+ } while (cursor !== undefined);
432
+
433
+ expect(collectedNames).toHaveLength(20);
434
+ expect(new Set(collectedNames).size).toBe(20);
435
+ expect(collectedNames[0]).toBe("tool-1");
436
+ expect(collectedNames[19]).toBe("tool-20");
437
+ } finally {
438
+ await client.close();
439
+ await clientTransport.close();
440
+ await serverTransport.close();
441
+ await serverPromise;
442
+ }
443
+ });
444
+ });
445
+
446
+ describe("createMockProgressServer", () => {
447
+ it("sends 3+ progress notifications for slow_task before returning the final result", async () => {
448
+ const server = await createMockProgressServer();
449
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
450
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
451
+ const progressUpdates: Array<{
452
+ progressToken: string | number;
453
+ progress: number;
454
+ total?: number;
455
+ message?: string;
456
+ }> = [];
457
+
458
+ client.setNotificationHandler(ProgressNotificationSchema, (notification) => {
459
+ progressUpdates.push(notification.params);
460
+ });
461
+
462
+ const serverPromise = server.connect(serverTransport);
463
+ await client.connect(clientTransport);
464
+
465
+ try {
466
+ const progressToken = "slow-task-1";
467
+ const result = await client.callTool({
468
+ name: "slow_task",
469
+ _meta: {
470
+ progressToken,
471
+ },
472
+ });
473
+
474
+ expect(result).toMatchObject({
475
+ content: [{ type: "text", text: "slow_task complete" }],
476
+ });
477
+
478
+ expect(progressUpdates).toHaveLength(4);
479
+ expect(progressUpdates).toEqual([
480
+ {
481
+ progressToken,
482
+ progress: 1,
483
+ total: 4,
484
+ message: "Completed step 1 of 4",
485
+ },
486
+ {
487
+ progressToken,
488
+ progress: 2,
489
+ total: 4,
490
+ message: "Completed step 2 of 4",
491
+ },
492
+ {
493
+ progressToken,
494
+ progress: 3,
495
+ total: 4,
496
+ message: "Completed step 3 of 4",
497
+ },
498
+ {
499
+ progressToken,
500
+ progress: 4,
501
+ total: 4,
502
+ message: "Completed step 4 of 4",
503
+ },
504
+ ]);
505
+ } finally {
506
+ await client.close();
507
+ await clientTransport.close();
508
+ await serverTransport.close();
509
+ await serverPromise;
510
+ }
511
+ });
512
+ });
513
+
514
+ describe("createMockPromptServer", () => {
515
+ it("responds to prompts/list with code_review and summarize prompts", async () => {
516
+ const server = await createMockPromptServer();
517
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
518
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
519
+ const serverPromise = server.connect(serverTransport);
520
+ await client.connect(clientTransport);
521
+
522
+ try {
523
+ expect(client.getServerCapabilities()?.prompts).toBeDefined();
524
+
525
+ const result = await client.listPrompts();
526
+ expect(result.prompts).toHaveLength(2);
527
+
528
+ const promptsByName = new Map(result.prompts.map((prompt) => [prompt.name, prompt]));
529
+
530
+ expect(promptsByName.get("code_review")).toMatchObject({
531
+ name: "code_review",
532
+ arguments: [
533
+ {
534
+ name: "code",
535
+ required: true,
536
+ },
537
+ ],
538
+ });
539
+ expect(promptsByName.get("summarize")).toMatchObject({
540
+ name: "summarize",
541
+ });
542
+ expect(promptsByName.get("summarize")?.arguments).toBeUndefined();
543
+ } finally {
544
+ await client.close();
545
+ await clientTransport.close();
546
+ await serverTransport.close();
547
+ await serverPromise;
548
+ }
549
+ });
550
+
551
+ it("responds to prompts/get with expanded messages from arguments", async () => {
552
+ const server = await createMockPromptServer();
553
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
554
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
555
+ const serverPromise = server.connect(serverTransport);
556
+ await client.connect(clientTransport);
557
+
558
+ try {
559
+ const result = await client.getPrompt({
560
+ name: "code_review",
561
+ arguments: {
562
+ code: "const answer = 42;",
563
+ },
564
+ });
565
+
566
+ expect(result).toMatchObject({
567
+ description: "Review code for correctness and maintainability.",
568
+ messages: [
569
+ {
570
+ role: "user",
571
+ content: {
572
+ type: "text",
573
+ text: "Please review the following code:\nconst answer = 42;",
574
+ },
575
+ },
576
+ {
577
+ role: "assistant",
578
+ content: {
579
+ type: "text",
580
+ text: "I will review the code for potential issues and improvements.",
581
+ },
582
+ },
583
+ ],
584
+ });
585
+ } finally {
586
+ await client.close();
587
+ await clientTransport.close();
588
+ await serverTransport.close();
589
+ await serverPromise;
590
+ }
591
+ });
592
+
593
+ it("returns invalid params for a nonexistent prompt name", async () => {
594
+ const server = await createMockPromptServer();
595
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
596
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
597
+ const serverPromise = server.connect(serverTransport);
598
+ await client.connect(clientTransport);
599
+
600
+ try {
601
+ await expect(
602
+ client.getPrompt({
603
+ name: "unknown_prompt",
604
+ })
605
+ ).rejects.toMatchObject({
606
+ code: ErrorCode.InvalidParams,
607
+ message: expect.stringContaining("Unknown prompt: unknown_prompt"),
608
+ });
609
+ } finally {
610
+ await client.close();
611
+ await clientTransport.close();
612
+ await serverTransport.close();
613
+ await serverPromise;
614
+ }
615
+ });
616
+
617
+ it("returns invalid params when a required prompt argument is missing", async () => {
618
+ const server = await createMockPromptServer();
619
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
620
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
621
+ const serverPromise = server.connect(serverTransport);
622
+ await client.connect(clientTransport);
623
+
624
+ try {
625
+ await expect(
626
+ client.getPrompt({
627
+ name: "code_review",
628
+ })
629
+ ).rejects.toMatchObject({
630
+ code: ErrorCode.InvalidParams,
631
+ message: expect.stringContaining("Missing required prompt argument: code"),
632
+ });
633
+ } finally {
634
+ await client.close();
635
+ await clientTransport.close();
636
+ await serverTransport.close();
637
+ await serverPromise;
638
+ }
639
+ });
640
+ });
641
+
642
+ describe("createMockResourceServer", () => {
643
+ it("responds to resources/list with both static resources", async () => {
644
+ const server = await createMockResourceServer();
645
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
646
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
647
+ const serverPromise = server.connect(serverTransport);
648
+ await client.connect(clientTransport);
649
+
650
+ try {
651
+ const result = await client.listResources();
652
+
653
+ expect(result.resources).toHaveLength(2);
654
+
655
+ const resourcesByUri = new Map(result.resources.map((resource) => [resource.uri, resource]));
656
+ expect(resourcesByUri.get("file:///readme.txt")).toMatchObject({
657
+ uri: "file:///readme.txt",
658
+ name: "readme.txt",
659
+ mimeType: "text/plain",
660
+ });
661
+ expect(resourcesByUri.get("file:///image.png")).toMatchObject({
662
+ uri: "file:///image.png",
663
+ name: "image.png",
664
+ mimeType: "image/png",
665
+ });
666
+ } finally {
667
+ await client.close();
668
+ await clientTransport.close();
669
+ await serverTransport.close();
670
+ await serverPromise;
671
+ }
672
+ });
673
+
674
+ it("responds to resources/read for text resource", async () => {
675
+ const server = await createMockResourceServer();
676
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
677
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
678
+ const serverPromise = server.connect(serverTransport);
679
+ await client.connect(clientTransport);
680
+
681
+ try {
682
+ const result = await client.readResource({ uri: "file:///readme.txt" });
683
+
684
+ expect(result.contents).toEqual([
685
+ {
686
+ uri: "file:///readme.txt",
687
+ mimeType: "text/plain",
688
+ text: "This is a mock README resource.",
689
+ },
690
+ ]);
691
+ } finally {
692
+ await client.close();
693
+ await clientTransport.close();
694
+ await serverTransport.close();
695
+ await serverPromise;
696
+ }
697
+ });
698
+
699
+ it("responds to resources/read for binary resource", async () => {
700
+ const server = await createMockResourceServer();
701
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
702
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
703
+ const serverPromise = server.connect(serverTransport);
704
+ await client.connect(clientTransport);
705
+
706
+ try {
707
+ const result = await client.readResource({ uri: "file:///image.png" });
708
+
709
+ expect(result.contents).toEqual([
710
+ {
711
+ uri: "file:///image.png",
712
+ mimeType: "image/png",
713
+ blob: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8Xw8AAoMBgL9qj3QAAAAASUVORK5CYII=",
714
+ },
715
+ ]);
716
+ } finally {
717
+ await client.close();
718
+ await clientTransport.close();
719
+ await serverTransport.close();
720
+ await serverPromise;
721
+ }
722
+ });
723
+
724
+ it("responds to resources/templates/list with file template", async () => {
725
+ const server = await createMockResourceServer();
726
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
727
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
728
+ const serverPromise = server.connect(serverTransport);
729
+ await client.connect(clientTransport);
730
+
731
+ try {
732
+ const result = await client.listResourceTemplates();
733
+
734
+ expect(result.resourceTemplates).toEqual([
735
+ {
736
+ uriTemplate: "file:///{path}",
737
+ name: "file-template",
738
+ },
739
+ ]);
740
+ } finally {
741
+ await client.close();
742
+ await clientTransport.close();
743
+ await serverTransport.close();
744
+ await serverPromise;
745
+ }
746
+ });
747
+ });
748
+
749
+ describe("createMockRootsServer", () => {
750
+ it("requests roots/list during tool call and uses returned roots in the result", async () => {
751
+ const server = await createMockRootsServer();
752
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
753
+ const client = new Client(
754
+ { name: "test-client", version: "1.0.0" },
755
+ { capabilities: { roots: {} } }
756
+ );
757
+ let rootsListRequestCount = 0;
758
+
759
+ client.setRequestHandler(ListRootsRequestSchema, async () => {
760
+ rootsListRequestCount += 1;
761
+
762
+ return {
763
+ roots: [
764
+ {
765
+ uri: "file:///workspace",
766
+ name: "workspace",
767
+ },
768
+ {
769
+ uri: "file:///workspace/docs",
770
+ },
771
+ ],
772
+ };
773
+ });
774
+
775
+ const serverPromise = server.connect(serverTransport);
776
+ await client.connect(clientTransport);
777
+
778
+ try {
779
+ const result = await client.callTool({
780
+ name: "roots_summary",
781
+ });
782
+
783
+ expect(rootsListRequestCount).toBe(1);
784
+ expect(result).toMatchObject({
785
+ content: [
786
+ {
787
+ type: "text",
788
+ text: "Roots: workspace (file:///workspace), file:///workspace/docs",
789
+ },
790
+ ],
791
+ });
792
+ } finally {
793
+ await client.close();
794
+ await clientTransport.close();
795
+ await serverTransport.close();
796
+ await serverPromise;
797
+ }
798
+ });
799
+ });
800
+
801
+ describe("createMockSamplingServer", () => {
802
+ it("calls sampling/createMessage and uses the client response in tool output", async () => {
803
+ const server = await createMockSamplingServer();
804
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
805
+ const client = new Client(
806
+ { name: "test-client", version: "1.0.0" },
807
+ { capabilities: { sampling: {} } }
808
+ );
809
+ let receivedSamplingParams: unknown;
810
+
811
+ client.setRequestHandler(CreateMessageRequestSchema, async (request) => {
812
+ receivedSamplingParams = request.params;
813
+
814
+ return {
815
+ model: "test-model",
816
+ role: "assistant",
817
+ stopReason: "endTurn",
818
+ content: {
819
+ type: "text",
820
+ text: "TypeScript adds types to JavaScript.",
821
+ },
822
+ };
823
+ });
824
+
825
+ const serverPromise = server.connect(serverTransport);
826
+ await client.connect(clientTransport);
827
+
828
+ try {
829
+ const result = await client.callTool({
830
+ name: "sample_message",
831
+ arguments: {
832
+ topic: "TypeScript",
833
+ },
834
+ });
835
+
836
+ expect(receivedSamplingParams).toMatchObject({
837
+ messages: [
838
+ {
839
+ role: "user",
840
+ content: {
841
+ type: "text",
842
+ text: "Provide a concise sentence about TypeScript.",
843
+ },
844
+ },
845
+ ],
846
+ maxTokens: 64,
847
+ modelPreferences: {
848
+ hints: [{ name: "mock-sampling-model" }],
849
+ speedPriority: 0.2,
850
+ intelligencePriority: 0.9,
851
+ },
852
+ systemPrompt: "Return exactly one concise sentence.",
853
+ });
854
+ expect(result).toMatchObject({
855
+ content: [
856
+ {
857
+ type: "text",
858
+ text: "Sampled response: TypeScript adds types to JavaScript.",
859
+ },
860
+ ],
861
+ });
862
+ } finally {
863
+ await client.close();
864
+ await clientTransport.close();
865
+ await serverTransport.close();
866
+ await serverPromise;
867
+ }
868
+ });
869
+ });
870
+
871
+ describe("createMockSlowToolServer", () => {
872
+ it("delays the slow tool response by configurable duration", async () => {
873
+ const server = await createMockSlowToolServer({ delayMs: 15, pollIntervalMs: 2 });
874
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
875
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
876
+ const serverPromise = server.connect(serverTransport);
877
+ await client.connect(clientTransport);
878
+
879
+ try {
880
+ const result = await client.callTool({
881
+ name: "slow",
882
+ arguments: {
883
+ delayMs: 15,
884
+ },
885
+ });
886
+
887
+ expect(result).toMatchObject({
888
+ content: [{ type: "text", text: "slow complete after 15ms" }],
889
+ });
890
+ } finally {
891
+ await client.close();
892
+ await clientTransport.close();
893
+ await serverTransport.close();
894
+ await serverPromise;
895
+ }
896
+ });
897
+
898
+ it("stops processing after notifications/cancelled and records cancellation", async () => {
899
+ const server = await createMockSlowToolServer({ delayMs: 1_000, pollIntervalMs: 5 });
900
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
901
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
902
+ const abortController = new AbortController();
903
+ const serverPromise = server.connect(serverTransport);
904
+ await client.connect(clientTransport);
905
+
906
+ try {
907
+ await client.listTools();
908
+
909
+ const callPromise = client.callTool(
910
+ {
911
+ name: "slow",
912
+ arguments: {
913
+ delayMs: 50,
914
+ },
915
+ },
916
+ undefined,
917
+ { signal: abortController.signal }
918
+ );
919
+
920
+ await waitFor(() => server.wasStarted());
921
+ abortController.abort("test cancellation");
922
+
923
+ await expect(callPromise).rejects.toThrow();
924
+ await waitFor(() => server.wasCancelled());
925
+ expect(server.wasCancelled()).toBe(true);
926
+ expect(server.getCancelledRequestIds()).toHaveLength(1);
927
+ } finally {
928
+ await client.close();
929
+ await clientTransport.close();
930
+ await serverTransport.close();
931
+ await serverPromise;
932
+ }
933
+ });
934
+ });
935
+
936
+ describe("createMockSubscribableResourceServer", () => {
937
+ it("accepts subscriptions and sends update/list_changed notifications", async () => {
938
+ const server = await createMockSubscribableResourceServer();
939
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
940
+ const client = new Client({ name: "test-client", version: "1.0.0" }, {});
941
+ const updatedUris: string[] = [];
942
+ let listChangedCount = 0;
943
+
944
+ client.setNotificationHandler(ResourceUpdatedNotificationSchema, (notification) => {
945
+ updatedUris.push(notification.params.uri);
946
+ });
947
+ client.setNotificationHandler(ResourceListChangedNotificationSchema, () => {
948
+ listChangedCount += 1;
949
+ });
950
+
951
+ const serverPromise = server.connect(serverTransport);
952
+ await client.connect(clientTransport);
953
+
954
+ try {
955
+ expect(client.getServerCapabilities()?.resources).toMatchObject({
956
+ subscribe: true,
957
+ listChanged: true,
958
+ });
959
+
960
+ await client.subscribeResource({ uri: "file:///readme.txt" });
961
+
962
+ await server.triggerResourceUpdated("file:///image.png");
963
+ await new Promise((resolve) => setTimeout(resolve, 20));
964
+ expect(updatedUris).toEqual([]);
965
+
966
+ await server.triggerResourceUpdated("file:///readme.txt");
967
+ await waitFor(() => updatedUris.includes("file:///readme.txt"));
968
+ expect(updatedUris).toEqual(["file:///readme.txt"]);
969
+
970
+ await server.triggerResourceListChanged();
971
+ await waitFor(() => listChangedCount === 1);
972
+ expect(listChangedCount).toBe(1);
973
+ } finally {
974
+ await client.close();
975
+ await clientTransport.close();
976
+ await serverTransport.close();
977
+ await serverPromise;
978
+ }
979
+ });
980
+ });