glm-acp-agent 1.0.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 (102) hide show
  1. package/README.md +365 -0
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +62 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/llm/credentials.d.ts +15 -0
  7. package/dist/llm/credentials.d.ts.map +1 -0
  8. package/dist/llm/credentials.js +54 -0
  9. package/dist/llm/credentials.js.map +1 -0
  10. package/dist/llm/glm-client.d.ts +63 -0
  11. package/dist/llm/glm-client.d.ts.map +1 -0
  12. package/dist/llm/glm-client.js +217 -0
  13. package/dist/llm/glm-client.js.map +1 -0
  14. package/dist/llm/logger.d.ts +19 -0
  15. package/dist/llm/logger.d.ts.map +1 -0
  16. package/dist/llm/logger.js +44 -0
  17. package/dist/llm/logger.js.map +1 -0
  18. package/dist/protocol/agent.d.ts +77 -0
  19. package/dist/protocol/agent.d.ts.map +1 -0
  20. package/dist/protocol/agent.js +732 -0
  21. package/dist/protocol/agent.js.map +1 -0
  22. package/dist/protocol/connection.d.ts +9 -0
  23. package/dist/protocol/connection.d.ts.map +1 -0
  24. package/dist/protocol/connection.js +18 -0
  25. package/dist/protocol/connection.js.map +1 -0
  26. package/dist/protocol/image-preprocessor.d.ts +32 -0
  27. package/dist/protocol/image-preprocessor.d.ts.map +1 -0
  28. package/dist/protocol/image-preprocessor.js +149 -0
  29. package/dist/protocol/image-preprocessor.js.map +1 -0
  30. package/dist/protocol/session-store.d.ts +54 -0
  31. package/dist/protocol/session-store.d.ts.map +1 -0
  32. package/dist/protocol/session-store.js +109 -0
  33. package/dist/protocol/session-store.js.map +1 -0
  34. package/dist/protocol/system-prompt.d.ts +29 -0
  35. package/dist/protocol/system-prompt.d.ts.map +1 -0
  36. package/dist/protocol/system-prompt.js +103 -0
  37. package/dist/protocol/system-prompt.js.map +1 -0
  38. package/dist/setup.d.ts +8 -0
  39. package/dist/setup.d.ts.map +1 -0
  40. package/dist/setup.js +95 -0
  41. package/dist/setup.js.map +1 -0
  42. package/dist/tests/agent.test.d.ts +2 -0
  43. package/dist/tests/agent.test.d.ts.map +1 -0
  44. package/dist/tests/agent.test.js +1118 -0
  45. package/dist/tests/agent.test.js.map +1 -0
  46. package/dist/tests/credentials.test.d.ts +2 -0
  47. package/dist/tests/credentials.test.d.ts.map +1 -0
  48. package/dist/tests/credentials.test.js +112 -0
  49. package/dist/tests/credentials.test.js.map +1 -0
  50. package/dist/tests/executor.test.d.ts +2 -0
  51. package/dist/tests/executor.test.d.ts.map +1 -0
  52. package/dist/tests/executor.test.js +497 -0
  53. package/dist/tests/executor.test.js.map +1 -0
  54. package/dist/tests/glm-client.test.d.ts +2 -0
  55. package/dist/tests/glm-client.test.d.ts.map +1 -0
  56. package/dist/tests/glm-client.test.js +205 -0
  57. package/dist/tests/glm-client.test.js.map +1 -0
  58. package/dist/tests/image-preprocessor.test.d.ts +2 -0
  59. package/dist/tests/image-preprocessor.test.d.ts.map +1 -0
  60. package/dist/tests/image-preprocessor.test.js +133 -0
  61. package/dist/tests/image-preprocessor.test.js.map +1 -0
  62. package/dist/tests/integration.test.d.ts +2 -0
  63. package/dist/tests/integration.test.d.ts.map +1 -0
  64. package/dist/tests/integration.test.js +192 -0
  65. package/dist/tests/integration.test.js.map +1 -0
  66. package/dist/tests/logger.test.d.ts +2 -0
  67. package/dist/tests/logger.test.d.ts.map +1 -0
  68. package/dist/tests/logger.test.js +14 -0
  69. package/dist/tests/logger.test.js.map +1 -0
  70. package/dist/tests/mcp-arg-remap.test.d.ts +2 -0
  71. package/dist/tests/mcp-arg-remap.test.d.ts.map +1 -0
  72. package/dist/tests/mcp-arg-remap.test.js +65 -0
  73. package/dist/tests/mcp-arg-remap.test.js.map +1 -0
  74. package/dist/tests/vision-mcp-client.test.d.ts +2 -0
  75. package/dist/tests/vision-mcp-client.test.d.ts.map +1 -0
  76. package/dist/tests/vision-mcp-client.test.js +129 -0
  77. package/dist/tests/vision-mcp-client.test.js.map +1 -0
  78. package/dist/tests/zai-mcp-client.test.d.ts +2 -0
  79. package/dist/tests/zai-mcp-client.test.d.ts.map +1 -0
  80. package/dist/tests/zai-mcp-client.test.js +386 -0
  81. package/dist/tests/zai-mcp-client.test.js.map +1 -0
  82. package/dist/tools/definitions.d.ts +18 -0
  83. package/dist/tools/definitions.d.ts.map +1 -0
  84. package/dist/tools/definitions.js +149 -0
  85. package/dist/tools/definitions.js.map +1 -0
  86. package/dist/tools/executor.d.ts +48 -0
  87. package/dist/tools/executor.d.ts.map +1 -0
  88. package/dist/tools/executor.js +670 -0
  89. package/dist/tools/executor.js.map +1 -0
  90. package/dist/tools/mcp-arg-remap.d.ts +21 -0
  91. package/dist/tools/mcp-arg-remap.d.ts.map +1 -0
  92. package/dist/tools/mcp-arg-remap.js +70 -0
  93. package/dist/tools/mcp-arg-remap.js.map +1 -0
  94. package/dist/tools/vision-mcp-client.d.ts +38 -0
  95. package/dist/tools/vision-mcp-client.d.ts.map +1 -0
  96. package/dist/tools/vision-mcp-client.js +191 -0
  97. package/dist/tools/vision-mcp-client.js.map +1 -0
  98. package/dist/tools/zai-mcp-client.d.ts +24 -0
  99. package/dist/tools/zai-mcp-client.d.ts.map +1 -0
  100. package/dist/tools/zai-mcp-client.js +189 -0
  101. package/dist/tools/zai-mcp-client.js.map +1 -0
  102. package/package.json +37 -0
