@ai-agent-forge/agent-forge 0.88.5 → 0.88.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.
- package/CHANGELOG.md +36 -0
- package/bundled-plugins/agent-loop-policy/agent-forge.json +1 -1
- package/bundled-plugins/agent-loop-policy/plugin.json +1 -1
- package/bundled-plugins/alignment/agent-forge.json +19 -0
- package/bundled-plugins/alignment/dist/baseline.d.ts +12 -0
- package/bundled-plugins/alignment/dist/baseline.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/baseline.js +68 -0
- package/bundled-plugins/alignment/dist/baseline.js.map +1 -0
- package/bundled-plugins/alignment/dist/engine.d.ts +102 -0
- package/bundled-plugins/alignment/dist/engine.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/engine.js +654 -0
- package/bundled-plugins/alignment/dist/engine.js.map +1 -0
- package/bundled-plugins/alignment/dist/entry.d.ts +14 -0
- package/bundled-plugins/alignment/dist/entry.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/entry.js +23 -0
- package/bundled-plugins/alignment/dist/entry.js.map +1 -0
- package/bundled-plugins/alignment/dist/export.d.ts +15 -0
- package/bundled-plugins/alignment/dist/export.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/export.js +122 -0
- package/bundled-plugins/alignment/dist/export.js.map +1 -0
- package/bundled-plugins/alignment/dist/http-server.d.ts +33 -0
- package/bundled-plugins/alignment/dist/http-server.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/http-server.js +119 -0
- package/bundled-plugins/alignment/dist/http-server.js.map +1 -0
- package/bundled-plugins/alignment/dist/index.d.ts +24 -0
- package/bundled-plugins/alignment/dist/index.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/index.js +719 -0
- package/bundled-plugins/alignment/dist/index.js.map +1 -0
- package/bundled-plugins/alignment/dist/page.d.ts +15 -0
- package/bundled-plugins/alignment/dist/page.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/page.js +857 -0
- package/bundled-plugins/alignment/dist/page.js.map +1 -0
- package/bundled-plugins/alignment/dist/render.d.ts +40 -0
- package/bundled-plugins/alignment/dist/render.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/render.js +194 -0
- package/bundled-plugins/alignment/dist/render.js.map +1 -0
- package/bundled-plugins/alignment/dist/types.d.ts +217 -0
- package/bundled-plugins/alignment/dist/types.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/types.js +30 -0
- package/bundled-plugins/alignment/dist/types.js.map +1 -0
- package/bundled-plugins/alignment/plugin.json +12 -0
- package/bundled-plugins/browser-playwright/MANUAL.md +60 -0
- package/bundled-plugins/browser-playwright/agent-forge.json +25 -0
- package/bundled-plugins/browser-playwright/dist/capability.d.ts +25 -0
- package/bundled-plugins/browser-playwright/dist/capability.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/capability.js +612 -0
- package/bundled-plugins/browser-playwright/dist/capability.js.map +1 -0
- package/bundled-plugins/browser-playwright/dist/entry.d.ts +13 -0
- package/bundled-plugins/browser-playwright/dist/entry.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/entry.js +14 -0
- package/bundled-plugins/browser-playwright/dist/entry.js.map +1 -0
- package/bundled-plugins/browser-playwright/dist/index.d.ts +7 -0
- package/bundled-plugins/browser-playwright/dist/index.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/index.js +5 -0
- package/bundled-plugins/browser-playwright/dist/index.js.map +1 -0
- package/bundled-plugins/browser-playwright/dist/operations.d.ts +69 -0
- package/bundled-plugins/browser-playwright/dist/operations.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/operations.js +149 -0
- package/bundled-plugins/browser-playwright/dist/operations.js.map +1 -0
- package/bundled-plugins/browser-playwright/dist/playwright-driver.d.ts +275 -0
- package/bundled-plugins/browser-playwright/dist/playwright-driver.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/playwright-driver.js +953 -0
- package/bundled-plugins/browser-playwright/dist/playwright-driver.js.map +1 -0
- package/bundled-plugins/browser-playwright/dist/setup.d.ts +88 -0
- package/bundled-plugins/browser-playwright/dist/setup.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/setup.js +252 -0
- package/bundled-plugins/browser-playwright/dist/setup.js.map +1 -0
- package/bundled-plugins/browser-playwright/dist/types.d.ts +270 -0
- package/bundled-plugins/browser-playwright/dist/types.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/types.js +2 -0
- package/bundled-plugins/browser-playwright/dist/types.js.map +1 -0
- package/bundled-plugins/browser-playwright/plugin.json +20 -0
- package/bundled-plugins/coding-tools/agent-forge.json +1 -1
- package/bundled-plugins/coding-tools/dist/bash.d.ts +2 -7
- package/bundled-plugins/coding-tools/dist/bash.d.ts.map +1 -1
- package/bundled-plugins/coding-tools/dist/bash.js +2 -34
- package/bundled-plugins/coding-tools/dist/bash.js.map +1 -1
- package/bundled-plugins/coding-tools/dist/read.d.ts +2 -0
- package/bundled-plugins/coding-tools/dist/read.d.ts.map +1 -1
- package/bundled-plugins/coding-tools/dist/read.js +11 -0
- package/bundled-plugins/coding-tools/dist/read.js.map +1 -1
- package/bundled-plugins/coding-tools/plugin.json +1 -1
- package/bundled-plugins/compaction-policy/agent-forge.json +1 -1
- package/bundled-plugins/compaction-policy/plugin.json +1 -1
- package/bundled-plugins/mcp-http/dist/mcp-http.d.ts.map +1 -1
- package/bundled-plugins/mcp-http/dist/mcp-http.js +84 -31
- package/bundled-plugins/mcp-http/dist/mcp-http.js.map +1 -1
- package/bundled-plugins/overflow-policy/agent-forge.json +1 -1
- package/bundled-plugins/overflow-policy/plugin.json +1 -1
- package/bundled-plugins/provider-failover/agent-forge.json +1 -1
- package/bundled-plugins/provider-failover/plugin.json +1 -1
- package/bundled-plugins/retry-policy/agent-forge.json +1 -1
- package/bundled-plugins/retry-policy/plugin.json +1 -1
- package/dist/bundle/.build-manifest.json +60 -58
- package/dist/bundle/chunks/{anthropic-messages-3FJHRAJL.js → anthropic-messages-NL4Y6WPB.js} +1 -1
- package/dist/bundle/chunks/{azure-openai-responses-GCL47IDS.js → azure-openai-responses-CPXC42HO.js} +1 -1
- package/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
- package/dist/bundle/chunks/chunk-5LRQFZFE.js +2 -0
- package/dist/bundle/chunks/{chunk-HTEVUY7F.js → chunk-7HSJPQKN.js} +1 -1
- package/dist/bundle/chunks/{chunk-AC2FIS4G.js → chunk-EDO7Q6XR.js} +1 -1
- package/dist/bundle/chunks/{chunk-755VNWIF.js → chunk-ISLHFRER.js} +6 -6
- package/dist/bundle/chunks/{chunk-HV64ASMT.js → chunk-K3S6GTL7.js} +1 -1
- package/dist/bundle/chunks/{chunk-HEL6TAWE.js → chunk-L2SXIPI5.js} +37 -36
- package/dist/bundle/chunks/{chunk-V3ACW7VD.js → chunk-WJSKB3FC.js} +1 -1
- package/dist/bundle/chunks/{chunk-32YDZI4J.js → chunk-ZS5JEI76.js} +1 -1
- package/dist/bundle/chunks/{google-generative-ai-6BMXWA44.js → google-generative-ai-UMZ53EFA.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-6H7NLRTK.js → google-vertex-ZNR5NS6H.js} +1 -1
- package/dist/bundle/chunks/image-resize-worker.js +1 -1
- package/dist/bundle/chunks/{mistral-conversations-IXUHSFSJ.js → mistral-conversations-IT66WUNN.js} +1 -1
- package/dist/bundle/chunks/{openai-codex-responses-YHUAEVCM.js → openai-codex-responses-FOVCVG6I.js} +1 -1
- package/dist/bundle/chunks/{openai-completions-ULQCFC72.js → openai-completions-FLQ7CBOL.js} +1 -1
- package/dist/bundle/chunks/{openai-responses-NLLAJDPS.js → openai-responses-ABGYHS2T.js} +1 -1
- package/dist/bundle/chunks/{pi-messages-EJTI2EJ3.js → pi-messages-6K5W6LUN.js} +1 -1
- package/dist/bundle/chunks/{virtual-modules-TQWEGNNZ.js → virtual-modules-2QGILFV7.js} +1 -1
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/client.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/client/remote-session.d.ts.map +1 -1
- package/dist/client/remote-session.js +65 -6
- package/dist/client/remote-session.js.map +1 -1
- package/dist/core/agent-session-store.d.ts.map +1 -1
- package/dist/core/agent-session-store.js +20 -3
- package/dist/core/agent-session-store.js.map +1 -1
- package/dist/core/extensions/agent-composition.d.ts.map +1 -1
- package/dist/core/extensions/agent-composition.js +20 -1
- package/dist/core/extensions/agent-composition.js.map +1 -1
- package/dist/core/i18n/locales/zh-cn/core-errors.d.ts.map +1 -1
- package/dist/core/i18n/locales/zh-cn/core-errors.js +1 -0
- package/dist/core/i18n/locales/zh-cn/core-errors.js.map +1 -1
- package/dist/core/i18n/locales/zh-cn/interactive-mode.d.ts.map +1 -1
- package/dist/core/i18n/locales/zh-cn/interactive-mode.js +10 -3
- package/dist/core/i18n/locales/zh-cn/interactive-mode.js.map +1 -1
- package/dist/core/keybindings.d.ts +35 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +34 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +62 -15
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +11 -3
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +2 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/capability-mount.d.ts.map +1 -1
- package/dist/core/tools/capability-mount.js +4 -21
- package/dist/core/tools/capability-mount.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +2 -2
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +2 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +25 -0
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/changelog-pager.d.ts +53 -0
- package/dist/modes/interactive/components/changelog-pager.d.ts.map +1 -0
- package/dist/modes/interactive/components/changelog-pager.js +239 -0
- package/dist/modes/interactive/components/changelog-pager.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts +6 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +12 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-run-group.d.ts +41 -11
- package/dist/modes/interactive/components/tool-run-group.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-run-group.js +126 -14
- package/dist/modes/interactive/components/tool-run-group.js.map +1 -1
- package/dist/modes/interactive/interactive-commands.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-commands.js +23 -13
- package/dist/modes/interactive/interactive-commands.js.map +1 -1
- package/dist/modes/interactive/interactive-events.d.ts +14 -4
- package/dist/modes/interactive/interactive-events.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-events.js +94 -12
- package/dist/modes/interactive/interactive-events.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +6 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +21 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/plugins/ecosystem/node-local-package-source.d.ts +2 -1
- package/dist/plugins/ecosystem/node-local-package-source.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-local-package-source.js +110 -4
- package/dist/plugins/ecosystem/node-local-package-source.js.map +1 -1
- package/dist/plugins/ecosystem/node-package-install-host.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-package-install-host.js +106 -39
- package/dist/plugins/ecosystem/node-package-install-host.js.map +1 -1
- package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-plugin-bootstrap.js +85 -2
- package/dist/plugins/ecosystem/node-plugin-bootstrap.js.map +1 -1
- package/dist/suites/builtin-suites.d.ts.map +1 -1
- package/dist/suites/builtin-suites.js +2 -2
- package/dist/suites/builtin-suites.js.map +1 -1
- package/dist/utils/changelog.d.ts +27 -0
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js +110 -0
- package/dist/utils/changelog.js.map +1 -1
- package/docs/keybindings.md +14 -0
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/extensions/with-deps/plugin.json +1 -1
- package/package.json +1 -1
- package/dist/bundle/chunks/chunk-A3JYRWB6.js +0 -2
|
@@ -4,8 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Retries apply ONLY to requests the server could not have started (connect
|
|
6
6
|
* errors, 5xx, 429) — a completed response (any JSON-RPC reply) is never
|
|
7
|
-
* re-sent, keeping calls idempotent from the caller's perspective.
|
|
8
|
-
*
|
|
7
|
+
* re-sent, keeping calls idempotent from the caller's perspective. The same
|
|
8
|
+
* idempotency rule makes post-send failures terminal: a response-body read
|
|
9
|
+
* error, a per-request timeout, or a caller abort all mean the request
|
|
10
|
+
* already reached the server, so they are reported once and never re-sent.
|
|
11
|
+
* close() rejects every in-flight request and fails new ones immediately.
|
|
12
|
+
* Every transport error, retry attempt, and reconnect is reported through the
|
|
9
13
|
* `onEvent` callback so callers can observe what happened. CLI
|
|
10
14
|
* productization is out of scope.
|
|
11
15
|
*/
|
|
@@ -16,13 +20,14 @@
|
|
|
16
20
|
* example's own declaration, which stays self-contained.
|
|
17
21
|
*/
|
|
18
22
|
import { t } from "@agent-forge/host/i18n";
|
|
23
|
+
const formatError = (error) => error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
19
24
|
export function createMcpHttpTransport(options) {
|
|
20
25
|
const timeoutMs = options.timeoutMs ?? 10_000;
|
|
21
26
|
const retryLimit = options.retryLimit ?? 2;
|
|
22
27
|
const baseRetryMs = options.baseRetryMs ?? 100;
|
|
23
28
|
const maxRetryMs = options.maxRetryMs ?? 1_600;
|
|
24
29
|
const now = options.now ?? (() => Date.now());
|
|
25
|
-
const sleep = options.sleep ?? (() =>
|
|
30
|
+
const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
26
31
|
const events = [];
|
|
27
32
|
let nextId = 1;
|
|
28
33
|
let closed = false;
|
|
@@ -30,9 +35,18 @@ export function createMcpHttpTransport(options) {
|
|
|
30
35
|
events.push(event);
|
|
31
36
|
options.onEvent?.(event);
|
|
32
37
|
};
|
|
33
|
-
|
|
38
|
+
/** Abort controllers for in-flight postWithRetry runs; close() aborts them all. */
|
|
39
|
+
const inFlight = new Set();
|
|
40
|
+
const closedError = () => new Error(t("MCP HTTP transport is closed"));
|
|
41
|
+
const postOnce = async (body, requestOptions, closeSignal) => {
|
|
42
|
+
if (requestOptions?.signal?.aborted) {
|
|
43
|
+
return { status: "aborted", reason: t("request aborted before it was sent") };
|
|
44
|
+
}
|
|
34
45
|
const controller = new AbortController();
|
|
35
46
|
const timer = setTimeout(() => controller.abort(), requestOptions?.timeoutMs ?? timeoutMs);
|
|
47
|
+
const signals = [controller.signal, closeSignal];
|
|
48
|
+
if (requestOptions?.signal)
|
|
49
|
+
signals.push(requestOptions.signal);
|
|
36
50
|
try {
|
|
37
51
|
const response = await fetch(options.url, {
|
|
38
52
|
method: "POST",
|
|
@@ -42,52 +56,85 @@ export function createMcpHttpTransport(options) {
|
|
|
42
56
|
...(options.headers ?? {}),
|
|
43
57
|
},
|
|
44
58
|
body,
|
|
45
|
-
signal:
|
|
59
|
+
signal: AbortSignal.any(signals),
|
|
46
60
|
});
|
|
47
|
-
|
|
61
|
+
let text;
|
|
62
|
+
try {
|
|
63
|
+
text = await response.text();
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
// Response headers already arrived: the server may have started
|
|
67
|
+
// executing, so a re-send could duplicate side effects.
|
|
68
|
+
return { status: "read_error", reason: formatError(error) };
|
|
69
|
+
}
|
|
48
70
|
return { status: response.status, text };
|
|
49
71
|
}
|
|
50
72
|
catch (error) {
|
|
51
|
-
|
|
52
|
-
|
|
73
|
+
// An abort means the request was already sent (per-request timeout,
|
|
74
|
+
// caller signal, or close), so it must never be re-sent. Only a fetch
|
|
75
|
+
// failure before any response stays classified as connect_error.
|
|
76
|
+
if (controller.signal.aborted || requestOptions?.signal?.aborted) {
|
|
77
|
+
return { status: "aborted", reason: formatError(error) };
|
|
78
|
+
}
|
|
79
|
+
return { status: "connect_error", reason: formatError(error) };
|
|
53
80
|
}
|
|
54
81
|
finally {
|
|
55
82
|
clearTimeout(timer);
|
|
56
83
|
}
|
|
57
84
|
};
|
|
58
85
|
const postWithRetry = async (body, requestOptions) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
86
|
+
const closeController = new AbortController();
|
|
87
|
+
inFlight.add(closeController);
|
|
88
|
+
try {
|
|
89
|
+
for (let attempt = 1;; attempt += 1) {
|
|
90
|
+
if (closed)
|
|
91
|
+
throw closedError();
|
|
92
|
+
const response = await postOnce(body, requestOptions, closeController.signal);
|
|
93
|
+
if (closed)
|
|
94
|
+
throw closedError();
|
|
95
|
+
if (response.status === "read_error" || response.status === "aborted") {
|
|
96
|
+
record({
|
|
97
|
+
kind: "transport_error",
|
|
98
|
+
at: now(),
|
|
99
|
+
detail: `${response.status}: ${response.reason}`,
|
|
100
|
+
attempt,
|
|
101
|
+
});
|
|
102
|
+
throw new Error(t("MCP HTTP transport error: {reason}", { reason: response.reason }));
|
|
103
|
+
}
|
|
104
|
+
if (response.status !== "connect_error" && response.status < 500 && response.status !== 429) {
|
|
105
|
+
record({ kind: "response", at: now(), detail: `HTTP ${response.status}`, attempt });
|
|
106
|
+
return response;
|
|
107
|
+
}
|
|
108
|
+
const reason = response.status === "connect_error" ? response.reason : `HTTP ${response.status}`;
|
|
109
|
+
if (attempt > retryLimit) {
|
|
110
|
+
record({
|
|
111
|
+
kind: "transport_error",
|
|
112
|
+
at: now(),
|
|
113
|
+
detail: `giving up after ${attempt - 1} retries: ${reason}`,
|
|
114
|
+
attempt,
|
|
115
|
+
});
|
|
116
|
+
throw new Error(t("MCP HTTP transport error: {reason}", { reason }));
|
|
117
|
+
}
|
|
118
|
+
const waitedMs = Math.min(baseRetryMs * 2 ** (attempt - 1), maxRetryMs);
|
|
67
119
|
record({
|
|
68
|
-
kind: "
|
|
120
|
+
kind: "retry",
|
|
69
121
|
at: now(),
|
|
70
|
-
detail: `
|
|
122
|
+
detail: `attempt ${attempt} failed (${reason}); waiting ${waitedMs}ms`,
|
|
71
123
|
attempt,
|
|
72
124
|
});
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
kind: "retry",
|
|
78
|
-
at: now(),
|
|
79
|
-
detail: `attempt ${attempt} failed (${reason}); waiting ${waitedMs}ms`,
|
|
80
|
-
attempt,
|
|
81
|
-
});
|
|
82
|
-
await sleep(waitedMs);
|
|
83
|
-
if (attempt === 1 && response.status === "connect_error") {
|
|
84
|
-
record({ kind: "reconnect", at: now(), detail: "re-establishing HTTP connection", attempt });
|
|
125
|
+
await sleep(waitedMs);
|
|
126
|
+
if (attempt === 1 && response.status === "connect_error") {
|
|
127
|
+
record({ kind: "reconnect", at: now(), detail: "re-establishing HTTP connection", attempt });
|
|
128
|
+
}
|
|
85
129
|
}
|
|
86
130
|
}
|
|
131
|
+
finally {
|
|
132
|
+
inFlight.delete(closeController);
|
|
133
|
+
}
|
|
87
134
|
};
|
|
88
135
|
const rpc = async (method, params, requestOptions) => {
|
|
89
136
|
if (closed)
|
|
90
|
-
throw
|
|
137
|
+
throw closedError();
|
|
91
138
|
const id = nextId;
|
|
92
139
|
nextId += 1;
|
|
93
140
|
const body = JSON.stringify({ jsonrpc: "2.0", id, method, ...(params === undefined ? {} : { params }) });
|
|
@@ -123,6 +170,12 @@ export function createMcpHttpTransport(options) {
|
|
|
123
170
|
},
|
|
124
171
|
async close() {
|
|
125
172
|
closed = true;
|
|
173
|
+
// Reject every in-flight request promptly: aborting its run makes the
|
|
174
|
+
// pending fetch fail and the loop throw the closed error instead of
|
|
175
|
+
// returning a late success or retrying after close.
|
|
176
|
+
for (const controller of inFlight) {
|
|
177
|
+
controller.abort(closedError());
|
|
178
|
+
}
|
|
126
179
|
},
|
|
127
180
|
events() {
|
|
128
181
|
return Object.freeze([...events]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-http.js","sourceRoot":"","sources":["../src/mcp-http.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MCP HTTP transport (4.3) — a streamable-HTTP MCP transport implementing the\n * same transport interface as the stdio plugin, plus observable retry.\n *\n * Retries apply ONLY to requests the server could not have started (connect\n * errors, 5xx, 429) — a completed response (any JSON-RPC reply) is never\n * re-sent, keeping calls idempotent from the caller's perspective. Every\n * transport error, retry attempt, and reconnect is reported through the\n * `onEvent` callback so callers can observe what happened. CLI\n * productization is out of scope.\n */\n/**\n * Per-request options for MCP transports. Defined here rather than imported\n * from the mcp-stdio example so production src never depends on examples/\n * (统一修复轮 C reverse-dependency fix); structurally identical to the\n * example's own declaration, which stays self-contained.\n */\nimport { t } from \"@agent-forge/host/i18n\";\n\nexport interface McpRequestOptions {\n\tsignal?: AbortSignal;\n\ttimeoutMs?: number;\n}\n\nexport interface McpHttpTransportEventV1 {\n\treadonly kind: \"connect\" | \"transport_error\" | \"retry\" | \"reconnect\" | \"response\";\n\treadonly at: number;\n\treadonly detail: string;\n\treadonly attempt?: number;\n}\n\nexport interface McpHttpTransportOptionsV1 {\n\treadonly url: string;\n\treadonly timeoutMs?: number;\n\treadonly retryLimit?: number;\n\treadonly baseRetryMs?: number;\n\treadonly maxRetryMs?: number;\n\treadonly now?: () => number;\n\treadonly sleep?: (ms: number) => Promise<void> | void;\n\treadonly onEvent?: (event: McpHttpTransportEventV1) => void;\n\t/**\n\t * Extra request headers merged after the transport defaults on every\n\t * outbound request (initialize / notifications/initialized / tools/list /\n\t * tools/call), so user values can override the defaults — the\n\t * `Authorization: Bearer ...` case for hosted remote MCP servers. Values\n\t * are sent verbatim (never case-normalized). They may carry credentials:\n\t * the suite MCP pool materializes them into the generated plugin entry\n\t * below `<agentDir>/mcp/pool/`, which sits inside the same local-machine\n\t * data boundary as `auth.json` (docs/design/产品价值度量与数据边界登记.md\n\t * §2.1 本机数据).\n\t */\n\treadonly headers?: Readonly<Record<string, string>>;\n}\n\n/** Structurally compatible with the stdio plugin's transport interface. */\nexport interface McpHttpTransportV1 {\n\trequest(method: string, params?: unknown, options?: McpRequestOptions): Promise<unknown>;\n\tnotify(method: string, params?: unknown): Promise<void> | void;\n\tclose(): Promise<void> | void;\n\tevents(): readonly McpHttpTransportEventV1[];\n}\n\ninterface JsonRpcResponse {\n\tid?: number | string | null;\n\tresult?: unknown;\n\terror?: { code?: number; message?: string };\n}\n\nexport function createMcpHttpTransport(options: McpHttpTransportOptionsV1): McpHttpTransportV1 {\n\tconst timeoutMs = options.timeoutMs ?? 10_000;\n\tconst retryLimit = options.retryLimit ?? 2;\n\tconst baseRetryMs = options.baseRetryMs ?? 100;\n\tconst maxRetryMs = options.maxRetryMs ?? 1_600;\n\tconst now = options.now ?? (() => Date.now());\n\tconst sleep = options.sleep ?? (() => undefined);\n\tconst events: McpHttpTransportEventV1[] = [];\n\tlet nextId = 1;\n\tlet closed = false;\n\n\tconst record = (event: McpHttpTransportEventV1): void => {\n\t\tevents.push(event);\n\t\toptions.onEvent?.(event);\n\t};\n\n\tconst postOnce = async (\n\t\tbody: string,\n\t\trequestOptions: McpRequestOptions | undefined,\n\t): Promise<{ status: number; text: string } | { status: \"connect_error\"; reason: string }> => {\n\t\tconst controller = new AbortController();\n\t\tconst timer = setTimeout(() => controller.abort(), requestOptions?.timeoutMs ?? timeoutMs);\n\t\ttry {\n\t\t\tconst response = await fetch(options.url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\taccept: \"application/json, text/event-stream\",\n\t\t\t\t\t...(options.headers ?? {}),\n\t\t\t\t},\n\t\t\t\tbody,\n\t\t\t\tsignal: controller.signal,\n\t\t\t});\n\t\t\tconst text = await response.text();\n\t\t\treturn { status: response.status, text };\n\t\t} catch (error) {\n\t\t\tconst reason = error instanceof Error ? `${error.name}: ${error.message}` : String(error);\n\t\t\treturn { status: \"connect_error\", reason };\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t};\n\n\tconst postWithRetry = async (\n\t\tbody: string,\n\t\trequestOptions?: McpRequestOptions,\n\t): Promise<{ status: number; text: string }> => {\n\t\tfor (let attempt = 1; ; attempt += 1) {\n\t\t\tconst response = await postOnce(body, requestOptions);\n\t\t\tif (response.status !== \"connect_error\" && response.status < 500 && response.status !== 429) {\n\t\t\t\trecord({ kind: \"response\", at: now(), detail: `HTTP ${response.status}`, attempt });\n\t\t\t\treturn response;\n\t\t\t}\n\t\t\tconst reason = response.status === \"connect_error\" ? response.reason : `HTTP ${response.status}`;\n\t\t\tif (attempt > retryLimit) {\n\t\t\t\trecord({\n\t\t\t\t\tkind: \"transport_error\",\n\t\t\t\t\tat: now(),\n\t\t\t\t\tdetail: `giving up after ${attempt - 1} retries: ${reason}`,\n\t\t\t\t\tattempt,\n\t\t\t\t});\n\t\t\t\tthrow new Error(t(\"MCP HTTP transport error: {reason}\", { reason }));\n\t\t\t}\n\t\t\tconst waitedMs = Math.min(baseRetryMs * 2 ** (attempt - 1), maxRetryMs);\n\t\t\trecord({\n\t\t\t\tkind: \"retry\",\n\t\t\t\tat: now(),\n\t\t\t\tdetail: `attempt ${attempt} failed (${reason}); waiting ${waitedMs}ms`,\n\t\t\t\tattempt,\n\t\t\t});\n\t\t\tawait sleep(waitedMs);\n\t\t\tif (attempt === 1 && response.status === \"connect_error\") {\n\t\t\t\trecord({ kind: \"reconnect\", at: now(), detail: \"re-establishing HTTP connection\", attempt });\n\t\t\t}\n\t\t}\n\t};\n\n\tconst rpc = async (method: string, params?: unknown, requestOptions?: McpRequestOptions): Promise<unknown> => {\n\t\tif (closed) throw new Error(t(\"MCP HTTP transport is closed\"));\n\t\tconst id = nextId;\n\t\tnextId += 1;\n\t\tconst body = JSON.stringify({ jsonrpc: \"2.0\", id, method, ...(params === undefined ? {} : { params }) });\n\t\tconst response = await postWithRetry(body, requestOptions);\n\t\tif (response.status !== 200) {\n\t\t\tthrow new Error(t(\"MCP HTTP transport returned HTTP {status}\", { status: response.status }));\n\t\t}\n\t\tlet parsed: JsonRpcResponse;\n\t\ttry {\n\t\t\tparsed = JSON.parse(response.text) as JsonRpcResponse;\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t// v8 ignore next -- JSON.parse 只会抛出 Error 对象\n\t\t\t\tt(\"MCP HTTP transport returned non-JSON body: {reason}\", {\n\t\t\t\t\treason: error instanceof Error ? error.message : String(error),\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (parsed.error !== undefined) {\n\t\t\tthrow new Error(\n\t\t\t\tt(\"MCP error {code}: {message}\", {\n\t\t\t\t\tcode: parsed.error.code ?? \"\",\n\t\t\t\t\tmessage: parsed.error.message ?? t(\"unknown error\"),\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn parsed.result;\n\t};\n\n\treturn {\n\t\tasync request(method, params, requestOptions) {\n\t\t\treturn rpc(method, params, requestOptions);\n\t\t},\n\t\tasync notify(method, params) {\n\t\t\tawait postWithRetry(\n\t\t\t\tJSON.stringify({ jsonrpc: \"2.0\", method, ...(params === undefined ? {} : { params }) }),\n\t\t\t\tundefined,\n\t\t\t);\n\t\t},\n\t\tasync close() {\n\t\t\tclosed = true;\n\t\t},\n\t\tevents() {\n\t\t\treturn Object.freeze([...events]);\n\t\t},\n\t};\n}\n\n/** Minimal api surface the HTTP server plugin relies on (capability registerTool). */\nexport interface McpHttpServerPluginApiV1 {\n\tregisterTool(definition: unknown): unknown;\n}\n\nfunction normalizeToolResult(result: unknown): { content: unknown[]; details?: unknown } {\n\tif (result && typeof result === \"object\" && Array.isArray((result as { content?: unknown }).content)) {\n\t\tconst { content, details } = result as { content: unknown[]; details?: unknown };\n\t\treturn { content, details };\n\t}\n\tif (result === undefined) return { content: [{ type: \"text\", text: \"\" }] };\n\treturn { content: [{ type: \"text\", text: JSON.stringify(result) }] };\n}\n\n/**\n * Configuration-driven entry for remote MCP servers: wires\n * createMcpHttpTransport into the same plugin shape as the generated stdio\n * bridge (initialize/tools/list handshake, one registerTool per remote tool,\n * dual call convention). Tool call policy stays host-owned; the plugin only\n * forwards requests and maps results. The suite MCP registration pool\n * materializes url-configured servers through this entry; optional `headers`\n * are forwarded verbatim to the transport (credential note there).\n */\nexport function createMcpHttpServerPlugin(config: {\n\turl: string;\n\t/** Forwarded to the transport verbatim; see McpHttpTransportOptionsV1.headers for the credential note. */\n\theaders?: Readonly<Record<string, string>>;\n}): (api: McpHttpServerPluginApiV1) => Promise<{ dispose(): void }> {\n\treturn async (api) => {\n\t\tconst transport = createMcpHttpTransport({ url: config.url, headers: config.headers });\n\t\ttry {\n\t\t\tawait transport.request(\"initialize\", {\n\t\t\t\tprotocolVersion: \"2024-11-05\",\n\t\t\t\tcapabilities: {},\n\t\t\t\tclientInfo: { name: \"agent-forge-mcp-pool\", version: \"0.1.0\" },\n\t\t\t});\n\t\t\tawait transport.notify(\"notifications/initialized\");\n\t\t\tconst listing = (await transport.request(\"tools/list\")) as { tools?: unknown };\n\t\t\tif (!listing || !Array.isArray(listing.tools)) {\n\t\t\t\tthrow new Error(t(\"MCP tools/list result must contain tools[]\"));\n\t\t\t}\n\t\t\tfor (const tool of listing.tools) {\n\t\t\t\tconst candidate = tool as { name?: unknown; description?: unknown; inputSchema?: unknown };\n\t\t\t\tif (!candidate || typeof candidate.name !== \"string\" || !candidate.name) {\n\t\t\t\t\tthrow new Error(t(\"MCP tool requires a non-empty name\"));\n\t\t\t\t}\n\t\t\t\tapi.registerTool({\n\t\t\t\t\tname: candidate.name,\n\t\t\t\t\tlabel: candidate.name,\n\t\t\t\t\tdescription:\n\t\t\t\t\t\ttypeof candidate.description === \"string\" ? candidate.description : `MCP tool: ${candidate.name}`,\n\t\t\t\t\tparameters:\n\t\t\t\t\t\tcandidate.inputSchema && typeof candidate.inputSchema === \"object\"\n\t\t\t\t\t\t\t? candidate.inputSchema\n\t\t\t\t\t\t\t: { type: \"object\", properties: {} },\n\t\t\t\t\texecute: async (toolCallId: string, params: unknown, signal?: AbortSignal) => {\n\t\t\t\t\t\t// Dual call convention: the session passes (toolCallId, params,\n\t\t\t\t\t\t// signal); the runtime's invokeTool passes the input as the\n\t\t\t\t\t\t// single first argument.\n\t\t\t\t\t\tconst singleArgCall = params === undefined && typeof toolCallId !== \"string\";\n\t\t\t\t\t\tconst input = singleArgCall ? toolCallId : params;\n\t\t\t\t\t\tconst result = await transport.request(\n\t\t\t\t\t\t\t\"tools/call\",\n\t\t\t\t\t\t\t{ name: candidate.name as string, arguments: input || {} },\n\t\t\t\t\t\t\tsingleArgCall ? undefined : { signal },\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn normalizeToolResult(result);\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tdispose() {\n\t\t\t\t\ttransport.close();\n\t\t\t\t},\n\t\t\t};\n\t\t} catch (error) {\n\t\t\ttransport.close();\n\t\t\tthrow error;\n\t\t}\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mcp-http.js","sourceRoot":"","sources":["../src/mcp-http.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * MCP HTTP transport (4.3) — a streamable-HTTP MCP transport implementing the\n * same transport interface as the stdio plugin, plus observable retry.\n *\n * Retries apply ONLY to requests the server could not have started (connect\n * errors, 5xx, 429) — a completed response (any JSON-RPC reply) is never\n * re-sent, keeping calls idempotent from the caller's perspective. The same\n * idempotency rule makes post-send failures terminal: a response-body read\n * error, a per-request timeout, or a caller abort all mean the request\n * already reached the server, so they are reported once and never re-sent.\n * close() rejects every in-flight request and fails new ones immediately.\n * Every transport error, retry attempt, and reconnect is reported through the\n * `onEvent` callback so callers can observe what happened. CLI\n * productization is out of scope.\n */\n/**\n * Per-request options for MCP transports. Defined here rather than imported\n * from the mcp-stdio example so production src never depends on examples/\n * (统一修复轮 C reverse-dependency fix); structurally identical to the\n * example's own declaration, which stays self-contained.\n */\nimport { t } from \"@agent-forge/host/i18n\";\n\nexport interface McpRequestOptions {\n\tsignal?: AbortSignal;\n\ttimeoutMs?: number;\n}\n\nexport interface McpHttpTransportEventV1 {\n\treadonly kind: \"connect\" | \"transport_error\" | \"retry\" | \"reconnect\" | \"response\";\n\treadonly at: number;\n\treadonly detail: string;\n\treadonly attempt?: number;\n}\n\nexport interface McpHttpTransportOptionsV1 {\n\treadonly url: string;\n\treadonly timeoutMs?: number;\n\treadonly retryLimit?: number;\n\treadonly baseRetryMs?: number;\n\treadonly maxRetryMs?: number;\n\treadonly now?: () => number;\n\treadonly sleep?: (ms: number) => Promise<void> | void;\n\treadonly onEvent?: (event: McpHttpTransportEventV1) => void;\n\t/**\n\t * Extra request headers merged after the transport defaults on every\n\t * outbound request (initialize / notifications/initialized / tools/list /\n\t * tools/call), so user values can override the defaults — the\n\t * `Authorization: Bearer ...` case for hosted remote MCP servers. Values\n\t * are sent verbatim (never case-normalized). They may carry credentials:\n\t * the suite MCP pool materializes them into the generated plugin entry\n\t * below `<agentDir>/mcp/pool/`, which sits inside the same local-machine\n\t * data boundary as `auth.json` (docs/design/产品价值度量与数据边界登记.md\n\t * §2.1 本机数据).\n\t */\n\treadonly headers?: Readonly<Record<string, string>>;\n}\n\n/** Structurally compatible with the stdio plugin's transport interface. */\nexport interface McpHttpTransportV1 {\n\trequest(method: string, params?: unknown, options?: McpRequestOptions): Promise<unknown>;\n\tnotify(method: string, params?: unknown): Promise<void> | void;\n\tclose(): Promise<void> | void;\n\tevents(): readonly McpHttpTransportEventV1[];\n}\n\ninterface JsonRpcResponse {\n\tid?: number | string | null;\n\tresult?: unknown;\n\terror?: { code?: number; message?: string };\n}\n\n/**\n * One postOnce outcome. `connect_error` means the fetch call itself failed\n * before a response arrived (the server could not have started) and stays\n * retryable; `read_error` and `aborted` mean the request already reached the\n * server, so both are terminal and never re-sent.\n */\ntype PostOnceResult =\n\t| { status: number; text: string }\n\t| { status: \"connect_error\"; reason: string }\n\t| { status: \"read_error\"; reason: string }\n\t| { status: \"aborted\"; reason: string };\n\nconst formatError = (error: unknown): string =>\n\terror instanceof Error ? `${error.name}: ${error.message}` : String(error);\n\nexport function createMcpHttpTransport(options: McpHttpTransportOptionsV1): McpHttpTransportV1 {\n\tconst timeoutMs = options.timeoutMs ?? 10_000;\n\tconst retryLimit = options.retryLimit ?? 2;\n\tconst baseRetryMs = options.baseRetryMs ?? 100;\n\tconst maxRetryMs = options.maxRetryMs ?? 1_600;\n\tconst now = options.now ?? (() => Date.now());\n\tconst sleep = options.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n\tconst events: McpHttpTransportEventV1[] = [];\n\tlet nextId = 1;\n\tlet closed = false;\n\n\tconst record = (event: McpHttpTransportEventV1): void => {\n\t\tevents.push(event);\n\t\toptions.onEvent?.(event);\n\t};\n\n\t/** Abort controllers for in-flight postWithRetry runs; close() aborts them all. */\n\tconst inFlight = new Set<AbortController>();\n\n\tconst closedError = (): Error => new Error(t(\"MCP HTTP transport is closed\"));\n\n\tconst postOnce = async (\n\t\tbody: string,\n\t\trequestOptions: McpRequestOptions | undefined,\n\t\tcloseSignal: AbortSignal,\n\t): Promise<PostOnceResult> => {\n\t\tif (requestOptions?.signal?.aborted) {\n\t\t\treturn { status: \"aborted\", reason: t(\"request aborted before it was sent\") };\n\t\t}\n\t\tconst controller = new AbortController();\n\t\tconst timer = setTimeout(() => controller.abort(), requestOptions?.timeoutMs ?? timeoutMs);\n\t\tconst signals: AbortSignal[] = [controller.signal, closeSignal];\n\t\tif (requestOptions?.signal) signals.push(requestOptions.signal);\n\t\ttry {\n\t\t\tconst response = await fetch(options.url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\taccept: \"application/json, text/event-stream\",\n\t\t\t\t\t...(options.headers ?? {}),\n\t\t\t\t},\n\t\t\t\tbody,\n\t\t\t\tsignal: AbortSignal.any(signals),\n\t\t\t});\n\t\t\tlet text: string;\n\t\t\ttry {\n\t\t\t\ttext = await response.text();\n\t\t\t} catch (error) {\n\t\t\t\t// Response headers already arrived: the server may have started\n\t\t\t\t// executing, so a re-send could duplicate side effects.\n\t\t\t\treturn { status: \"read_error\", reason: formatError(error) };\n\t\t\t}\n\t\t\treturn { status: response.status, text };\n\t\t} catch (error) {\n\t\t\t// An abort means the request was already sent (per-request timeout,\n\t\t\t// caller signal, or close), so it must never be re-sent. Only a fetch\n\t\t\t// failure before any response stays classified as connect_error.\n\t\t\tif (controller.signal.aborted || requestOptions?.signal?.aborted) {\n\t\t\t\treturn { status: \"aborted\", reason: formatError(error) };\n\t\t\t}\n\t\t\treturn { status: \"connect_error\", reason: formatError(error) };\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t};\n\n\tconst postWithRetry = async (\n\t\tbody: string,\n\t\trequestOptions?: McpRequestOptions,\n\t): Promise<{ status: number; text: string }> => {\n\t\tconst closeController = new AbortController();\n\t\tinFlight.add(closeController);\n\t\ttry {\n\t\t\tfor (let attempt = 1; ; attempt += 1) {\n\t\t\t\tif (closed) throw closedError();\n\t\t\t\tconst response = await postOnce(body, requestOptions, closeController.signal);\n\t\t\t\tif (closed) throw closedError();\n\t\t\t\tif (response.status === \"read_error\" || response.status === \"aborted\") {\n\t\t\t\t\trecord({\n\t\t\t\t\t\tkind: \"transport_error\",\n\t\t\t\t\t\tat: now(),\n\t\t\t\t\t\tdetail: `${response.status}: ${response.reason}`,\n\t\t\t\t\t\tattempt,\n\t\t\t\t\t});\n\t\t\t\t\tthrow new Error(t(\"MCP HTTP transport error: {reason}\", { reason: response.reason }));\n\t\t\t\t}\n\t\t\t\tif (response.status !== \"connect_error\" && response.status < 500 && response.status !== 429) {\n\t\t\t\t\trecord({ kind: \"response\", at: now(), detail: `HTTP ${response.status}`, attempt });\n\t\t\t\t\treturn response;\n\t\t\t\t}\n\t\t\t\tconst reason = response.status === \"connect_error\" ? response.reason : `HTTP ${response.status}`;\n\t\t\t\tif (attempt > retryLimit) {\n\t\t\t\t\trecord({\n\t\t\t\t\t\tkind: \"transport_error\",\n\t\t\t\t\t\tat: now(),\n\t\t\t\t\t\tdetail: `giving up after ${attempt - 1} retries: ${reason}`,\n\t\t\t\t\t\tattempt,\n\t\t\t\t\t});\n\t\t\t\t\tthrow new Error(t(\"MCP HTTP transport error: {reason}\", { reason }));\n\t\t\t\t}\n\t\t\t\tconst waitedMs = Math.min(baseRetryMs * 2 ** (attempt - 1), maxRetryMs);\n\t\t\t\trecord({\n\t\t\t\t\tkind: \"retry\",\n\t\t\t\t\tat: now(),\n\t\t\t\t\tdetail: `attempt ${attempt} failed (${reason}); waiting ${waitedMs}ms`,\n\t\t\t\t\tattempt,\n\t\t\t\t});\n\t\t\t\tawait sleep(waitedMs);\n\t\t\t\tif (attempt === 1 && response.status === \"connect_error\") {\n\t\t\t\t\trecord({ kind: \"reconnect\", at: now(), detail: \"re-establishing HTTP connection\", attempt });\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tinFlight.delete(closeController);\n\t\t}\n\t};\n\n\tconst rpc = async (method: string, params?: unknown, requestOptions?: McpRequestOptions): Promise<unknown> => {\n\t\tif (closed) throw closedError();\n\t\tconst id = nextId;\n\t\tnextId += 1;\n\t\tconst body = JSON.stringify({ jsonrpc: \"2.0\", id, method, ...(params === undefined ? {} : { params }) });\n\t\tconst response = await postWithRetry(body, requestOptions);\n\t\tif (response.status !== 200) {\n\t\t\tthrow new Error(t(\"MCP HTTP transport returned HTTP {status}\", { status: response.status }));\n\t\t}\n\t\tlet parsed: JsonRpcResponse;\n\t\ttry {\n\t\t\tparsed = JSON.parse(response.text) as JsonRpcResponse;\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t// v8 ignore next -- JSON.parse 只会抛出 Error 对象\n\t\t\t\tt(\"MCP HTTP transport returned non-JSON body: {reason}\", {\n\t\t\t\t\treason: error instanceof Error ? error.message : String(error),\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (parsed.error !== undefined) {\n\t\t\tthrow new Error(\n\t\t\t\tt(\"MCP error {code}: {message}\", {\n\t\t\t\t\tcode: parsed.error.code ?? \"\",\n\t\t\t\t\tmessage: parsed.error.message ?? t(\"unknown error\"),\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn parsed.result;\n\t};\n\n\treturn {\n\t\tasync request(method, params, requestOptions) {\n\t\t\treturn rpc(method, params, requestOptions);\n\t\t},\n\t\tasync notify(method, params) {\n\t\t\tawait postWithRetry(\n\t\t\t\tJSON.stringify({ jsonrpc: \"2.0\", method, ...(params === undefined ? {} : { params }) }),\n\t\t\t\tundefined,\n\t\t\t);\n\t\t},\n\t\tasync close() {\n\t\t\tclosed = true;\n\t\t\t// Reject every in-flight request promptly: aborting its run makes the\n\t\t\t// pending fetch fail and the loop throw the closed error instead of\n\t\t\t// returning a late success or retrying after close.\n\t\t\tfor (const controller of inFlight) {\n\t\t\t\tcontroller.abort(closedError());\n\t\t\t}\n\t\t},\n\t\tevents() {\n\t\t\treturn Object.freeze([...events]);\n\t\t},\n\t};\n}\n\n/** Minimal api surface the HTTP server plugin relies on (capability registerTool). */\nexport interface McpHttpServerPluginApiV1 {\n\tregisterTool(definition: unknown): unknown;\n}\n\nfunction normalizeToolResult(result: unknown): { content: unknown[]; details?: unknown } {\n\tif (result && typeof result === \"object\" && Array.isArray((result as { content?: unknown }).content)) {\n\t\tconst { content, details } = result as { content: unknown[]; details?: unknown };\n\t\treturn { content, details };\n\t}\n\tif (result === undefined) return { content: [{ type: \"text\", text: \"\" }] };\n\treturn { content: [{ type: \"text\", text: JSON.stringify(result) }] };\n}\n\n/**\n * Configuration-driven entry for remote MCP servers: wires\n * createMcpHttpTransport into the same plugin shape as the generated stdio\n * bridge (initialize/tools/list handshake, one registerTool per remote tool,\n * dual call convention). Tool call policy stays host-owned; the plugin only\n * forwards requests and maps results. The suite MCP registration pool\n * materializes url-configured servers through this entry; optional `headers`\n * are forwarded verbatim to the transport (credential note there).\n */\nexport function createMcpHttpServerPlugin(config: {\n\turl: string;\n\t/** Forwarded to the transport verbatim; see McpHttpTransportOptionsV1.headers for the credential note. */\n\theaders?: Readonly<Record<string, string>>;\n}): (api: McpHttpServerPluginApiV1) => Promise<{ dispose(): void }> {\n\treturn async (api) => {\n\t\tconst transport = createMcpHttpTransport({ url: config.url, headers: config.headers });\n\t\ttry {\n\t\t\tawait transport.request(\"initialize\", {\n\t\t\t\tprotocolVersion: \"2024-11-05\",\n\t\t\t\tcapabilities: {},\n\t\t\t\tclientInfo: { name: \"agent-forge-mcp-pool\", version: \"0.1.0\" },\n\t\t\t});\n\t\t\tawait transport.notify(\"notifications/initialized\");\n\t\t\tconst listing = (await transport.request(\"tools/list\")) as { tools?: unknown };\n\t\t\tif (!listing || !Array.isArray(listing.tools)) {\n\t\t\t\tthrow new Error(t(\"MCP tools/list result must contain tools[]\"));\n\t\t\t}\n\t\t\tfor (const tool of listing.tools) {\n\t\t\t\tconst candidate = tool as { name?: unknown; description?: unknown; inputSchema?: unknown };\n\t\t\t\tif (!candidate || typeof candidate.name !== \"string\" || !candidate.name) {\n\t\t\t\t\tthrow new Error(t(\"MCP tool requires a non-empty name\"));\n\t\t\t\t}\n\t\t\t\tapi.registerTool({\n\t\t\t\t\tname: candidate.name,\n\t\t\t\t\tlabel: candidate.name,\n\t\t\t\t\tdescription:\n\t\t\t\t\t\ttypeof candidate.description === \"string\" ? candidate.description : `MCP tool: ${candidate.name}`,\n\t\t\t\t\tparameters:\n\t\t\t\t\t\tcandidate.inputSchema && typeof candidate.inputSchema === \"object\"\n\t\t\t\t\t\t\t? candidate.inputSchema\n\t\t\t\t\t\t\t: { type: \"object\", properties: {} },\n\t\t\t\t\texecute: async (toolCallId: string, params: unknown, signal?: AbortSignal) => {\n\t\t\t\t\t\t// Dual call convention: the session passes (toolCallId, params,\n\t\t\t\t\t\t// signal); the runtime's invokeTool passes the input as the\n\t\t\t\t\t\t// single first argument.\n\t\t\t\t\t\tconst singleArgCall = params === undefined && typeof toolCallId !== \"string\";\n\t\t\t\t\t\tconst input = singleArgCall ? toolCallId : params;\n\t\t\t\t\t\tconst result = await transport.request(\n\t\t\t\t\t\t\t\"tools/call\",\n\t\t\t\t\t\t\t{ name: candidate.name as string, arguments: input || {} },\n\t\t\t\t\t\t\tsingleArgCall ? undefined : { signal },\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn normalizeToolResult(result);\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tdispose() {\n\t\t\t\t\ttransport.close();\n\t\t\t\t},\n\t\t\t};\n\t\t} catch (error) {\n\t\t\ttransport.close();\n\t\t\tthrow error;\n\t\t}\n\t};\n}\n"]}
|