@geohar/pi-mcp-combiner 0.13.3 → 0.14.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/README.md +227 -126
- package/dist/client/config-ladder.d.ts +62 -0
- package/dist/client/config-ladder.d.ts.map +1 -0
- package/dist/client/config-ladder.js +162 -0
- package/dist/client/config-ladder.js.map +1 -0
- package/dist/client/connection.d.ts +113 -0
- package/dist/client/connection.d.ts.map +1 -0
- package/dist/client/connection.js +332 -0
- package/dist/client/connection.js.map +1 -0
- package/dist/client/control.d.ts +17 -0
- package/dist/client/control.d.ts.map +1 -0
- package/dist/client/control.js +80 -0
- package/dist/client/control.js.map +1 -0
- package/dist/client/direct-tools.d.ts +41 -0
- package/dist/client/direct-tools.d.ts.map +1 -0
- package/dist/client/direct-tools.js +122 -0
- package/dist/client/direct-tools.js.map +1 -0
- package/dist/client/elicitation.d.ts +22 -0
- package/dist/client/elicitation.d.ts.map +1 -0
- package/dist/client/elicitation.js +93 -0
- package/dist/client/elicitation.js.map +1 -0
- package/dist/client/footer.d.ts +17 -0
- package/dist/client/footer.d.ts.map +1 -0
- package/dist/client/footer.js +99 -0
- package/dist/client/footer.js.map +1 -0
- package/dist/client/panel.d.ts +16 -0
- package/dist/client/panel.d.ts.map +1 -0
- package/dist/client/panel.js +426 -0
- package/dist/client/panel.js.map +1 -0
- package/dist/client/prompts.d.ts +30 -0
- package/dist/client/prompts.d.ts.map +1 -0
- package/dist/client/prompts.js +230 -0
- package/dist/client/prompts.js.map +1 -0
- package/dist/client/proxy-tool.d.ts +22 -0
- package/dist/client/proxy-tool.d.ts.map +1 -0
- package/dist/client/proxy-tool.js +225 -0
- package/dist/client/proxy-tool.js.map +1 -0
- package/dist/client/ranking.d.ts +21 -0
- package/dist/client/ranking.d.ts.map +1 -0
- package/dist/client/ranking.js +113 -0
- package/dist/client/ranking.js.map +1 -0
- package/dist/client/render.d.ts +28 -0
- package/dist/client/render.d.ts.map +1 -0
- package/dist/client/render.js +166 -0
- package/dist/client/render.js.map +1 -0
- package/dist/client/renderers.d.ts +31 -0
- package/dist/client/renderers.d.ts.map +1 -0
- package/dist/client/renderers.js +191 -0
- package/dist/client/renderers.js.map +1 -0
- package/dist/client/resources.d.ts +20 -0
- package/dist/client/resources.d.ts.map +1 -0
- package/dist/client/resources.js +119 -0
- package/dist/client/resources.js.map +1 -0
- package/dist/client/schema-signature.d.ts +3 -0
- package/dist/client/schema-signature.d.ts.map +1 -0
- package/dist/client/schema-signature.js +183 -0
- package/dist/client/schema-signature.js.map +1 -0
- package/dist/client/script.d.ts +10 -0
- package/dist/client/script.d.ts.map +1 -0
- package/dist/client/script.js +130 -0
- package/dist/client/script.js.map +1 -0
- package/dist/client/settings.d.ts +46 -0
- package/dist/client/settings.d.ts.map +1 -0
- package/dist/client/settings.js +81 -0
- package/dist/client/settings.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +333 -29
- package/dist/index.js.map +1 -1
- package/dist/pi.d.ts +82 -2
- package/dist/pi.d.ts.map +1 -1
- package/dist/pi.js +2 -1
- package/dist/pi.js.map +1 -1
- package/dist/sharedserver-resolve.d.ts.map +1 -1
- package/dist/sharedserver-resolve.js +2 -1
- package/dist/sharedserver-resolve.js.map +1 -1
- package/package.json +68 -57
- package/src/client/config-ladder.ts +221 -0
- package/src/client/connection.ts +373 -0
- package/src/client/control.ts +94 -0
- package/src/client/direct-tools.ts +146 -0
- package/src/client/elicitation.ts +115 -0
- package/src/client/footer.ts +108 -0
- package/src/client/panel.ts +478 -0
- package/src/client/prompts.ts +251 -0
- package/src/client/proxy-tool.ts +261 -0
- package/src/client/ranking.ts +130 -0
- package/src/client/render.ts +166 -0
- package/src/client/renderers.ts +211 -0
- package/src/client/resources.ts +137 -0
- package/src/client/schema-signature.ts +184 -0
- package/src/client/script.ts +150 -0
- package/src/client/settings.ts +109 -0
- package/src/index.ts +384 -32
- package/src/pi.ts +86 -6
- package/src/sharedserver-resolve.ts +10 -3
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Shared-MCP-config reading for the combiner entry — the `.pi/mcp.json` interplay.
|
|
2
|
+
//
|
|
3
|
+
// The extension READS the standard MCP files (pi-mcp-adapter's ladder, mirrored here)
|
|
4
|
+
// but never writes them. It recognizes "its" entry (`mcp-combiner` by name,
|
|
5
|
+
// `x-combiner: true`, or a url whose origin matches the resolved combiner origin) and
|
|
6
|
+
// honors:
|
|
7
|
+
// - `url` (incl. an explicit path token like /mcp/pi-<name>, which wins over our
|
|
8
|
+
// minted per-session token)
|
|
9
|
+
// - `auth`/`bearerTokenEnv` (compat keys; we only ever send a static bearer)
|
|
10
|
+
// - a namespaced `combiner` block for per-project exposure knobs:
|
|
11
|
+
// { "servers": { "allow": [...] | "deny": [...] }, "exposeResources": bool, "prompts": bool }
|
|
12
|
+
// Unknown keys pass through pi-mcp-adapter's lenient validator untouched, so this
|
|
13
|
+
// file stays valid for both readers simultaneously.
|
|
14
|
+
//
|
|
15
|
+
// Later files win (adapter precedence order). Server DEFINITIONS stay global in the
|
|
16
|
+
// combiner's servers.json — the ladder only shapes connection + exposure.
|
|
17
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { agentDir } from "./settings.js";
|
|
21
|
+
/** The ladder, lowest precedence first. Mirrors pi-mcp-adapter's documented order. */
|
|
22
|
+
export function ladderPaths(cwd) {
|
|
23
|
+
return [
|
|
24
|
+
join(homedir(), ".config", "mcp", "mcp.json"),
|
|
25
|
+
join(homedir(), ".agents", "mcp.json"),
|
|
26
|
+
join(homedir(), ".agents", "mcp", "mcp.json"),
|
|
27
|
+
join(agentDir(), "mcp.json"),
|
|
28
|
+
join(cwd, ".mcp.json"),
|
|
29
|
+
join(cwd, ".pi", "mcp.json"),
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
/** Origin (scheme://host:port) of a URL string, or undefined when unparseable. */
|
|
33
|
+
function origin(url) {
|
|
34
|
+
try {
|
|
35
|
+
return new URL(url).origin;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function isRecord(v) {
|
|
42
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
43
|
+
}
|
|
44
|
+
function stringArray(v) {
|
|
45
|
+
return Array.isArray(v) && v.every((x) => typeof x === "string") ? v : undefined;
|
|
46
|
+
}
|
|
47
|
+
/** Recognize "our" entry: explicit name, explicit marker, or url origin match. */
|
|
48
|
+
export function isCombinerEntry(name, entry, combinerOrigin) {
|
|
49
|
+
if (name === "mcp-combiner")
|
|
50
|
+
return true;
|
|
51
|
+
if (entry["x-combiner"] === true)
|
|
52
|
+
return true;
|
|
53
|
+
const url = typeof entry.url === "string" ? entry.url : undefined;
|
|
54
|
+
if (url && combinerOrigin && origin(url) === combinerOrigin)
|
|
55
|
+
return true;
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
/** Parse one file's contribution: the combiner entry (if recognized) + other names. */
|
|
59
|
+
function readOne(path, combinerOrigin) {
|
|
60
|
+
let doc;
|
|
61
|
+
try {
|
|
62
|
+
doc = JSON.parse(readFileSync(path, "utf8"));
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return { others: [] }; // absent or malformed → contributes nothing
|
|
66
|
+
}
|
|
67
|
+
if (!isRecord(doc))
|
|
68
|
+
return { others: [] };
|
|
69
|
+
const servers = doc.mcpServers ?? doc["mcp-servers"];
|
|
70
|
+
if (!isRecord(servers))
|
|
71
|
+
return { others: [] };
|
|
72
|
+
let entry;
|
|
73
|
+
const others = [];
|
|
74
|
+
for (const [name, raw] of Object.entries(servers)) {
|
|
75
|
+
if (!isRecord(raw))
|
|
76
|
+
continue;
|
|
77
|
+
if (isCombinerEntry(name, raw, combinerOrigin)) {
|
|
78
|
+
// Later ladder files replace (not deep-merge) the entry, matching the
|
|
79
|
+
// adapter's later-wins semantics; the `combiner` block merges per-key.
|
|
80
|
+
const prev = entry;
|
|
81
|
+
const block = { ...(prev?.combiner ?? {}) };
|
|
82
|
+
const rawBlock = isRecord(raw.combiner) ? raw.combiner : undefined;
|
|
83
|
+
if (rawBlock) {
|
|
84
|
+
const serversBlock = isRecord(rawBlock.servers) ? rawBlock.servers : undefined;
|
|
85
|
+
if (serversBlock) {
|
|
86
|
+
block.servers = {
|
|
87
|
+
allow: stringArray(serversBlock.allow) ?? block.servers?.allow,
|
|
88
|
+
deny: stringArray(serversBlock.deny) ?? block.servers?.deny,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (typeof rawBlock.exposeResources === "boolean")
|
|
92
|
+
block.exposeResources = rawBlock.exposeResources;
|
|
93
|
+
if (typeof rawBlock.prompts === "boolean")
|
|
94
|
+
block.prompts = rawBlock.prompts;
|
|
95
|
+
if (rawBlock.directTools === "search")
|
|
96
|
+
block.directTools = "search";
|
|
97
|
+
else if (stringArray(rawBlock.directTools)?.length)
|
|
98
|
+
block.directTools = stringArray(rawBlock.directTools);
|
|
99
|
+
}
|
|
100
|
+
entry = {
|
|
101
|
+
url: typeof raw.url === "string" && raw.url ? raw.url : prev?.url,
|
|
102
|
+
bearerTokenEnv: typeof raw.bearerTokenEnv === "string" && raw.bearerTokenEnv
|
|
103
|
+
? raw.bearerTokenEnv
|
|
104
|
+
: prev?.bearerTokenEnv,
|
|
105
|
+
combiner: Object.keys(block).length ? block : prev?.combiner,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
others.push(name);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { entry, others };
|
|
113
|
+
}
|
|
114
|
+
/** Walk the ladder (cwd-relative project files included) and merge. */
|
|
115
|
+
export function readLadder(cwd, combinerOrigin) {
|
|
116
|
+
const out = { entry: {}, otherServers: [], sources: [] };
|
|
117
|
+
for (const path of ladderPaths(cwd)) {
|
|
118
|
+
if (!existsSync(path))
|
|
119
|
+
continue;
|
|
120
|
+
out.sources.push(path);
|
|
121
|
+
const { entry, others } = readOne(path, combinerOrigin);
|
|
122
|
+
if (entry)
|
|
123
|
+
out.entry = entry;
|
|
124
|
+
for (const name of others)
|
|
125
|
+
if (!out.otherServers.includes(name))
|
|
126
|
+
out.otherServers.push(name);
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
/** Extract an explicit /mcp/<token> path token from a URL, if present. */
|
|
131
|
+
export function urlTokenOf(url) {
|
|
132
|
+
const m = url.match(/\/mcp\/([^/?#]+)/);
|
|
133
|
+
return m?.[1];
|
|
134
|
+
}
|
|
135
|
+
/** Normalize a URL back to bare /mcp (strip any token path). */
|
|
136
|
+
export function stripUrlToken(url) {
|
|
137
|
+
return url.replace(/(\/mcp)\/[^/?#]+/, "$1");
|
|
138
|
+
}
|
|
139
|
+
function originOf(url) {
|
|
140
|
+
try {
|
|
141
|
+
return new URL(url).origin;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Resolve the full per-session config. Pure — no env or settings reads inside. */
|
|
148
|
+
export function resolveSessionConfig(opts) {
|
|
149
|
+
const ladder = readLadder(opts.cwd, originOf(opts.defaultUrl));
|
|
150
|
+
const url = opts.envUrl ?? opts.settingsUrl ?? ladder.entry.url ?? opts.defaultUrl;
|
|
151
|
+
return {
|
|
152
|
+
baseUrl: stripUrlToken(url),
|
|
153
|
+
bearerTokenEnv: ladder.entry.bearerTokenEnv,
|
|
154
|
+
urlToken: urlTokenOf(url),
|
|
155
|
+
serverFilter: ladder.entry.combiner?.servers,
|
|
156
|
+
directSpec: ladder.entry.combiner?.directTools,
|
|
157
|
+
entry: ladder.entry,
|
|
158
|
+
otherServers: ladder.otherServers,
|
|
159
|
+
sources: ladder.sources,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=config-ladder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-ladder.js","sourceRoot":"","sources":["../../src/client/config-ladder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/client";
|
|
2
|
+
import { type ElicitUi } from "./elicitation.js";
|
|
3
|
+
import type { ServerFilter } from "./config-ladder.js";
|
|
4
|
+
export type LogFn = (level: "info" | "warn" | "error", message: string) => void;
|
|
5
|
+
export type ResolvedConnection = {
|
|
6
|
+
/** Base URL including /mcp, WITHOUT any token path (e.g. http://127.0.0.1:9741/mcp). */
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
/** Env var holding the inbound bearer token (read at connect time), if any. */
|
|
9
|
+
bearerTokenEnv?: string;
|
|
10
|
+
/** Explicit token already present in the configured URL path (user override). */
|
|
11
|
+
urlToken?: string;
|
|
12
|
+
};
|
|
13
|
+
export type ConnectionState = "disconnected" | "connecting" | "connected" | "failed";
|
|
14
|
+
export type ToolSummary = {
|
|
15
|
+
name: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
inputSchema?: unknown;
|
|
18
|
+
};
|
|
19
|
+
export type PromptArgument = {
|
|
20
|
+
name: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
required?: boolean;
|
|
23
|
+
};
|
|
24
|
+
export type PromptSummary = {
|
|
25
|
+
name: string;
|
|
26
|
+
title?: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
arguments?: PromptArgument[];
|
|
29
|
+
};
|
|
30
|
+
export type ResourceSummary = {
|
|
31
|
+
uri: string;
|
|
32
|
+
name?: string;
|
|
33
|
+
description?: string;
|
|
34
|
+
mimeType?: string;
|
|
35
|
+
_meta?: Record<string, unknown>;
|
|
36
|
+
};
|
|
37
|
+
/** Optional callbacks fired by the connection (set from index.ts wiring). */
|
|
38
|
+
export type ConnectionHooks = {
|
|
39
|
+
/** tools/list_changed arrived — caches invalidated; refresh derived surfaces. */
|
|
40
|
+
onToolsChanged?: () => void;
|
|
41
|
+
/** connection state transitioned — refresh footer/status surfaces. */
|
|
42
|
+
onStateChange?: (state: ConnectionState) => void;
|
|
43
|
+
/** Stage 2: the combiner announced a widget UI URL while an agent tool call
|
|
44
|
+
* is held in flight — the client should open it (gated by uiAutoOpen). */
|
|
45
|
+
onWidgetUrl?: (url: string) => void;
|
|
46
|
+
};
|
|
47
|
+
/** Build the tokened endpoint URL: <origin>/mcp/<token> (combiner's URL form). */
|
|
48
|
+
export declare function tokenedUrl(baseUrl: string, token: string): string;
|
|
49
|
+
export declare class CombinerConnection {
|
|
50
|
+
private conn;
|
|
51
|
+
private readonly log;
|
|
52
|
+
/** Latest dialog-capable UI, refreshed each session_start. */
|
|
53
|
+
private elicitUi;
|
|
54
|
+
private prompts;
|
|
55
|
+
private promptsFetchedAt;
|
|
56
|
+
private resources;
|
|
57
|
+
private resourcesFetchedAt;
|
|
58
|
+
private token;
|
|
59
|
+
private client;
|
|
60
|
+
private connecting;
|
|
61
|
+
private tools;
|
|
62
|
+
private toolsFetchedAt;
|
|
63
|
+
private hooks;
|
|
64
|
+
state: ConnectionState;
|
|
65
|
+
lastError: string | undefined;
|
|
66
|
+
constructor(conn: ResolvedConnection, log: LogFn);
|
|
67
|
+
/** Bind lifecycle hooks (tools changed / state transitions). */
|
|
68
|
+
setHooks(hooks: ConnectionHooks): void;
|
|
69
|
+
/** Effective grouping token for this session (explicit URL token wins). */
|
|
70
|
+
get sessionToken(): string | undefined;
|
|
71
|
+
/** Bind the UI slice used by elicitation (called from session_start). */
|
|
72
|
+
bindUi(ui: ElicitUi): void;
|
|
73
|
+
/** Swap connection inputs (base URL / bearer env) when a session re-resolves a
|
|
74
|
+
* different combiner URL (e.g. a worktree branch pinning its own combiner).
|
|
75
|
+
* No-op when nothing changed; otherwise drops the client for a fresh connect. */
|
|
76
|
+
rebind(conn: ResolvedConnection): boolean;
|
|
77
|
+
/** Set the grouping token for this session; resets any open connection. */
|
|
78
|
+
setToken(token: string): void;
|
|
79
|
+
/** Drop the connection + caches (session reset / shutdown). Never throws. */
|
|
80
|
+
reset(reason: string): Promise<void>;
|
|
81
|
+
/** Effective token: explicit URL token wins over the minted session token. */
|
|
82
|
+
private effectiveToken;
|
|
83
|
+
/** Connect (idempotent, single-flight). Throws with a human message on failure. */
|
|
84
|
+
ensureConnected(): Promise<Client>;
|
|
85
|
+
/** tools/list, cached until invalidated (list_changed / reset / reconnect). */
|
|
86
|
+
listTools(force?: boolean): Promise<ToolSummary[]>;
|
|
87
|
+
/** prompts/list, cached alongside tools (combiner namespaces `<server>_<name>`). */
|
|
88
|
+
listPrompts(force?: boolean): Promise<PromptSummary[]>;
|
|
89
|
+
/** prompts/get through the combiner. */
|
|
90
|
+
getPrompt(name: string, args: Record<string, string>): Promise<unknown>;
|
|
91
|
+
/** resources/list, cached alongside tools/prompts. */
|
|
92
|
+
listResources(force?: boolean): Promise<ResourceSummary[]>;
|
|
93
|
+
/** resources/read through the combiner. */
|
|
94
|
+
readResource(uri: string): Promise<unknown>;
|
|
95
|
+
/** callTool with a single stale-session retry: on transport/stale errors we reset
|
|
96
|
+
* and reconnect once (the combiner may have bounced; handover keeps our token). */
|
|
97
|
+
callTool(name: string, args: Record<string, unknown> | undefined): Promise<unknown>;
|
|
98
|
+
/** Control-plane origin for the combiner's REST routes (/health, /sessions/*). */
|
|
99
|
+
controlOrigin(): string;
|
|
100
|
+
/** The interactive-UI URL for a resource (the combiner's UI host serves the
|
|
101
|
+
* widget for THIS session's token), or empty when no token is set. */
|
|
102
|
+
uiUrlFor(resourceUri: string): string;
|
|
103
|
+
/** Headers for control-plane calls: the same inbound bearer as /mcp (the combiner
|
|
104
|
+
* gates its mutating routes — /sessions*, /handover* — with it; /health is open
|
|
105
|
+
* but presenting anyway is harmless). */
|
|
106
|
+
private controlHeaders;
|
|
107
|
+
/** POST the project-derived server filter for our token. The combiner accepts
|
|
108
|
+
* pending filters pre-connect, so this is safe before the first connect. */
|
|
109
|
+
applyFilter(filter: ServerFilter): Promise<void>;
|
|
110
|
+
/** GET /health — the control-plane health snapshot. */
|
|
111
|
+
health(): Promise<unknown>;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/client/connection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
// The combiner MCP client: one streamable-HTTP connection, one token, reconnect.
|
|
2
|
+
//
|
|
3
|
+
// This is the piece that makes the extension the adapter: it owns the Client +
|
|
4
|
+
// StreamableHTTPClientTransport pair against the combiner's /mcp endpoint, mints the
|
|
5
|
+
// per-Pi-session grouping token into the URL path (URL form beats headers combiner-
|
|
6
|
+
// side, so the token rides the path), sends the optional inbound bearer, bridges
|
|
7
|
+
// elicitation to Pi's UI, and keeps a tools/list cache fresh via tools/list_changed.
|
|
8
|
+
//
|
|
9
|
+
// Lifecycle: lazy by default (connect on first mcp() call), `eager` connects at
|
|
10
|
+
// session_start. A session reset (new/resume/fork/reload) rebinds the token and
|
|
11
|
+
// drops the client; a transport close or stale-session 404 triggers a one-shot
|
|
12
|
+
// reconnect-and-retry on the next call. The combiner's handover means the same token
|
|
13
|
+
// resumes its parked isolated upstream sessions after a combiner restart.
|
|
14
|
+
import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
|
|
15
|
+
import { handleElicitation } from "./elicitation.js";
|
|
16
|
+
const CLIENT_INFO = { name: "pi-mcp-combiner", version: "0.1.0" };
|
|
17
|
+
const CONNECT_TIMEOUT_MS = 8_000;
|
|
18
|
+
function isRecord(v) {
|
|
19
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
20
|
+
}
|
|
21
|
+
/** Pull {message, requestedSchema} out of an elicitation/create request params,
|
|
22
|
+
* tolerating both form-mode and legacy shapes. Returns undefined when unusable. */
|
|
23
|
+
function toElicitParams(raw) {
|
|
24
|
+
const p = isRecord(raw) ? raw : undefined;
|
|
25
|
+
if (!p)
|
|
26
|
+
return undefined;
|
|
27
|
+
const out = {};
|
|
28
|
+
if (typeof p.message === "string")
|
|
29
|
+
out.message = p.message;
|
|
30
|
+
if (isRecord(p.requestedSchema))
|
|
31
|
+
out.requestedSchema = p.requestedSchema;
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
/** Build the tokened endpoint URL: <origin>/mcp/<token> (combiner's URL form). */
|
|
35
|
+
export function tokenedUrl(baseUrl, token) {
|
|
36
|
+
try {
|
|
37
|
+
const u = new URL(baseUrl);
|
|
38
|
+
// Normalize to bare /mcp (strip any existing path segments below it), append token.
|
|
39
|
+
const path = u.pathname.replace(/\/mcp(?:\/[^/]*)*\/?$/, "/mcp");
|
|
40
|
+
u.pathname = `${path.replace(/\/$/, "")}/${encodeURIComponent(token)}`;
|
|
41
|
+
return u.toString();
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return baseUrl;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export class CombinerConnection {
|
|
48
|
+
conn;
|
|
49
|
+
log;
|
|
50
|
+
/** Latest dialog-capable UI, refreshed each session_start. */
|
|
51
|
+
elicitUi = { hasUI: false };
|
|
52
|
+
prompts;
|
|
53
|
+
promptsFetchedAt = 0;
|
|
54
|
+
resources;
|
|
55
|
+
resourcesFetchedAt = 0;
|
|
56
|
+
token;
|
|
57
|
+
client;
|
|
58
|
+
connecting;
|
|
59
|
+
tools;
|
|
60
|
+
toolsFetchedAt = 0;
|
|
61
|
+
hooks = {};
|
|
62
|
+
state = "disconnected";
|
|
63
|
+
lastError;
|
|
64
|
+
constructor(conn, log) {
|
|
65
|
+
this.conn = conn;
|
|
66
|
+
this.log = log;
|
|
67
|
+
}
|
|
68
|
+
/** Bind lifecycle hooks (tools changed / state transitions). */
|
|
69
|
+
setHooks(hooks) {
|
|
70
|
+
this.hooks = hooks;
|
|
71
|
+
}
|
|
72
|
+
/** Effective grouping token for this session (explicit URL token wins). */
|
|
73
|
+
get sessionToken() {
|
|
74
|
+
return this.effectiveToken();
|
|
75
|
+
}
|
|
76
|
+
/** Bind the UI slice used by elicitation (called from session_start). */
|
|
77
|
+
bindUi(ui) {
|
|
78
|
+
this.elicitUi = ui;
|
|
79
|
+
}
|
|
80
|
+
/** Swap connection inputs (base URL / bearer env) when a session re-resolves a
|
|
81
|
+
* different combiner URL (e.g. a worktree branch pinning its own combiner).
|
|
82
|
+
* No-op when nothing changed; otherwise drops the client for a fresh connect. */
|
|
83
|
+
rebind(conn) {
|
|
84
|
+
if (conn.baseUrl === this.conn.baseUrl &&
|
|
85
|
+
conn.bearerTokenEnv === this.conn.bearerTokenEnv &&
|
|
86
|
+
conn.urlToken === this.conn.urlToken) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
this.conn = conn;
|
|
90
|
+
void this.reset("connection rebound (session config changed)");
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
/** Set the grouping token for this session; resets any open connection. */
|
|
94
|
+
setToken(token) {
|
|
95
|
+
if (this.token === token)
|
|
96
|
+
return;
|
|
97
|
+
this.token = token;
|
|
98
|
+
void this.reset("token changed");
|
|
99
|
+
}
|
|
100
|
+
/** Drop the connection + caches (session reset / shutdown). Never throws. */
|
|
101
|
+
async reset(reason) {
|
|
102
|
+
const client = this.client;
|
|
103
|
+
this.client = undefined;
|
|
104
|
+
this.connecting = undefined;
|
|
105
|
+
this.tools = undefined;
|
|
106
|
+
this.state = "disconnected";
|
|
107
|
+
try {
|
|
108
|
+
await client?.close();
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// best-effort
|
|
112
|
+
}
|
|
113
|
+
if (reason)
|
|
114
|
+
this.log("info", `connection reset (${reason})`);
|
|
115
|
+
}
|
|
116
|
+
/** Effective token: explicit URL token wins over the minted session token. */
|
|
117
|
+
effectiveToken() {
|
|
118
|
+
return this.conn.urlToken ?? this.token;
|
|
119
|
+
}
|
|
120
|
+
/** Connect (idempotent, single-flight). Throws with a human message on failure. */
|
|
121
|
+
async ensureConnected() {
|
|
122
|
+
if (this.client)
|
|
123
|
+
return this.client;
|
|
124
|
+
if (this.connecting)
|
|
125
|
+
return this.connecting;
|
|
126
|
+
const token = this.effectiveToken();
|
|
127
|
+
if (!token)
|
|
128
|
+
throw new Error("no grouping token set (session not started)");
|
|
129
|
+
const url = tokenedUrl(this.conn.baseUrl, token);
|
|
130
|
+
let endpoint;
|
|
131
|
+
try {
|
|
132
|
+
endpoint = new URL(url);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
throw new Error(`combiner URL "${url}" is not parseable; set a valid url (…/mcp) in settings or mcp.json`);
|
|
136
|
+
}
|
|
137
|
+
const bearer = this.conn.bearerTokenEnv ? process.env[this.conn.bearerTokenEnv] : undefined;
|
|
138
|
+
this.state = "connecting";
|
|
139
|
+
this.connecting = (async () => {
|
|
140
|
+
const client = new Client(CLIENT_INFO, { capabilities: { elicitation: {} } });
|
|
141
|
+
const transport = new StreamableHTTPClientTransport(endpoint, {
|
|
142
|
+
authProvider: bearer ? { token: async () => bearer } : undefined,
|
|
143
|
+
});
|
|
144
|
+
// Elicitation bridge — the combiner's permission gate depends on it.
|
|
145
|
+
client.setRequestHandler("elicitation/create", (req) => {
|
|
146
|
+
const params = toElicitParams(isRecord(req) ? req.params : undefined);
|
|
147
|
+
if (!params)
|
|
148
|
+
return { action: "decline" };
|
|
149
|
+
return handleElicitation(params, this.elicitUi);
|
|
150
|
+
});
|
|
151
|
+
client.setNotificationHandler("notifications/tools/list_changed", () => {
|
|
152
|
+
this.tools = undefined; // refetch on next list
|
|
153
|
+
this.prompts = undefined;
|
|
154
|
+
this.resources = undefined;
|
|
155
|
+
try {
|
|
156
|
+
this.hooks.onToolsChanged?.();
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// hook errors must never break the notification path
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
client.setNotificationHandler("notifications/resources/list_changed", () => {
|
|
163
|
+
this.resources = undefined;
|
|
164
|
+
try {
|
|
165
|
+
this.hooks.onToolsChanged?.();
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// hook errors must never break the notification path
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
// Stage 2: the widget hold announces its UI URL via a log
|
|
172
|
+
// notification ("Interactive UI ready: <url>") while the agent's
|
|
173
|
+
// call is in flight — open it so the user sees the widget at once.
|
|
174
|
+
client.setNotificationHandler("notifications/message", (n) => {
|
|
175
|
+
const data = n.params?.data;
|
|
176
|
+
if (typeof data !== "string")
|
|
177
|
+
return;
|
|
178
|
+
const m = data.match(/Interactive UI ready: (\S+)/);
|
|
179
|
+
if (m) {
|
|
180
|
+
try {
|
|
181
|
+
this.hooks.onWidgetUrl?.(m[1]);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// hook errors must never break the notification path
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
client.onclose = () => {
|
|
189
|
+
if (this.client === client) {
|
|
190
|
+
this.client = undefined;
|
|
191
|
+
this.state = "disconnected";
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
await Promise.race([
|
|
195
|
+
client.connect(transport),
|
|
196
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("connect timeout")), CONNECT_TIMEOUT_MS)),
|
|
197
|
+
]);
|
|
198
|
+
this.client = client;
|
|
199
|
+
this.state = "connected";
|
|
200
|
+
this.lastError = undefined;
|
|
201
|
+
this.hooks.onStateChange?.(this.state);
|
|
202
|
+
return client;
|
|
203
|
+
})();
|
|
204
|
+
try {
|
|
205
|
+
return await this.connecting;
|
|
206
|
+
}
|
|
207
|
+
catch (e) {
|
|
208
|
+
this.state = "failed";
|
|
209
|
+
this.lastError = e instanceof Error ? e.message : String(e);
|
|
210
|
+
this.log("warn", `combiner connect failed (${this.lastError})`);
|
|
211
|
+
this.hooks.onStateChange?.(this.state);
|
|
212
|
+
throw new Error(`cannot reach the combiner at ${this.conn.baseUrl}: ${this.lastError}`);
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
this.connecting = undefined;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/** tools/list, cached until invalidated (list_changed / reset / reconnect). */
|
|
219
|
+
async listTools(force = false) {
|
|
220
|
+
if (!force && this.tools && Date.now() - this.toolsFetchedAt < 5_000)
|
|
221
|
+
return this.tools;
|
|
222
|
+
const client = await this.ensureConnected();
|
|
223
|
+
const res = await client.listTools();
|
|
224
|
+
this.tools = (res.tools ?? []);
|
|
225
|
+
this.toolsFetchedAt = Date.now();
|
|
226
|
+
return this.tools;
|
|
227
|
+
}
|
|
228
|
+
/** prompts/list, cached alongside tools (combiner namespaces `<server>_<name>`). */
|
|
229
|
+
async listPrompts(force = false) {
|
|
230
|
+
if (!force && this.prompts && Date.now() - this.promptsFetchedAt < 5_000)
|
|
231
|
+
return this.prompts;
|
|
232
|
+
const client = await this.ensureConnected();
|
|
233
|
+
const res = await client.listPrompts();
|
|
234
|
+
this.prompts = (res.prompts ?? []);
|
|
235
|
+
this.promptsFetchedAt = Date.now();
|
|
236
|
+
return this.prompts;
|
|
237
|
+
}
|
|
238
|
+
/** prompts/get through the combiner. */
|
|
239
|
+
async getPrompt(name, args) {
|
|
240
|
+
const client = await this.ensureConnected();
|
|
241
|
+
return client.getPrompt({ name, arguments: args });
|
|
242
|
+
}
|
|
243
|
+
/** resources/list, cached alongside tools/prompts. */
|
|
244
|
+
async listResources(force = false) {
|
|
245
|
+
if (!force && this.resources && Date.now() - this.resourcesFetchedAt < 5_000)
|
|
246
|
+
return this.resources;
|
|
247
|
+
const client = await this.ensureConnected();
|
|
248
|
+
const res = await client.listResources();
|
|
249
|
+
this.resources = (res.resources ?? []);
|
|
250
|
+
this.resourcesFetchedAt = Date.now();
|
|
251
|
+
return this.resources;
|
|
252
|
+
}
|
|
253
|
+
/** resources/read through the combiner. */
|
|
254
|
+
async readResource(uri) {
|
|
255
|
+
const client = await this.ensureConnected();
|
|
256
|
+
return client.readResource({ uri });
|
|
257
|
+
}
|
|
258
|
+
/** callTool with a single stale-session retry: on transport/stale errors we reset
|
|
259
|
+
* and reconnect once (the combiner may have bounced; handover keeps our token). */
|
|
260
|
+
async callTool(name, args) {
|
|
261
|
+
const attempt = async () => {
|
|
262
|
+
const client = await this.ensureConnected();
|
|
263
|
+
return client.callTool({ name, arguments: args ?? {} });
|
|
264
|
+
};
|
|
265
|
+
try {
|
|
266
|
+
return await attempt();
|
|
267
|
+
}
|
|
268
|
+
catch (e) {
|
|
269
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
270
|
+
const stale = /404|stale|session|not found|closed|fetch failed/i.test(msg);
|
|
271
|
+
if (!stale)
|
|
272
|
+
throw e;
|
|
273
|
+
this.log("info", `call "${name}" went stale (${msg}); reconnecting once`);
|
|
274
|
+
await this.reset("stale session");
|
|
275
|
+
return await attempt();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/** Control-plane origin for the combiner's REST routes (/health, /sessions/*). */
|
|
279
|
+
controlOrigin() {
|
|
280
|
+
try {
|
|
281
|
+
return new URL(this.conn.baseUrl).origin;
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return this.conn.baseUrl;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/** The interactive-UI URL for a resource (the combiner's UI host serves the
|
|
288
|
+
* widget for THIS session's token), or empty when no token is set. */
|
|
289
|
+
uiUrlFor(resourceUri) {
|
|
290
|
+
const token = this.effectiveToken();
|
|
291
|
+
if (!token || !resourceUri)
|
|
292
|
+
return "";
|
|
293
|
+
return `${this.controlOrigin()}/ui/${encodeURIComponent(token)}/?resource=${encodeURIComponent(resourceUri)}`;
|
|
294
|
+
}
|
|
295
|
+
/** Headers for control-plane calls: the same inbound bearer as /mcp (the combiner
|
|
296
|
+
* gates its mutating routes — /sessions*, /handover* — with it; /health is open
|
|
297
|
+
* but presenting anyway is harmless). */
|
|
298
|
+
controlHeaders() {
|
|
299
|
+
const token = this.conn.bearerTokenEnv ? process.env[this.conn.bearerTokenEnv] : undefined;
|
|
300
|
+
return token ? { authorization: `Bearer ${token}` } : {};
|
|
301
|
+
}
|
|
302
|
+
/** POST the project-derived server filter for our token. The combiner accepts
|
|
303
|
+
* pending filters pre-connect, so this is safe before the first connect. */
|
|
304
|
+
async applyFilter(filter) {
|
|
305
|
+
const token = this.effectiveToken();
|
|
306
|
+
if (!token)
|
|
307
|
+
throw new Error("no grouping token set (session not started)");
|
|
308
|
+
const body = {};
|
|
309
|
+
if (filter.allow?.length)
|
|
310
|
+
body.allowed_servers = filter.allow;
|
|
311
|
+
else if (filter.deny?.length)
|
|
312
|
+
body.disabled_servers = filter.deny;
|
|
313
|
+
else
|
|
314
|
+
return;
|
|
315
|
+
const res = await fetch(`${this.controlOrigin()}/sessions/token/${encodeURIComponent(token)}/filter`, {
|
|
316
|
+
method: "POST",
|
|
317
|
+
headers: { "content-type": "application/json", ...this.controlHeaders() },
|
|
318
|
+
body: JSON.stringify(body),
|
|
319
|
+
});
|
|
320
|
+
if (!res.ok)
|
|
321
|
+
throw new Error(`filter apply failed: ${res.status} ${await res.text().catch(() => "")}`);
|
|
322
|
+
this.log("info", `session server filter applied (${filter.allow ? "allow" : "deny"}-list)`);
|
|
323
|
+
}
|
|
324
|
+
/** GET /health — the control-plane health snapshot. */
|
|
325
|
+
async health() {
|
|
326
|
+
const res = await fetch(`${this.controlOrigin()}/health`, { headers: this.controlHeaders() });
|
|
327
|
+
if (!res.ok)
|
|
328
|
+
throw new Error(`/health returned ${res.status}`);
|
|
329
|
+
return res.json();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/client/connection.ts"],"names":[],"mappings":"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"}
|