@@ -0,0 +1,65 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { remapArguments, resolveToolName } from "../tools/mcp-arg-remap.js";
4
+ // ---------------------------------------------------------------------------
5
+ // remapArguments
6
+ // ---------------------------------------------------------------------------
7
+ test("remapArguments: passes through unchanged when targetProperties is empty (no schema)", () => {
8
+ const args = { query: "hello", count: 5 };
9
+ const result = remapArguments(args, []);
10
+ assert.deepEqual(result, { query: "hello", count: 5 });
11
+ });
12
+ test("remapArguments: passes through when key already matches a target property", () => {
13
+ const result = remapArguments({ search_query: "hello" }, ["search_query", "count"]);
14
+ assert.deepEqual(result, { search_query: "hello" });
15
+ });
16
+ test("remapArguments: remaps query → search_query when search_query is in targetProperties", () => {
17
+ const result = remapArguments({ query: "hello", count: 3 }, ["search_query", "count"]);
18
+ assert.deepEqual(result, { search_query: "hello", count: 3 });
19
+ });
20
+ test("remapArguments: passes through unrecognised key that has no alias", () => {
21
+ const result = remapArguments({ unknown_key: "val" }, ["search_query"]);
22
+ assert.deepEqual(result, { unknown_key: "val" });
23
+ });
24
+ test("remapArguments: passes through alias key when alias not in targetProperties", () => {
25
+ // query → search_query alias exists, but target schema doesn't have search_query
26
+ const result = remapArguments({ query: "hello" }, ["q"]);
27
+ assert.deepEqual(result, { query: "hello" });
28
+ });
29
+ test("remapArguments: canonical key wins over alias (key already in targetProperties)", () => {
30
+ // If caller somehow passes search_query directly and it matches, no remapping occurs
31
+ const result = remapArguments({ search_query: "direct" }, ["search_query"]);
32
+ assert.deepEqual(result, { search_query: "direct" });
33
+ });
34
+ // ---------------------------------------------------------------------------
35
+ // resolveToolName
36
+ // ---------------------------------------------------------------------------
37
+ test("resolveToolName: exact match takes priority", () => {
38
+ const result = resolveToolName("webSearchPrime", ["webSearchPrime", "webReader"], "test-endpoint");
39
+ assert.equal(result, "webSearchPrime");
40
+ });
41
+ test("resolveToolName: returns requestedName unchanged when availableTools is empty (fail-open)", () => {
42
+ const result = resolveToolName("image_analysis", [], "test-endpoint");
43
+ assert.equal(result, "image_analysis");
44
+ });
45
+ test("resolveToolName: keyword fallback matches search tool with different name", () => {
46
+ const result = resolveToolName("webSearch", ["SearchToolV2", "readerTool"], "test-endpoint");
47
+ assert.equal(result, "SearchToolV2");
48
+ });
49
+ test("resolveToolName: keyword fallback matches reader tool with different name", () => {
50
+ const result = resolveToolName("webReader", ["searchTool", "ContentReaderV2"], "test-endpoint");
51
+ assert.equal(result, "ContentReaderV2");
52
+ });
53
+ test("resolveToolName: keyword fallback matches vision/image tool with different name", () => {
54
+ const result = resolveToolName("image_analysis", ["analyzeImage", "otherTool"], "test-endpoint");
55
+ assert.equal(result, "analyzeImage");
56
+ });
57
+ test("resolveToolName: throws descriptive error when no match found", () => {
58
+ assert.throws(() => resolveToolName("unknownTool", ["webSearch", "webReader"], "https://api.example.com"), (err) => {
59
+ assert.ok(err.message.includes('"unknownTool"'));
60
+ assert.ok(err.message.includes("webSearch"));
61
+ assert.ok(err.message.includes("webReader"));
62
+ return true;
63
+ });
64
+ });
65
+ //# sourceMappingURL=mcp-arg-remap.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-arg-remap.test.js","sourceRoot":"","sources":["../../src/tests/mcp-arg-remap.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=vision-mcp-client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vision-mcp-client.test.d.ts","sourceRoot":"","sources":["../../src/tests/vision-mcp-client.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,129 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { EventEmitter } from "node:events";
4
+ import { Readable, Writable } from "node:stream";
5
+ import { StdioVisionMcpClient } from "../tools/vision-mcp-client.js";
6
+ function makeFakeChild() {
7
+ const written = [];
8
+ const stdin = new Writable({
9
+ write(chunk, _enc, cb) {
10
+ written.push(chunk.toString("utf8"));
11
+ cb();
12
+ },
13
+ });
14
+ const stdout = new Readable({ read() { } });
15
+ const stderr = new Readable({ read() { } });
16
+ const child = Object.assign(new EventEmitter(), {
17
+ stdin,
18
+ stdout,
19
+ stderr,
20
+ pid: 4242,
21
+ kill: () => true,
22
+ });
23
+ const pushStdout = (line) => stdout.push(line);
24
+ return { child, written, pushStdout };
25
+ }
26
+ test("StdioVisionMcpClient initializes once and forwards tools/call", async () => {
27
+ const { child, written, pushStdout } = makeFakeChild();
28
+ const client = new StdioVisionMcpClient({
29
+ apiKey: "key-1",
30
+ spawn: () => child,
31
+ });
32
+ const callPromise = client.callTool("image_analysis", {
33
+ image_source: "/tmp/x.png",
34
+ prompt: "describe",
35
+ });
36
+ // Wait a tick so the client has written initialize.
37
+ await new Promise((r) => setImmediate(r));
38
+ const initLine = written[0] ?? "";
39
+ const initBody = JSON.parse(initLine.trim());
40
+ assert.equal(initBody.method, "initialize");
41
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: initBody.id, result: { protocolVersion: "2025-06-18" } }) + "\n");
42
+ // notifications/initialized and tools/list should follow.
43
+ await new Promise((r) => setImmediate(r));
44
+ const initialized = JSON.parse(written[1]?.trim() ?? "{}");
45
+ assert.equal(initialized.method, "notifications/initialized");
46
+ const toolsListBody = JSON.parse(written[2]?.trim() ?? "{}");
47
+ assert.equal(toolsListBody.method, "tools/list");
48
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: toolsListBody.id, result: { tools: [{ name: "image_analysis" }] } }) + "\n");
49
+ // tools/call should follow after tools/list completes.
50
+ await new Promise((r) => setImmediate(r));
51
+ const callBody = JSON.parse(written[3]?.trim() ?? "{}");
52
+ assert.equal(callBody.method, "tools/call");
53
+ assert.equal(callBody.params.name, "image_analysis");
54
+ assert.equal(callBody.params.arguments["image_source"], "/tmp/x.png");
55
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: callBody.id, result: { content: [{ type: "text", text: "a cat" }] } }) + "\n");
56
+ const result = await callPromise;
57
+ assert.deepEqual(result, { content: [{ type: "text", text: "a cat" }] });
58
+ await client.dispose();
59
+ });
60
+ test("StdioVisionMcpClient surfaces JSON-RPC errors with method context", async () => {
61
+ const { child, pushStdout } = makeFakeChild();
62
+ const client = new StdioVisionMcpClient({ apiKey: "k", spawn: () => child });
63
+ const callPromise = client.callTool("image_analysis", { image_source: "x" });
64
+ await new Promise((r) => setImmediate(r));
65
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: 1, result: {} }) + "\n"); // init response
66
+ await new Promise((r) => setImmediate(r));
67
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: 2, result: { tools: [{ name: "image_analysis" }] } }) + "\n"); // tools/list
68
+ await new Promise((r) => setImmediate(r));
69
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: 3, error: { code: -32000, message: "quota exceeded" } }) + "\n"); // tools/call error
70
+ await assert.rejects(callPromise, /Vision MCP image_analysis failed.*quota exceeded/);
71
+ await client.dispose();
72
+ });
73
+ test("StdioVisionMcpClient explains a missing npx as an actionable error", async () => {
74
+ const client = new StdioVisionMcpClient({
75
+ apiKey: "k",
76
+ spawn: () => {
77
+ const err = Object.assign(new Error("spawn npx ENOENT"), { code: "ENOENT" });
78
+ throw err;
79
+ },
80
+ });
81
+ await assert.rejects(() => client.callTool("image_analysis", { image_source: "x" }), /npx.*not found/i);
82
+ });
83
+ test("StdioVisionMcpClient resolves tool name via keyword fallback when server uses a different name", async () => {
84
+ const { child, written, pushStdout } = makeFakeChild();
85
+ const client = new StdioVisionMcpClient({ apiKey: "k", spawn: () => child });
86
+ const callPromise = client.callTool("image_analysis", { image_source: "/img.png" });
87
+ await new Promise((r) => setImmediate(r));
88
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: 1, result: { protocolVersion: "2025-06-18" } }) + "\n");
89
+ await new Promise((r) => setImmediate(r));
90
+ // Server uses "analyzeImage" instead of "image_analysis"
91
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: 2, result: { tools: [{ name: "analyzeImage" }] } }) + "\n");
92
+ await new Promise((r) => setImmediate(r));
93
+ const callBody = JSON.parse(written[3]?.trim() ?? "{}");
94
+ assert.equal(callBody.params.name, "analyzeImage");
95
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: callBody.id, result: { content: [{ type: "text", text: "dog" }] } }) + "\n");
96
+ const result = await callPromise;
97
+ assert.deepEqual(result, { content: [{ type: "text", text: "dog" }] });
98
+ await client.dispose();
99
+ });
100
+ test("StdioVisionMcpClient retries once on tool-not-found after re-discovering tools", async () => {
101
+ const { child, written, pushStdout } = makeFakeChild();
102
+ const client = new StdioVisionMcpClient({ apiKey: "k", spawn: () => child });
103
+ const callPromise = client.callTool("image_analysis", { image_source: "/img.png" });
104
+ await new Promise((r) => setImmediate(r));
105
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: 1, result: { protocolVersion: "2025-06-18" } }) + "\n");
106
+ await new Promise((r) => setImmediate(r));
107
+ // Initial discovery: tool name contains "image" so keyword matches
108
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: 2, result: { tools: [{ name: "old_image_tool" }] } }) + "\n");
109
+ await new Promise((r) => setImmediate(r));
110
+ // written[3] = first tools/call (name resolved to "old_image_tool" via keyword)
111
+ const firstCall = JSON.parse(written[3]?.trim() ?? "{}");
112
+ assert.equal(firstCall.params.name, "old_image_tool");
113
+ // tools/call fails with tool-not-found
114
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: firstCall.id, error: { code: -32601, message: "Tool old_image_tool not found" } }) + "\n");
115
+ await new Promise((r) => setImmediate(r));
116
+ // written[4] = re-discovery tools/list
117
+ const rediscoverBody = JSON.parse(written[4]?.trim() ?? "{}");
118
+ assert.equal(rediscoverBody.method, "tools/list");
119
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: rediscoverBody.id, result: { tools: [{ name: "analyzeImage" }] } }) + "\n");
120
+ await new Promise((r) => setImmediate(r));
121
+ // written[5] = retry tools/call with updated name
122
+ const retryCallBody = JSON.parse(written[5]?.trim() ?? "{}");
123
+ assert.equal(retryCallBody.params.name, "analyzeImage");
124
+ pushStdout(JSON.stringify({ jsonrpc: "2.0", id: retryCallBody.id, result: { content: [{ type: "text", text: "cat" }] } }) + "\n");
125
+ const result = await callPromise;
126
+ assert.deepEqual(result, { content: [{ type: "text", text: "cat" }] });
127
+ await client.dispose();
128
+ });
129
+ //# sourceMappingURL=vision-mcp-client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vision-mcp-client.test.js","sourceRoot":"","sources":["../../src/tests/vision-mcp-client.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=zai-mcp-client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zai-mcp-client.test.d.ts","sourceRoot":"","sources":["../../src/tests/zai-mcp-client.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,386 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { ZaiMcpClient } from "../tools/zai-mcp-client.js";
4
+ function jsonResponse(body, init = {}) {
5
+ const headers = new Headers(init.headers);
6
+ headers.set("Content-Type", "application/json");
7
+ if (init.sessionId)
8
+ headers.set("MCP-Session-Id", init.sessionId);
9
+ return new Response(JSON.stringify(body), { ...init, headers });
10
+ }
11
+ function sseResponse(body) {
12
+ return new Response(`event: message\ndata: ${JSON.stringify(body)}\n\n`, {
13
+ headers: { "Content-Type": "text/event-stream" },
14
+ });
15
+ }
16
+ function createFetchStub(responses) {
17
+ const calls = [];
18
+ const fetchStub = async (url, init) => {
19
+ assert.ok(init, "fetch init is required");
20
+ const body = typeof init.body === "string" ? JSON.parse(init.body) : {};
21
+ const headers = new Headers(init.headers);
22
+ calls.push({ url: String(url), init, body, headers });
23
+ const response = responses.shift();
24
+ assert.ok(response, "unexpected fetch call");
25
+ return response;
26
+ };
27
+ return { calls, fetchStub };
28
+ }
29
+ test("ZaiMcpClient initializes, sends initialized, discovers tools, and calls a tool", async () => {
30
+ const endpoint = "https://api.z.ai/api/mcp/web_search_prime/mcp";
31
+ const { calls, fetchStub } = createFetchStub([
32
+ jsonResponse({
33
+ jsonrpc: "2.0",
34
+ id: 1,
35
+ result: { protocolVersion: "2025-06-18", capabilities: {}, serverInfo: { name: "zai" } },
36
+ }, { sessionId: "session-123" }),
37
+ new Response(null, { status: 202 }),
38
+ jsonResponse({
39
+ jsonrpc: "2.0",
40
+ id: 2,
41
+ result: { tools: [{ name: "webSearchPrime", inputSchema: { properties: { search_query: { type: "string" }, result_count: { type: "number" } } } }] },
42
+ }),
43
+ jsonResponse({
44
+ jsonrpc: "2.0",
45
+ id: 3,
46
+ result: { content: [{ type: "text", text: "Search result text" }] },
47
+ }),
48
+ ]);
49
+ const client = new ZaiMcpClient(fetchStub);
50
+ const result = await client.callTool({
51
+ endpoint,
52
+ toolName: "webSearchPrime",
53
+ arguments: { query: "glm coding plan", count: 3 },
54
+ apiKey: "test-key",
55
+ });
56
+ assert.deepEqual(result, { content: [{ type: "text", text: "Search result text" }] });
57
+ assert.equal(calls.length, 4);
58
+ assert.equal(calls[0]?.body.method, "initialize");
59
+ assert.equal(calls[0]?.headers.get("Authorization"), "Bearer test-key");
60
+ assert.equal(calls[0]?.headers.get("Accept"), "application/json, text/event-stream");
61
+ assert.equal(calls[0]?.headers.get("Mcp-Method"), "initialize");
62
+ assert.equal(calls[0]?.headers.get("MCP-Protocol-Version"), "2025-06-18");
63
+ assert.equal(calls[1]?.body.method, "notifications/initialized");
64
+ assert.equal(calls[1]?.headers.get("MCP-Session-Id"), "session-123");
65
+ assert.equal(calls[1]?.headers.get("Mcp-Method"), "notifications/initialized");
66
+ assert.equal(calls[2]?.body.method, "tools/list");
67
+ assert.equal(calls[2]?.headers.get("MCP-Session-Id"), "session-123");
68
+ assert.equal(calls[3]?.body.method, "tools/call");
69
+ assert.deepEqual(calls[3]?.body.params, {
70
+ name: "webSearchPrime",
71
+ arguments: { search_query: "glm coding plan", count: 3 },
72
+ });
73
+ assert.equal(calls[3]?.headers.get("MCP-Session-Id"), "session-123");
74
+ assert.equal(calls[3]?.headers.get("Mcp-Method"), "tools/call");
75
+ assert.equal(calls[3]?.headers.get("Mcp-Name"), "webSearchPrime");
76
+ });
77
+ test("ZaiMcpClient discovers tools after initialization and caches them", async () => {
78
+ const endpoint = "https://api.z.ai/api/mcp/web_search_prime/mcp";
79
+ const { calls, fetchStub } = createFetchStub([
80
+ jsonResponse({
81
+ jsonrpc: "2.0",
82
+ id: 1,
83
+ result: { protocolVersion: "2025-06-18", capabilities: {}, serverInfo: { name: "zai" } },
84
+ }, { sessionId: "session-discover" }),
85
+ new Response(null, { status: 202 }),
86
+ jsonResponse({
87
+ jsonrpc: "2.0",
88
+ id: 2,
89
+ result: {
90
+ tools: [
91
+ { name: "webSearchPrime", description: "Search the web" },
92
+ { name: "otherTool", description: "Something else" },
93
+ ],
94
+ },
95
+ }),
96
+ jsonResponse({
97
+ jsonrpc: "2.0",
98
+ id: 3,
99
+ result: { content: [{ type: "text", text: "ok" }] },
100
+ }),
101
+ ]);
102
+ const client = new ZaiMcpClient(fetchStub);
103
+ await client.callTool({
104
+ endpoint,
105
+ toolName: "webSearchPrime",
106
+ arguments: { query: "test" },
107
+ apiKey: "test-key",
108
+ });
109
+ assert.equal(calls.length, 4);
110
+ assert.equal(calls[2]?.body.method, "tools/list");
111
+ assert.equal(calls[2]?.headers.get("MCP-Session-Id"), "session-discover");
112
+ assert.equal(calls[2]?.headers.get("Mcp-Method"), "tools/list");
113
+ });
114
+ test("ZaiMcpClient parses SSE wrapped JSON-RPC responses", async () => {
115
+ const endpoint = "https://api.z.ai/api/mcp/web_reader/mcp";
116
+ const { fetchStub } = createFetchStub([
117
+ sseResponse({
118
+ jsonrpc: "2.0",
119
+ id: 1,
120
+ result: { protocolVersion: "2025-06-18", capabilities: {} },
121
+ }),
122
+ new Response(null, { status: 202 }),
123
+ sseResponse({
124
+ jsonrpc: "2.0",
125
+ id: 2,
126
+ result: { tools: [{ name: "webReader" }] },
127
+ }),
128
+ sseResponse({
129
+ jsonrpc: "2.0",
130
+ id: 3,
131
+ result: { content: [{ type: "text", text: "# Title\nBody" }] },
132
+ }),
133
+ ]);
134
+ const client = new ZaiMcpClient(fetchStub);
135
+ const result = await client.callTool({
136
+ endpoint,
137
+ toolName: "webReader",
138
+ arguments: { url: "https://example.com" },
139
+ apiKey: "test-key",
140
+ });
141
+ assert.deepEqual(result, { content: [{ type: "text", text: "# Title\nBody" }] });
142
+ });
143
+ test("ZaiMcpClient resolves tool name via keyword fallback when exact name is unavailable", async () => {
144
+ const endpoint = "https://api.z.ai/api/mcp/web_search_prime/mcp";
145
+ const { calls, fetchStub } = createFetchStub([
146
+ jsonResponse({
147
+ jsonrpc: "2.0",
148
+ id: 1,
149
+ result: { protocolVersion: "2025-06-18", capabilities: {} },
150
+ }, { sessionId: "session-fallback" }),
151
+ new Response(null, { status: 202 }),
152
+ jsonResponse({
153
+ jsonrpc: "2.0",
154
+ id: 2,
155
+ result: { tools: [{ name: "webSearchV2" }, { name: "otherTool" }] },
156
+ }),
157
+ jsonResponse({
158
+ jsonrpc: "2.0",
159
+ id: 3,
160
+ result: { content: [{ type: "text", text: "fallback result" }] },
161
+ }),
162
+ ]);
163
+ const client = new ZaiMcpClient(fetchStub);
164
+ const result = await client.callTool({
165
+ endpoint,
166
+ toolName: "webSearchPrime",
167
+ arguments: { query: "test" },
168
+ apiKey: "test-key",
169
+ });
170
+ assert.deepEqual(result, { content: [{ type: "text", text: "fallback result" }] });
171
+ assert.equal(calls[3]?.body.method, "tools/call");
172
+ assert.deepEqual(calls[3]?.body.params, {
173
+ name: "webSearchV2",
174
+ arguments: { query: "test" },
175
+ });
176
+ assert.equal(calls[3]?.headers.get("Mcp-Name"), "webSearchV2");
177
+ });
178
+ test("ZaiMcpClient throws with diagnostics when no matching tool is found", async () => {
179
+ const endpoint = "https://api.z.ai/api/mcp/web_search_prime/mcp";
180
+ const { fetchStub } = createFetchStub([
181
+ jsonResponse({
182
+ jsonrpc: "2.0",
183
+ id: 1,
184
+ result: { protocolVersion: "2025-06-18", capabilities: {} },
185
+ }, { sessionId: "session-no-match" }),
186
+ new Response(null, { status: 202 }),
187
+ jsonResponse({
188
+ jsonrpc: "2.0",
189
+ id: 2,
190
+ result: { tools: [{ name: "totallyUnrelated" }] },
191
+ }),
192
+ ]);
193
+ const client = new ZaiMcpClient(fetchStub);
194
+ await assert.rejects(() => client.callTool({
195
+ endpoint,
196
+ toolName: "webSearchPrime",
197
+ arguments: { query: "test" },
198
+ apiKey: "test-key",
199
+ }), /Tool "webSearchPrime" not available/);
200
+ });
201
+ test("ZaiMcpClient explains Coding Plan eligibility when Z.AI returns 1113", async () => {
202
+ const endpoint = "https://api.z.ai/api/mcp/web_search_prime/mcp";
203
+ const { fetchStub } = createFetchStub([
204
+ jsonResponse({
205
+ error: {
206
+ code: "1113",
207
+ message: "No permission for current API key",
208
+ },
209
+ }, { status: 429 }),
210
+ ]);
211
+ const client = new ZaiMcpClient(fetchStub);
212
+ await assert.rejects(() => client.callTool({
213
+ endpoint,
214
+ toolName: "webSearchPrime",
215
+ arguments: { query: "glm" },
216
+ apiKey: "test-key",
217
+ }), /Coding Plan quota\/base URL\/tool eligibility.*1113/);
218
+ });
219
+ test("ZaiMcpClient retries once on tool-not-found error with re-initialization", async () => {
220
+ const endpoint = "https://api.z.ai/api/mcp/web_search_prime/mcp";
221
+ const { calls, fetchStub } = createFetchStub([
222
+ // First attempt: init
223
+ jsonResponse({
224
+ jsonrpc: "2.0",
225
+ id: 1,
226
+ result: { protocolVersion: "2025-06-18", capabilities: {} },
227
+ }, { sessionId: "session-old" }),
228
+ new Response(null, { status: 202 }),
229
+ // First attempt: tools/list returns stale list
230
+ jsonResponse({
231
+ jsonrpc: "2.0",
232
+ id: 2,
233
+ result: { tools: [{ name: "staleSearchTool" }] },
234
+ }),
235
+ // First attempt: tools/call fails with tool-not-found
236
+ jsonResponse({
237
+ jsonrpc: "2.0",
238
+ id: 3,
239
+ error: { code: -32601, message: "Tool not found: staleSearchTool" },
240
+ }),
241
+ // Retry: re-initialize
242
+ jsonResponse({
243
+ jsonrpc: "2.0",
244
+ id: 4,
245
+ result: { protocolVersion: "2025-06-18", capabilities: {} },
246
+ }, { sessionId: "session-new" }),
247
+ new Response(null, { status: 202 }),
248
+ // Retry: tools/list returns updated list
249
+ jsonResponse({
250
+ jsonrpc: "2.0",
251
+ id: 5,
252
+ result: { tools: [{ name: "webSearchV2" }] },
253
+ }),
254
+ // Retry: tools/call succeeds
255
+ jsonResponse({
256
+ jsonrpc: "2.0",
257
+ id: 6,
258
+ result: { content: [{ type: "text", text: "retry success" }] },
259
+ }),
260
+ ]);
261
+ const client = new ZaiMcpClient(fetchStub);
262
+ const result = await client.callTool({
263
+ endpoint,
264
+ toolName: "webSearchPrime",
265
+ arguments: { query: "test" },
266
+ apiKey: "test-key",
267
+ });
268
+ assert.deepEqual(result, { content: [{ type: "text", text: "retry success" }] });
269
+ assert.equal(calls.length, 8);
270
+ assert.equal(calls[4]?.body.method, "initialize");
271
+ assert.equal(calls[6]?.body.method, "tools/list");
272
+ assert.equal(calls[7]?.body.method, "tools/call");
273
+ assert.deepEqual(calls[7]?.body.params, {
274
+ name: "webSearchV2",
275
+ arguments: { query: "test" },
276
+ });
277
+ });
278
+ test("ZaiMcpClient does not retry on non-tool-not-found errors", async () => {
279
+ const endpoint = "https://api.z.ai/api/mcp/web_search_prime/mcp";
280
+ const { calls, fetchStub } = createFetchStub([
281
+ jsonResponse({
282
+ jsonrpc: "2.0",
283
+ id: 1,
284
+ result: { protocolVersion: "2025-06-18", capabilities: {} },
285
+ }, { sessionId: "session-no-retry" }),
286
+ new Response(null, { status: 202 }),
287
+ jsonResponse({
288
+ jsonrpc: "2.0",
289
+ id: 2,
290
+ result: { tools: [{ name: "webSearchPrime" }] },
291
+ }),
292
+ jsonResponse({
293
+ jsonrpc: "2.0",
294
+ id: 3,
295
+ error: { code: -32000, message: "Internal server error" },
296
+ }),
297
+ ]);
298
+ const client = new ZaiMcpClient(fetchStub);
299
+ await assert.rejects(() => client.callTool({
300
+ endpoint,
301
+ toolName: "webSearchPrime",
302
+ arguments: { query: "test" },
303
+ apiKey: "test-key",
304
+ }), /Internal server error/);
305
+ assert.equal(calls.length, 4);
306
+ });
307
+ test("ZaiMcpClient passes args through unchanged when arg names already match schema properties", async () => {
308
+ const endpoint = "https://api.z.ai/api/mcp/web_search_prime/mcp";
309
+ const { calls, fetchStub } = createFetchStub([
310
+ jsonResponse({ jsonrpc: "2.0", id: 1, result: { protocolVersion: "2025-06-18", capabilities: {} } }, { sessionId: "session-passthrough" }),
311
+ new Response(null, { status: 202 }),
312
+ jsonResponse({
313
+ jsonrpc: "2.0",
314
+ id: 2,
315
+ result: { tools: [{ name: "webSearchPrime", inputSchema: { properties: { search_query: { type: "string" } } } }] },
316
+ }),
317
+ jsonResponse({
318
+ jsonrpc: "2.0",
319
+ id: 3,
320
+ result: { content: [{ type: "text", text: "ok" }] },
321
+ }),
322
+ ]);
323
+ const client = new ZaiMcpClient(fetchStub);
324
+ await client.callTool({
325
+ endpoint,
326
+ toolName: "webSearchPrime",
327
+ arguments: { search_query: "already correct" },
328
+ apiKey: "test-key",
329
+ });
330
+ assert.deepEqual(calls[3]?.body.params, {
331
+ name: "webSearchPrime",
332
+ arguments: { search_query: "already correct" },
333
+ });
334
+ });
335
+ test("ZaiMcpClient retries once on -32602 arg-validation error and re-discovers schema", async () => {
336
+ const endpoint = "https://api.z.ai/api/mcp/web_search_prime/mcp";
337
+ const { calls, fetchStub } = createFetchStub([
338
+ // First attempt: init
339
+ jsonResponse({ jsonrpc: "2.0", id: 1, result: { protocolVersion: "2025-06-18", capabilities: {} } }, { sessionId: "session-stale" }),
340
+ new Response(null, { status: 202 }),
341
+ // First attempt: tools/list returns stale schema (wrong arg name)
342
+ jsonResponse({
343
+ jsonrpc: "2.0",
344
+ id: 2,
345
+ result: { tools: [{ name: "webSearchPrime", inputSchema: { properties: { q: { type: "string" } } } }] },
346
+ }),
347
+ // First attempt: tools/call fails with -32602
348
+ jsonResponse({
349
+ jsonrpc: "2.0",
350
+ id: 3,
351
+ error: { code: -32602, message: "search_query cannot be empty" },
352
+ }),
353
+ // Retry: re-initialize
354
+ jsonResponse({ jsonrpc: "2.0", id: 4, result: { protocolVersion: "2025-06-18", capabilities: {} } }, { sessionId: "session-fresh" }),
355
+ new Response(null, { status: 202 }),
356
+ // Retry: tools/list returns updated schema
357
+ jsonResponse({
358
+ jsonrpc: "2.0",
359
+ id: 5,
360
+ result: { tools: [{ name: "webSearchPrime", inputSchema: { properties: { search_query: { type: "string" } } } }] },
361
+ }),
362
+ // Retry: tools/call succeeds with remapped args
363
+ jsonResponse({
364
+ jsonrpc: "2.0",
365
+ id: 6,
366
+ result: { content: [{ type: "text", text: "retry success" }] },
367
+ }),
368
+ ]);
369
+ const client = new ZaiMcpClient(fetchStub);
370
+ const result = await client.callTool({
371
+ endpoint,
372
+ toolName: "webSearchPrime",
373
+ arguments: { query: "test" },
374
+ apiKey: "test-key",
375
+ });
376
+ assert.deepEqual(result, { content: [{ type: "text", text: "retry success" }] });
377
+ assert.equal(calls.length, 8);
378
+ assert.equal(calls[4]?.body.method, "initialize");
379
+ assert.equal(calls[6]?.body.method, "tools/list");
380
+ // After re-discovery the alias maps query → search_query
381
+ assert.deepEqual(calls[7]?.body.params, {
382
+ name: "webSearchPrime",
383
+ arguments: { search_query: "test" },
384
+ });
385
+ });
386
+ //# sourceMappingURL=zai-mcp-client.test.js.map