@kenkaiiii/ggcoder 5.26.3 → 5.28.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.
- package/dist/app-sidecar-sessions.d.ts +7 -2
- package/dist/app-sidecar-sessions.d.ts.map +1 -1
- package/dist/app-sidecar-sessions.js +36 -8
- package/dist/app-sidecar-sessions.js.map +1 -1
- package/dist/app-sidecar-sessions.test.js +88 -0
- package/dist/app-sidecar-sessions.test.js.map +1 -1
- package/dist/app-sidecar.js +27 -0
- package/dist/app-sidecar.js.map +1 -1
- package/dist/core/agent-session-process-gate.test.d.ts +2 -0
- package/dist/core/agent-session-process-gate.test.d.ts.map +1 -0
- package/dist/core/agent-session-process-gate.test.js +88 -0
- package/dist/core/agent-session-process-gate.test.js.map +1 -0
- package/dist/core/agent-session.d.ts +40 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +204 -6
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/foreign-session-import.d.ts +120 -0
- package/dist/core/foreign-session-import.d.ts.map +1 -0
- package/dist/core/foreign-session-import.js +594 -0
- package/dist/core/foreign-session-import.js.map +1 -0
- package/dist/core/foreign-session-import.test.d.ts +2 -0
- package/dist/core/foreign-session-import.test.d.ts.map +1 -0
- package/dist/core/foreign-session-import.test.js +220 -0
- package/dist/core/foreign-session-import.test.js.map +1 -0
- package/dist/core/foreign-transcript-blocks.d.ts +51 -0
- package/dist/core/foreign-transcript-blocks.d.ts.map +1 -0
- package/dist/core/foreign-transcript-blocks.js +151 -0
- package/dist/core/foreign-transcript-blocks.js.map +1 -0
- package/dist/core/mcp/catalog-cache.d.ts +63 -0
- package/dist/core/mcp/catalog-cache.d.ts.map +1 -0
- package/dist/core/mcp/catalog-cache.js +163 -0
- package/dist/core/mcp/catalog-cache.js.map +1 -0
- package/dist/core/mcp/catalog-cache.test.d.ts +2 -0
- package/dist/core/mcp/catalog-cache.test.d.ts.map +1 -0
- package/dist/core/mcp/catalog-cache.test.js +99 -0
- package/dist/core/mcp/catalog-cache.test.js.map +1 -0
- package/dist/core/mcp/client.d.ts +61 -0
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +216 -31
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/legacy-negotiation.test.d.ts +2 -0
- package/dist/core/mcp/legacy-negotiation.test.d.ts.map +1 -0
- package/dist/core/mcp/legacy-negotiation.test.js +80 -0
- package/dist/core/mcp/legacy-negotiation.test.js.map +1 -0
- package/dist/core/mcp/oauth-provider.d.ts +1 -2
- package/dist/core/mcp/oauth-provider.d.ts.map +1 -1
- package/dist/core/mcp/oauth-provider.js +5 -1
- package/dist/core/mcp/oauth-provider.js.map +1 -1
- package/dist/core/mcp/oauth-store.d.ts +1 -1
- package/dist/core/mcp/oauth-store.d.ts.map +1 -1
- package/dist/core/mcp/oauth-store.js.map +1 -1
- package/dist/core/mcp/when-connected.test.d.ts +2 -0
- package/dist/core/mcp/when-connected.test.d.ts.map +1 -0
- package/dist/core/mcp/when-connected.test.js +46 -0
- package/dist/core/mcp/when-connected.test.js.map +1 -0
- package/dist/core/process-gate.d.ts +45 -0
- package/dist/core/process-gate.d.ts.map +1 -0
- package/dist/core/process-gate.js +71 -0
- package/dist/core/process-gate.js.map +1 -0
- package/dist/core/process-gate.test.d.ts +2 -0
- package/dist/core/process-gate.test.d.ts.map +1 -0
- package/dist/core/process-gate.test.js +87 -0
- package/dist/core/process-gate.test.js.map +1 -0
- package/dist/core/process-manager.d.ts +9 -0
- package/dist/core/process-manager.d.ts.map +1 -1
- package/dist/core/process-manager.js +17 -15
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/project-discovery.d.ts +19 -0
- package/dist/core/project-discovery.d.ts.map +1 -1
- package/dist/core/project-discovery.js +100 -0
- package/dist/core/project-discovery.js.map +1 -1
- package/dist/core/session-manager.d.ts +5 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +2 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +6 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/tools/tool-search.d.ts +12 -1
- package/dist/tools/tool-search.d.ts.map +1 -1
- package/dist/tools/tool-search.js +22 -3
- package/dist/tools/tool-search.js.map +1 -1
- package/dist/tools/tool-search.test.d.ts +2 -0
- package/dist/tools/tool-search.test.d.ts.map +1 -0
- package/dist/tools/tool-search.test.js +69 -0
- package/dist/tools/tool-search.test.js.map +1 -0
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +27 -1
- package/dist/ui/App.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import { getAppPaths } from "@kenkaiiii/gg-core";
|
|
4
|
+
import { log } from "../logger.js";
|
|
5
|
+
/** Cached entries older than this are ignored — a stale catalog misleads. */
|
|
6
|
+
const MAX_ENTRY_AGE_MS = 30 * 24 * 60 * 60 * 1000;
|
|
7
|
+
/**
|
|
8
|
+
* Stable hash of everything that can change which tools a server exposes.
|
|
9
|
+
* Key order is normalized so a reordered config file is not a cache miss.
|
|
10
|
+
*/
|
|
11
|
+
export function hashServerConfig(config) {
|
|
12
|
+
const material = {
|
|
13
|
+
url: config.url ?? null,
|
|
14
|
+
headers: sortedEntries(config.headers),
|
|
15
|
+
command: config.command ?? null,
|
|
16
|
+
args: config.args ?? null,
|
|
17
|
+
env: sortedEntries(config.env),
|
|
18
|
+
transport: config.transport ?? null,
|
|
19
|
+
};
|
|
20
|
+
return crypto.createHash("sha256").update(JSON.stringify(material)).digest("hex").slice(0, 16);
|
|
21
|
+
}
|
|
22
|
+
function sortedEntries(record) {
|
|
23
|
+
if (!record)
|
|
24
|
+
return null;
|
|
25
|
+
return Object.entries(record).sort(([a], [b]) => a.localeCompare(b));
|
|
26
|
+
}
|
|
27
|
+
function isRecord(value) {
|
|
28
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29
|
+
}
|
|
30
|
+
/** Validate one persisted entry; unknown/partial shapes are dropped, not trusted. */
|
|
31
|
+
function parseEntry(value) {
|
|
32
|
+
if (!isRecord(value))
|
|
33
|
+
return undefined;
|
|
34
|
+
const { configHash, savedAt, protocolEra, tools } = value;
|
|
35
|
+
if (typeof configHash !== "string" || !configHash)
|
|
36
|
+
return undefined;
|
|
37
|
+
if (typeof savedAt !== "number" || !Number.isFinite(savedAt))
|
|
38
|
+
return undefined;
|
|
39
|
+
if (!Array.isArray(tools))
|
|
40
|
+
return undefined;
|
|
41
|
+
const parsedTools = [];
|
|
42
|
+
for (const tool of tools) {
|
|
43
|
+
if (!isRecord(tool))
|
|
44
|
+
continue;
|
|
45
|
+
if (typeof tool.name !== "string" || !tool.name)
|
|
46
|
+
continue;
|
|
47
|
+
parsedTools.push({
|
|
48
|
+
name: tool.name,
|
|
49
|
+
description: typeof tool.description === "string" ? tool.description : "",
|
|
50
|
+
rawInputSchema: isRecord(tool.rawInputSchema) ? tool.rawInputSchema : undefined,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
configHash,
|
|
55
|
+
savedAt,
|
|
56
|
+
protocolEra: protocolEra === "legacy" || protocolEra === "modern" ? protocolEra : undefined,
|
|
57
|
+
tools: parsedTools,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* File-backed MCP tool catalog cache at `~/.gg/mcp-catalog.json`. Every read
|
|
62
|
+
* re-reads the file so concurrent sidecars/windows see each other's writes;
|
|
63
|
+
* writes are read-modify-write so two servers don't clobber one another.
|
|
64
|
+
* Malformed files are treated as empty — a broken cache must never break a run.
|
|
65
|
+
*/
|
|
66
|
+
export class McpCatalogCache {
|
|
67
|
+
filePath;
|
|
68
|
+
/** Serializes read-modify-write cycles within one process. */
|
|
69
|
+
writeQueue = Promise.resolve();
|
|
70
|
+
constructor(filePath = getAppPaths().mcpCatalogFile) {
|
|
71
|
+
this.filePath = filePath;
|
|
72
|
+
}
|
|
73
|
+
async readAll() {
|
|
74
|
+
try {
|
|
75
|
+
const raw = await fs.readFile(this.filePath, "utf-8");
|
|
76
|
+
const parsed = JSON.parse(raw);
|
|
77
|
+
if (!isRecord(parsed) || !isRecord(parsed.servers))
|
|
78
|
+
return { version: 1, servers: {} };
|
|
79
|
+
const servers = {};
|
|
80
|
+
for (const [name, value] of Object.entries(parsed.servers)) {
|
|
81
|
+
const entry = parseEntry(value);
|
|
82
|
+
if (entry)
|
|
83
|
+
servers[name] = entry;
|
|
84
|
+
}
|
|
85
|
+
return { version: 1, servers };
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Missing or malformed: an empty cache is always a safe answer.
|
|
89
|
+
return { version: 1, servers: {} };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async writeAll(data) {
|
|
93
|
+
try {
|
|
94
|
+
await fs.writeFile(this.filePath, JSON.stringify(data, null, 2), {
|
|
95
|
+
encoding: "utf-8",
|
|
96
|
+
mode: 0o600,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
log("WARN", "mcp", "failed to persist MCP catalog cache", {
|
|
101
|
+
message: err instanceof Error ? err.message : String(err),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Cached tools for the given configs, skipping any server whose config hash
|
|
107
|
+
* changed or whose entry has aged out. Servers with no usable entry are
|
|
108
|
+
* simply absent from the result.
|
|
109
|
+
*/
|
|
110
|
+
async entriesFor(configs) {
|
|
111
|
+
const all = await this.readAll();
|
|
112
|
+
const fresh = new Map();
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
for (const config of configs) {
|
|
115
|
+
const entry = all.servers[config.name];
|
|
116
|
+
if (!entry)
|
|
117
|
+
continue;
|
|
118
|
+
if (entry.configHash !== hashServerConfig(config))
|
|
119
|
+
continue;
|
|
120
|
+
if (now - entry.savedAt > MAX_ENTRY_AGE_MS)
|
|
121
|
+
continue;
|
|
122
|
+
fresh.set(config.name, entry);
|
|
123
|
+
}
|
|
124
|
+
return fresh;
|
|
125
|
+
}
|
|
126
|
+
/** Negotiated protocol era recorded for a server, if its config still matches. */
|
|
127
|
+
async protocolEraFor(config) {
|
|
128
|
+
const entries = await this.entriesFor([config]);
|
|
129
|
+
return entries.get(config.name)?.protocolEra;
|
|
130
|
+
}
|
|
131
|
+
/** Record a server's live tool list. Serialized against concurrent saves. */
|
|
132
|
+
async save(config, tools, protocolEra) {
|
|
133
|
+
const run = this.writeQueue.then(async () => {
|
|
134
|
+
const all = await this.readAll();
|
|
135
|
+
all.servers[config.name] = {
|
|
136
|
+
configHash: hashServerConfig(config),
|
|
137
|
+
savedAt: Date.now(),
|
|
138
|
+
protocolEra: protocolEra ?? all.servers[config.name]?.protocolEra,
|
|
139
|
+
tools: tools.map((tool) => ({
|
|
140
|
+
name: tool.name,
|
|
141
|
+
description: tool.description,
|
|
142
|
+
...(tool.rawInputSchema ? { rawInputSchema: tool.rawInputSchema } : {}),
|
|
143
|
+
})),
|
|
144
|
+
};
|
|
145
|
+
await this.writeAll(all);
|
|
146
|
+
});
|
|
147
|
+
this.writeQueue = run.catch(() => undefined);
|
|
148
|
+
await run;
|
|
149
|
+
}
|
|
150
|
+
/** Drop a server's cached tools (removal, or a connect that found none). */
|
|
151
|
+
async clear(name) {
|
|
152
|
+
const run = this.writeQueue.then(async () => {
|
|
153
|
+
const all = await this.readAll();
|
|
154
|
+
if (!all.servers[name])
|
|
155
|
+
return;
|
|
156
|
+
delete all.servers[name];
|
|
157
|
+
await this.writeAll(all);
|
|
158
|
+
});
|
|
159
|
+
this.writeQueue = run.catch(() => undefined);
|
|
160
|
+
await run;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=catalog-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-cache.js","sourceRoot":"","sources":["../../../src/core/mcp/catalog-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-cache.test.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/catalog-cache.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { McpCatalogCache, hashServerConfig } from "./catalog-cache.js";
|
|
6
|
+
let dir;
|
|
7
|
+
let file;
|
|
8
|
+
const server = {
|
|
9
|
+
name: "kencode-search",
|
|
10
|
+
command: "node",
|
|
11
|
+
args: ["server.js"],
|
|
12
|
+
};
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
dir = await fs.mkdtemp(path.join(os.tmpdir(), "gg-mcp-catalog-"));
|
|
15
|
+
file = path.join(dir, "mcp-catalog.json");
|
|
16
|
+
});
|
|
17
|
+
afterEach(async () => {
|
|
18
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
19
|
+
});
|
|
20
|
+
describe("hashServerConfig", () => {
|
|
21
|
+
it("is stable across key and header ordering", () => {
|
|
22
|
+
const a = { name: "x", url: "https://x", headers: { a: "1", b: "2" } };
|
|
23
|
+
const b = { name: "x", headers: { b: "2", a: "1" }, url: "https://x" };
|
|
24
|
+
expect(hashServerConfig(a)).toBe(hashServerConfig(b));
|
|
25
|
+
});
|
|
26
|
+
it("changes when the command or args change", () => {
|
|
27
|
+
expect(hashServerConfig(server)).not.toBe(hashServerConfig({ ...server, args: ["other.js"] }));
|
|
28
|
+
expect(hashServerConfig(server)).not.toBe(hashServerConfig({ ...server, command: "bun" }));
|
|
29
|
+
});
|
|
30
|
+
it("ignores the fields that cannot change the exposed tools", () => {
|
|
31
|
+
expect(hashServerConfig(server)).toBe(hashServerConfig({ ...server, enabled: false, timeout: 999 }));
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe("McpCatalogCache", () => {
|
|
35
|
+
it("round-trips tools and the negotiated protocol era", async () => {
|
|
36
|
+
const cache = new McpCatalogCache(file);
|
|
37
|
+
await cache.save(server, [{ name: "mcp__kencode-search__search", description: "search code", rawInputSchema: {} }], "legacy");
|
|
38
|
+
const entries = await new McpCatalogCache(file).entriesFor([server]);
|
|
39
|
+
expect(entries.get(server.name)?.tools).toEqual([
|
|
40
|
+
{ name: "mcp__kencode-search__search", description: "search code", rawInputSchema: {} },
|
|
41
|
+
]);
|
|
42
|
+
expect(await cache.protocolEraFor(server)).toBe("legacy");
|
|
43
|
+
});
|
|
44
|
+
it("invalidates an entry when the server config changes", async () => {
|
|
45
|
+
const cache = new McpCatalogCache(file);
|
|
46
|
+
await cache.save(server, [{ name: "mcp__kencode-search__search", description: "x" }]);
|
|
47
|
+
const changed = { ...server, args: ["different.js"] };
|
|
48
|
+
expect((await cache.entriesFor([changed])).size).toBe(0);
|
|
49
|
+
expect((await cache.entriesFor([server])).size).toBe(1);
|
|
50
|
+
});
|
|
51
|
+
it("ignores servers that were never cached", async () => {
|
|
52
|
+
const cache = new McpCatalogCache(file);
|
|
53
|
+
await cache.save(server, [{ name: "mcp__kencode-search__search", description: "x" }]);
|
|
54
|
+
expect((await cache.entriesFor([{ name: "other", command: "node" }])).size).toBe(0);
|
|
55
|
+
});
|
|
56
|
+
it("treats a malformed cache file as empty instead of throwing", async () => {
|
|
57
|
+
await fs.writeFile(file, "{ not json", "utf-8");
|
|
58
|
+
const cache = new McpCatalogCache(file);
|
|
59
|
+
expect((await cache.entriesFor([server])).size).toBe(0);
|
|
60
|
+
// And it recovers by overwriting.
|
|
61
|
+
await cache.save(server, [{ name: "mcp__kencode-search__search", description: "x" }]);
|
|
62
|
+
expect((await cache.entriesFor([server])).size).toBe(1);
|
|
63
|
+
});
|
|
64
|
+
it("drops entries with an invalid shape but keeps the valid ones", async () => {
|
|
65
|
+
const other = { name: "other", command: "node", args: ["o.js"] };
|
|
66
|
+
await new McpCatalogCache(file).save(other, [{ name: "mcp__other__a", description: "a" }]);
|
|
67
|
+
const raw = JSON.parse(await fs.readFile(file, "utf-8"));
|
|
68
|
+
raw.servers[server.name] = { configHash: 42, tools: "nope" };
|
|
69
|
+
await fs.writeFile(file, JSON.stringify(raw), "utf-8");
|
|
70
|
+
const entries = await new McpCatalogCache(file).entriesFor([server, other]);
|
|
71
|
+
expect(entries.has(server.name)).toBe(false);
|
|
72
|
+
expect(entries.get(other.name)?.tools).toHaveLength(1);
|
|
73
|
+
});
|
|
74
|
+
it("ignores entries that have aged out", async () => {
|
|
75
|
+
const cache = new McpCatalogCache(file);
|
|
76
|
+
await cache.save(server, [{ name: "mcp__kencode-search__search", description: "x" }]);
|
|
77
|
+
const raw = JSON.parse(await fs.readFile(file, "utf-8"));
|
|
78
|
+
raw.servers[server.name].savedAt = Date.now() - 400 * 24 * 60 * 60 * 1000;
|
|
79
|
+
await fs.writeFile(file, JSON.stringify(raw), "utf-8");
|
|
80
|
+
expect((await cache.entriesFor([server])).size).toBe(0);
|
|
81
|
+
});
|
|
82
|
+
it("does not lose a concurrent save from another server", async () => {
|
|
83
|
+
const cache = new McpCatalogCache(file);
|
|
84
|
+
const other = { name: "other", command: "node", args: ["o.js"] };
|
|
85
|
+
await Promise.all([
|
|
86
|
+
cache.save(server, [{ name: "mcp__kencode-search__search", description: "x" }]),
|
|
87
|
+
cache.save(other, [{ name: "mcp__other__a", description: "a" }]),
|
|
88
|
+
]);
|
|
89
|
+
const entries = await cache.entriesFor([server, other]);
|
|
90
|
+
expect([...entries.keys()].sort()).toEqual(["kencode-search", "other"]);
|
|
91
|
+
});
|
|
92
|
+
it("clears a server's entry", async () => {
|
|
93
|
+
const cache = new McpCatalogCache(file);
|
|
94
|
+
await cache.save(server, [{ name: "mcp__kencode-search__search", description: "x" }]);
|
|
95
|
+
await cache.clear(server.name);
|
|
96
|
+
expect((await cache.entriesFor([server])).size).toBe(0);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
//# sourceMappingURL=catalog-cache.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-cache.test.js","sourceRoot":"","sources":["../../../src/core/mcp/catalog-cache.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentTool } from "@kenkaiiii/gg-agent";
|
|
2
2
|
import type { MCPServerConfig } from "./types.js";
|
|
3
|
+
import { McpCatalogCache } from "./catalog-cache.js";
|
|
3
4
|
/** Per-server connection outcome for the dashboard / non-interactive list. */
|
|
4
5
|
export interface MCPConnectResult {
|
|
5
6
|
name: string;
|
|
@@ -17,8 +18,59 @@ export interface MCPLoginResult {
|
|
|
17
18
|
toolCount: number;
|
|
18
19
|
error?: string;
|
|
19
20
|
}
|
|
21
|
+
/** Terminal state of one server's connection attempt, awaited by `whenConnected`. */
|
|
22
|
+
type ConnectionOutcome = {
|
|
23
|
+
ok: true;
|
|
24
|
+
} | {
|
|
25
|
+
ok: false;
|
|
26
|
+
error: string;
|
|
27
|
+
};
|
|
20
28
|
export declare class MCPClientManager {
|
|
21
29
|
private servers;
|
|
30
|
+
/**
|
|
31
|
+
* Per-server connection settlement, so a caller holding a cached-only tool
|
|
32
|
+
* can wait for the live client instead of failing or hanging forever.
|
|
33
|
+
*/
|
|
34
|
+
private connections;
|
|
35
|
+
private readonly catalogCache;
|
|
36
|
+
/**
|
|
37
|
+
* Opt into the 2026-07-28 revision. Off by default: `mode: "auto"` probes with
|
|
38
|
+
* `server/discover` before falling back to `initialize`, and a legacy stdio
|
|
39
|
+
* server that ignores pre-`initialize` requests pays the full probe timeout.
|
|
40
|
+
*/
|
|
41
|
+
private readonly modernProtocol;
|
|
42
|
+
constructor(opts?: {
|
|
43
|
+
catalogCache?: McpCatalogCache;
|
|
44
|
+
modernProtocol?: boolean;
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* Version-negotiation options for a real (non-probe) connect. `probe()` is
|
|
48
|
+
* spawn-per-invocation and deliberately never negotiates: a legacy stdio
|
|
49
|
+
* server that ignores the discovery request would stall it for the whole
|
|
50
|
+
* probe timeout, turning "validate this server" into a 30s hang.
|
|
51
|
+
*
|
|
52
|
+
* The probe timeout is transport-aware because the SDK's timeout VERDICT is:
|
|
53
|
+
*
|
|
54
|
+
* - **stdio** — silence on a local pipe means a legacy server, and the SDK
|
|
55
|
+
* falls back to `initialize`. The probe runs on a disposable sibling
|
|
56
|
+
* process, so the fallback is clean. Waiting the full connect timeout to
|
|
57
|
+
* reach a conclusion we can draw in seconds is pure dead time, so cap it
|
|
58
|
+
* short.
|
|
59
|
+
* - **HTTP** — silence means an outage, and the SDK REJECTS the connect
|
|
60
|
+
* rather than falling back. A short timeout would therefore turn a slow
|
|
61
|
+
* cold start into a hard connection failure, so inherit the full connect
|
|
62
|
+
* timeout and let the normal timeout handling deal with a real outage.
|
|
63
|
+
*/
|
|
64
|
+
private negotiationOptions;
|
|
65
|
+
/** Get-or-create the settlement record for one server name. */
|
|
66
|
+
private connectionSlot;
|
|
67
|
+
/**
|
|
68
|
+
* Resolve once a server's connection attempt has settled. Returns `ok:false`
|
|
69
|
+
* with the failure reason when that server could not connect, and times out
|
|
70
|
+
* rather than hanging when no attempt is ever made (e.g. a server that was
|
|
71
|
+
* removed from the config since the catalog cache was written).
|
|
72
|
+
*/
|
|
73
|
+
whenConnected(name: string, timeoutMs?: number): Promise<ConnectionOutcome>;
|
|
22
74
|
connectAll(configs: MCPServerConfig[]): Promise<AgentTool[]>;
|
|
23
75
|
/**
|
|
24
76
|
* Connect every enabled server and return one result per server (success →
|
|
@@ -56,6 +108,15 @@ export declare class MCPClientManager {
|
|
|
56
108
|
* protocol mismatch as a login requirement.
|
|
57
109
|
*/
|
|
58
110
|
private connectHttp;
|
|
111
|
+
/**
|
|
112
|
+
* Protocol era actually negotiated with a server, as the SDK reports it.
|
|
113
|
+
* Undefined before a connect completes; with negotiation off the SDK always
|
|
114
|
+
* settles on `legacy` (the 2025 `initialize` handshake).
|
|
115
|
+
*/
|
|
116
|
+
private negotiatedEra;
|
|
117
|
+
/** Previously negotiated era for this exact server config, if still cached. */
|
|
118
|
+
private cachedEra;
|
|
59
119
|
dispose(): Promise<void>;
|
|
60
120
|
}
|
|
121
|
+
export {};
|
|
61
122
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/client.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AASlD,OAAO,EAAE,eAAe,EAAoB,MAAM,oBAAoB,CAAC;AASvE,8EAA8E;AAC9E,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;oFACgF;IAChF,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qFAAqF;AACrF,KAAK,iBAAiB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAAyB;IACxC;;;OAGG;IACH,OAAO,CAAC,WAAW,CAGf;IAEJ,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkB;IAC/C;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;gBAE7B,IAAI,GAAE;QAAE,YAAY,CAAC,EAAE,eAAe,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAO;IAKnF;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,kBAAkB;IAU1B,+DAA+D;IAC/D,OAAO,CAAC,cAAc;IAetB;;;;;OAKG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAkB3E,UAAU,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAKlE;;;;OAIG;IACG,kBAAkB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAmCjF;;;;OAIG;IACG,KAAK,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAoB/D;;;;;;;;;;OAUG;IACG,KAAK,CACT,MAAM,EAAE,eAAe,EACvB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EACzC,SAAS,SAAU,GAClB,OAAO,CAAC,cAAc,CAAC;YA2HZ,aAAa;IAkK3B;;;;;;;;;OASG;YACW,WAAW;IAkDzB;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAIrB,+EAA+E;YACjE,SAAS;IAQjB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAW/B"}
|