synartesis 0.1.1 → 0.2.1

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/proxy.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  runRead,
20
20
  toPayload,
21
21
  verifyAgainstServers
22
- } from "./chunk-X4VQNEP5.js";
22
+ } from "./chunk-7AUGXEWC.js";
23
23
  import {
24
24
  SnapshotError,
25
25
  UpstreamError,
@@ -30,6 +30,163 @@ import {
30
30
  import { resolve } from "path";
31
31
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
32
32
 
33
+ // src/proxy/http.ts
34
+ import { createServer } from "http";
35
+ import { randomUUID, timingSafeEqual } from "crypto";
36
+ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
37
+ function toRequest(req, body, origin) {
38
+ const headers = new Headers();
39
+ for (const [key, value] of Object.entries(req.headers)) {
40
+ if (typeof value === "string") {
41
+ headers.set(key, value);
42
+ } else if (Array.isArray(value)) {
43
+ for (const one of value) {
44
+ headers.append(key, one);
45
+ }
46
+ }
47
+ }
48
+ const method = req.method ?? "GET";
49
+ return new Request(new URL(req.url ?? "/", origin), {
50
+ method,
51
+ headers,
52
+ // A GET or HEAD may not carry one, and node sends an empty buffer anyway.
53
+ ...method === "GET" || method === "HEAD" ? {} : { body }
54
+ });
55
+ }
56
+ async function writeResponse(res, response) {
57
+ const headers = {};
58
+ response.headers.forEach((value, key) => {
59
+ headers[key] = value;
60
+ });
61
+ res.writeHead(response.status, headers);
62
+ if (response.body === null) {
63
+ res.end();
64
+ return;
65
+ }
66
+ for await (const chunk of response.body) {
67
+ res.write(Buffer.from(chunk));
68
+ }
69
+ res.end();
70
+ }
71
+ function tokenMatches(given, expected) {
72
+ const a = Buffer.from(given);
73
+ const b = Buffer.from(expected);
74
+ return a.length === b.length && timingSafeEqual(a, b);
75
+ }
76
+ function bearer(req) {
77
+ const header = req.headers.authorization;
78
+ if (typeof header !== "string") {
79
+ return void 0;
80
+ }
81
+ const match = /^Bearer[ ]+(.+)$/i.exec(header.trim());
82
+ return match?.[1];
83
+ }
84
+ function refuse(res, status, message) {
85
+ res.writeHead(status, {
86
+ "content-type": "application/json",
87
+ // Told the same way twice: the header is what a client acts on, the body
88
+ // is what a person reads in a terminal.
89
+ ...status === 401 ? { "www-authenticate": 'Bearer realm="synartesis"' } : {}
90
+ });
91
+ res.end(JSON.stringify({ error: message }));
92
+ }
93
+ async function readRaw(req) {
94
+ const chunks = [];
95
+ let size = 0;
96
+ for await (const chunk of req) {
97
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
98
+ size += buffer.length;
99
+ if (size > 8 * 1024 * 1024) {
100
+ throw new Error("request body too large");
101
+ }
102
+ chunks.push(buffer);
103
+ }
104
+ return Buffer.concat(chunks);
105
+ }
106
+ function isInitialize(body) {
107
+ const one = (message) => typeof message === "object" && message !== null && "method" in message && message.method === "initialize";
108
+ return Array.isArray(body) ? body.some(one) : one(body);
109
+ }
110
+ async function serveHttp(options) {
111
+ const sessions = /* @__PURE__ */ new Map();
112
+ const server = createServer((req, res) => {
113
+ void (async () => {
114
+ try {
115
+ const token = bearer(req);
116
+ if (token === void 0 || !tokenMatches(token, options.token)) {
117
+ refuse(res, 401, "a bearer token is required");
118
+ return;
119
+ }
120
+ if (req.url !== void 0 && !req.url.startsWith("/mcp")) {
121
+ refuse(res, 404, "the endpoint is /mcp");
122
+ return;
123
+ }
124
+ const origin = `http://${options.host}:${String(options.port)}`;
125
+ const raw = await readRaw(req);
126
+ const sessionId = req.headers["mcp-session-id"];
127
+ const existing = typeof sessionId === "string" ? sessions.get(sessionId) : void 0;
128
+ if (existing !== void 0) {
129
+ await writeResponse(res, await existing.transport.handleRequest(toRequest(req, raw, origin)));
130
+ return;
131
+ }
132
+ const body = req.method === "POST" && raw.length > 0 ? JSON.parse(raw.toString("utf8")) : void 0;
133
+ if (req.method !== "POST" || !isInitialize(body)) {
134
+ refuse(res, 400, "no such session; start one with an initialize request");
135
+ return;
136
+ }
137
+ const proxy = options.create();
138
+ const transport = new WebStandardStreamableHTTPServerTransport({
139
+ sessionIdGenerator: () => randomUUID(),
140
+ onsessioninitialized: (id) => {
141
+ sessions.set(id, { transport, proxy });
142
+ options.log.info({ session: id }, "http session opened");
143
+ }
144
+ });
145
+ await proxy.server.connect(transport);
146
+ transport.onclose = () => {
147
+ const id = transport.sessionId;
148
+ if (id !== void 0) {
149
+ sessions.delete(id);
150
+ }
151
+ };
152
+ await writeResponse(res, await transport.handleRequest(toRequest(req, raw, origin)));
153
+ } catch (error) {
154
+ const message = error instanceof Error ? error.message : String(error);
155
+ if (!res.headersSent) {
156
+ refuse(res, 400, message);
157
+ } else {
158
+ res.end();
159
+ }
160
+ }
161
+ })();
162
+ });
163
+ await new Promise((resolve2) => {
164
+ server.listen(options.port, options.host, resolve2);
165
+ });
166
+ const address = server.address();
167
+ const port = typeof address === "object" && address !== null ? address.port : options.port;
168
+ if (options.host !== "127.0.0.1" && options.host !== "localhost") {
169
+ options.log.warn(
170
+ `listening on ${options.host}, which is not loopback: anything that can reach this port and holds the token can write through your servers`
171
+ );
172
+ }
173
+ options.log.info({ host: options.host, port, endpoint: "/mcp" }, "http proxy ready");
174
+ return {
175
+ port,
176
+ close: async () => {
177
+ for (const { transport } of sessions.values()) {
178
+ await transport.close().catch(() => void 0);
179
+ }
180
+ sessions.clear();
181
+ await new Promise((resolve2) => {
182
+ server.close(() => {
183
+ resolve2();
184
+ });
185
+ });
186
+ }
187
+ };
188
+ }
189
+
33
190
  // src/gate/gate.ts
34
191
  var DEFAULT_GATE_TIMEOUT_MS = 3e5;
35
192
  var DEFAULT_HINT = (actionId) => `synartesis approve ${actionId.slice(0, 8)}`;
@@ -795,7 +952,7 @@ function parseArgv(argv) {
795
952
  const at = argv.indexOf(flag);
796
953
  return at === -1 ? void 0 : argv[at + 1];
797
954
  };
798
- const known = ["--manifest", "--journal", "--gate-timeout", "--log-level"];
955
+ const known = ["--manifest", "--journal", "--gate-timeout", "--log-level", "--http", "--http-host", "--token"];
799
956
  const unknown = argv.find((token) => token.startsWith("--") && !known.includes(token));
800
957
  if (unknown !== void 0) {
801
958
  throw new Error(`unknown flag ${unknown}; expected one of ${known.join(", ")}`);
@@ -809,17 +966,39 @@ function parseArgv(argv) {
809
966
  if (!isLogLevel(level)) {
810
967
  throw new Error(`--log-level must be one of ${LOG_LEVELS.join(", ")}`);
811
968
  }
969
+ const httpPort = read("--http");
970
+ let http;
971
+ if (httpPort !== void 0) {
972
+ const port = Number(httpPort);
973
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
974
+ throw new Error("--http needs a port number");
975
+ }
976
+ const token = read("--token") ?? process.env["SYNARTESIS_TOKEN"];
977
+ if (token === void 0 || token.length < 16) {
978
+ throw new Error(
979
+ "--http needs --token, or SYNARTESIS_TOKEN, of at least 16 characters: this serves write access over a socket"
980
+ );
981
+ }
982
+ http = { port, host: read("--http-host") ?? "127.0.0.1", token };
983
+ }
812
984
  const manifest = findManifest(read("--manifest"));
813
985
  return {
814
986
  manifest,
815
987
  journal: findJournal(read("--journal"), manifest),
816
988
  gateTimeoutMs: seconds === void 0 ? DEFAULT_GATE_TIMEOUT_MS : seconds * 1e3,
989
+ gateTimeoutGiven: seconds !== void 0,
990
+ ...http === void 0 ? {} : { http },
817
991
  logLevel: level
818
992
  };
819
993
  }
820
994
  async function main() {
821
995
  const argv = parseArgv(process.argv.slice(2));
822
996
  const log = createLogger(argv.logLevel);
997
+ if (argv.gateTimeoutGiven) {
998
+ log.warn(
999
+ "--gate-timeout has no effect: a held call is refused immediately and the agent makes it again once you approve"
1000
+ );
1001
+ }
823
1002
  if (process.stderr.isTTY) {
824
1003
  process.stderr.write(mark());
825
1004
  }
@@ -846,7 +1025,7 @@ async function main() {
846
1025
  },
847
1026
  "proxy ready"
848
1027
  );
849
- const proxy = createProxyServer({
1028
+ const build = () => createProxyServer({
850
1029
  upstreams,
851
1030
  manifest,
852
1031
  journal,
@@ -855,6 +1034,34 @@ async function main() {
855
1034
  // Absolute, because whoever approves may be in any directory at all.
856
1035
  approveHint: (actionId) => `${cliCommandFrom(import.meta.url)} approve ${actionId.slice(0, 8)} --journal ${resolve(argv.journal)}`
857
1036
  });
1037
+ if (argv.http !== void 0) {
1038
+ const served = await serveHttp({
1039
+ ...argv.http,
1040
+ create: build,
1041
+ log: {
1042
+ info: (data, message) => {
1043
+ log.info(data, message);
1044
+ },
1045
+ warn: (message) => {
1046
+ log.warn(message);
1047
+ }
1048
+ }
1049
+ });
1050
+ const stop = () => {
1051
+ void (async () => {
1052
+ await served.close();
1053
+ for (const upstream of upstreams) {
1054
+ await upstream.close();
1055
+ }
1056
+ journal.close();
1057
+ process.exit(0);
1058
+ })();
1059
+ };
1060
+ process.on("SIGINT", stop);
1061
+ process.on("SIGTERM", stop);
1062
+ return;
1063
+ }
1064
+ const proxy = build();
858
1065
  let shuttingDown = false;
859
1066
  const shutdown = (code) => {
860
1067
  if (shuttingDown) {
package/dist/proxy.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/proxy/stdio.ts","../src/gate/gate.ts","../src/logging.ts","../src/proxy/proxy.ts","../src/gate/heuristic.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * better-sqlite3 requires Node 22, and on Node 20 it does not fail politely:\n * it segfaults the moment a database is opened. Saying so is better than\n * letting somebody meet exit code 139.\n */\nconst NODE_MAJOR = Number(process.versions.node.split(\".\")[0]);\nif (NODE_MAJOR < 22) {\n process.stderr.write(\n `synartesis: needs Node 22 or newer, and this is ${process.version}.\\n`,\n );\n process.exit(2);\n}\n\nimport { resolve } from \"node:path\";\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nimport { describe } from \"../errors.js\";\nimport { DEFAULT_GATE_TIMEOUT_MS } from \"../gate/gate.js\";\nimport { cliCommandFrom } from \"../invocation.js\";\nimport { findJournal, findManifest } from \"../locate.js\";\nimport { createLogger, isLogLevel, LOG_LEVELS, type LogLevel } from \"../logging.js\";\nimport { mark } from \"../style.js\";\nimport { openJournal } from \"../journal/journal.js\";\nimport { loadManifest } from \"../manifest/load.js\";\nimport { verifyAgainstServers } from \"../manifest/verify.js\";\nimport { createProxyServer } from \"./proxy.js\";\nimport { connectStdioUpstream, type Upstream } from \"./upstream.js\";\n\n/**\n * The manifest is the configuration (D3): it already declares every server and\n * how to start it, so there is nothing left for flags to say.\n *\n * synartesis-proxy [--manifest synartesis.yaml] [--journal .synartesis/journal.db]\n * [--gate-timeout <seconds>] [--log-level <level>]\n */\ninterface Argv {\n readonly manifest: string;\n readonly journal: string;\n readonly gateTimeoutMs: number;\n readonly logLevel: LogLevel;\n}\n\nfunction parseArgv(argv: readonly string[]): Argv {\n const read = (flag: string): string | undefined => {\n const at = argv.indexOf(flag);\n return at === -1 ? undefined : argv[at + 1];\n };\n const known = [\"--manifest\", \"--journal\", \"--gate-timeout\", \"--log-level\"];\n const unknown = argv.find((token) => token.startsWith(\"--\") && !known.includes(token));\n if (unknown !== undefined) {\n throw new Error(`unknown flag ${unknown}; expected one of ${known.join(\", \")}`);\n }\n\n const rawTimeout = read(\"--gate-timeout\");\n const seconds = rawTimeout === undefined ? undefined : Number(rawTimeout);\n if (seconds !== undefined && (!Number.isFinite(seconds) || seconds <= 0)) {\n throw new Error(\"--gate-timeout needs a positive number of seconds\");\n }\n\n const level = read(\"--log-level\") ?? \"info\";\n if (!isLogLevel(level)) {\n throw new Error(`--log-level must be one of ${LOG_LEVELS.join(\", \")}`);\n }\n\n const manifest = findManifest(read(\"--manifest\"));\n return {\n manifest,\n journal: findJournal(read(\"--journal\"), manifest),\n gateTimeoutMs: seconds === undefined ? DEFAULT_GATE_TIMEOUT_MS : seconds * 1000,\n logLevel: level,\n };\n}\n\nasync function main(): Promise<void> {\n const argv = parseArgv(process.argv.slice(2));\n const log = createLogger(argv.logLevel);\n // Only on a real terminal. A client collecting our stderr into a log file\n // wants the structured records and nothing else.\n if (process.stderr.isTTY) {\n process.stderr.write(mark());\n }\n // Loaded before anything is spawned: never start with a broken policy.\n const manifest = loadManifest(argv.manifest);\n const journal = openJournal(argv.journal);\n\n const upstreams: Upstream[] = [];\n for (const [name, spec] of Object.entries(manifest.servers)) {\n upstreams.push(\n await connectStdioUpstream({\n name,\n command: spec.command,\n args: spec.args,\n ...(spec.env === undefined ? {} : { env: spec.env }),\n }),\n );\n }\n\n // Never serve a request under a policy that calls tools the servers do not\n // have: at run time that is indistinguishable from a missing resource.\n await verifyAgainstServers(upstreams, manifest);\n\n log.info(\n {\n manifest: argv.manifest,\n journal: argv.journal,\n servers: upstreams.map((upstream) => upstream.name),\n policies: manifest.tools.length,\n },\n \"proxy ready\",\n );\n\n const proxy = createProxyServer({\n upstreams,\n manifest,\n journal,\n gateTimeoutMs: argv.gateTimeoutMs,\n logger: log,\n // Absolute, because whoever approves may be in any directory at all.\n approveHint: (actionId: string): string =>\n `${cliCommandFrom(import.meta.url)} approve ${actionId.slice(0, 8)} --journal ${resolve(argv.journal)}`,\n });\n\n let shuttingDown = false;\n const shutdown = (code: number): void => {\n if (shuttingDown) {\n return;\n }\n shuttingDown = true;\n void (async (): Promise<void> => {\n // Let in-flight calls settle before tearing the connection down. An\n // aborted write leaves the journal unable to say whether it applied.\n await Promise.race([\n proxy.whenIdle(),\n new Promise<void>((resolve) => setTimeout(resolve, 5000).unref()),\n ]);\n await proxy.server.close();\n for (const upstream of upstreams) {\n await upstream.close();\n }\n journal.close();\n process.exit(code);\n })();\n };\n\n process.on(\"SIGINT\", () => {\n shutdown(0);\n });\n process.on(\"SIGTERM\", () => {\n shutdown(0);\n });\n\n // StdioServerTransport only reports a close that we initiate; it never\n // reacts to the parent closing the pipe. Without these listeners the proxy\n // survives its own client, holding every upstream child open until whoever\n // spawned us escalates to a signal.\n // The pipe closing means no more requests are coming, not that the ones\n // already delivered can be dropped. The transport hands only a few buffered\n // frames to handlers per turn of the event loop, so wait until the proxy has\n // been quiet for several consecutive turns rather than yielding a fixed\n // number of times, which is guesswork. The cap stops a wedged upstream from\n // holding the process open.\n const pipeClosed = (): void => {\n const giveUpAt = Date.now() + 5000;\n let quiet = 0;\n const settle = (): void => {\n quiet = proxy.busy() ? 0 : quiet + 1;\n if (quiet >= 10 || Date.now() > giveUpAt) {\n shutdown(0);\n return;\n }\n setImmediate(settle);\n };\n setImmediate(settle);\n };\n process.stdin.on(\"end\", pipeClosed);\n process.stdin.on(\"close\", pipeClosed);\n\n const inner = proxy.server.server;\n const onclose = inner.onclose;\n inner.onclose = (): void => {\n onclose?.();\n shutdown(0);\n };\n\n await proxy.server.connect(new StdioServerTransport());\n}\n\ntry {\n await main();\n} catch (error: unknown) {\n // stdout carries protocol frames only; diagnostics must not corrupt it.\n process.stderr.write(`synartesis: ${describe(error)}\\n`);\n process.exit(1);\n}\n","import type { Journal } from \"../journal/journal.js\";\n\nexport interface GateRequest {\n readonly actionId: string;\n readonly runId: string;\n readonly seq: number;\n readonly server: string;\n readonly tool: string;\n readonly args: unknown;\n /** Why this is being asked about, in the words the agent is given. */\n readonly why: string;\n readonly signal: AbortSignal;\n}\n\nexport type GateDecision =\n | { readonly approved: true; readonly by: string }\n | {\n readonly approved: false;\n readonly by?: string;\n readonly reason: string;\n /**\n * Nobody has refused; the request is simply waiting for a person. The\n * agent should tell its user how to approve and then try again.\n */\n readonly awaiting?: boolean;\n };\n\nexport interface Gate {\n decide(request: GateRequest): Promise<GateDecision>;\n}\n\nexport const DEFAULT_GATE_TIMEOUT_MS = 300_000;\n\n/**\n * Records the request and refuses immediately, rather than holding the call\n * open until someone answers.\n *\n * Holding it open cannot work against a real client. Measured against Claude\n * Code: a suspended call sat for the full five minutes while the client had\n * long since reported it as failed, and any approval in that gap would have\n * sent something the agent had already said it had not sent. Every useful\n * window for a person to notice, open a terminal and decide is longer than a\n * client will wait, so the two cannot be reconciled by choosing a better\n * timeout. Refusing at once and letting the agent retry removes the conflict\n * instead of tuning it.\n */\n/**\n * `approveHint` builds the command a person on this machine would actually\n * run, journal path and all. A hint that omits an argument the caller needs is\n * an instruction that fails the moment somebody follows it.\n */\nexport type ApproveHint = (actionId: string) => string;\n\nconst DEFAULT_HINT: ApproveHint = (actionId) => `synartesis approve ${actionId.slice(0, 8)}`;\n\nexport function createRetryGate(journal: Journal, approveHint: ApproveHint = DEFAULT_HINT): Gate {\n return {\n decide(request: GateRequest): Promise<GateDecision> {\n journal.markGated(request.actionId, request.why);\n return Promise.resolve({\n approved: false,\n awaiting: true,\n reason:\n \"it is waiting for a person to approve it. Ask them to run: \" +\n approveHint(request.actionId) +\n \" --- then make this exact call again.\",\n });\n },\n };\n}\n\nexport interface JournalGateOptions {\n readonly timeoutMs?: number;\n readonly pollMs?: number;\n /** Where the operator is told that something is waiting. */\n readonly notify?: (request: GateRequest) => void;\n}\n\n/**\n * Approval arrives out of band, through the journal, rather than from a prompt\n * on stdin.\n *\n * The proxy speaks MCP over stdin and stdout: that pipe carries protocol\n * frames, so there is nothing to prompt on. A prompt written to the\n * controlling terminal would work only when one exists, which rules out every\n * desktop client. The journal is already a transactional, WAL-mode, multi\n * process store, so `synartesis approve` in any other terminal is the natural\n * channel, and it behaves identically wherever the proxy was launched from.\n */\nexport function createJournalGate(journal: Journal, options: JournalGateOptions = {}): Gate {\n const timeoutMs = options.timeoutMs ?? DEFAULT_GATE_TIMEOUT_MS;\n const pollMs = options.pollMs ?? 100;\n const notify = options.notify ?? ((): void => undefined);\n\n return {\n async decide(request: GateRequest): Promise<GateDecision> {\n journal.markGated(request.actionId, request.why);\n notify(request);\n\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const action = journal.getAction(request.actionId);\n if (action === undefined) {\n return { approved: false, reason: \"the journal entry disappeared while awaiting approval\" };\n }\n if (action.status !== \"gated\") {\n return action.status === \"denied\"\n ? {\n approved: false,\n ...(action.approvedBy === undefined ? {} : { by: action.approvedBy }),\n reason: action.error ?? \"denied\",\n }\n : { approved: true, by: action.approvedBy ?? \"unknown\" };\n }\n\n if (request.signal.aborted) {\n journal.deny(request.actionId, undefined, \"the client disconnected before a decision\");\n return { approved: false, reason: \"the client disconnected before a decision\" };\n }\n if (Date.now() >= deadline) {\n // Deny by default (3.4): silence is not consent.\n const reason = `no answer within ${String(Math.round(timeoutMs / 1000))}s, so it was denied`;\n journal.deny(request.actionId, undefined, reason);\n return { approved: false, reason };\n }\n\n await new Promise<void>((resolve) => setTimeout(resolve, pollMs).unref());\n }\n },\n };\n}\n","import pino, { type Logger } from \"pino\";\n\nexport type { Logger };\n\nexport const LOG_LEVELS = [\"trace\", \"debug\", \"info\", \"warn\", \"error\", \"silent\"] as const;\nexport type LogLevel = (typeof LOG_LEVELS)[number];\n\nexport function isLogLevel(value: string): value is LogLevel {\n return LOG_LEVELS.some((level) => level === value);\n}\n\n/**\n * Always fd 2. stdout carries MCP protocol frames, and a single stray log line\n * on it corrupts the session for every client. Synchronous so that the last\n * lines before an exit are not lost, which is exactly when they matter.\n */\nexport function createLogger(level: LogLevel): Logger {\n return pino(\n { level, base: { name: \"synartesis\" } },\n pino.destination({ dest: 2, sync: true }),\n );\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport {\n CallToolRequestSchema,\n CompleteRequestSchema,\n ErrorCode,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourceTemplatesRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n McpError,\n ReadResourceRequestSchema,\n SetLevelRequestSchema,\n SubscribeRequestSchema,\n UnsubscribeRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type {\n Implementation,\n Request,\n ServerCapabilities,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\n\nimport { SnapshotError, UpstreamError, describe } from \"../errors.js\";\nimport { createRetryGate, type ApproveHint, type Gate } from \"../gate/gate.js\";\nimport { shouldGateOnWrite } from \"../gate/heuristic.js\";\nimport type { Journal } from \"../journal/journal.js\";\nimport type { Logger } from \"../logging.js\";\nimport {\n createPolicyResolver,\n type PolicyResolver,\n} from \"../manifest/match.js\";\nimport { qualify, type Manifest } from \"../manifest/types.js\";\nimport { createRouter, type Router } from \"./routing.js\";\nimport {\n observeState,\n planInverse,\n isDisconnected,\n mayHaveArrived,\n planRead,\n refusal,\n runRead,\n toPayload,\n type ResolvedRead,\n} from \"./snapshot.js\";\nimport type { Upstream } from \"./upstream.js\";\n\nexport interface ProxyOptions {\n readonly upstreams: readonly Upstream[];\n readonly manifest: Manifest;\n readonly journal: Journal;\n /** Defaults to out-of-band approval through the journal. */\n readonly gate?: Gate;\n readonly gateTimeoutMs?: number;\n readonly logger?: Logger;\n /** Builds the exact command a person here would run to approve an action. */\n readonly approveHint?: ApproveHint;\n}\n\nexport interface ProxyServer {\n readonly server: McpServer;\n /** Resolves with the run id once the client session is initialized. */\n readonly ready: Promise<string>;\n /** Resolves when no tool call is in flight, so shutdown can drain first. */\n whenIdle(): Promise<void>;\n /** The open run, once the session has initialized. */\n readonly runId: string | undefined;\n /** Whether any forwarded request is currently in flight. */\n busy(): boolean;\n}\n\ntype Passthrough = { [key: string]: unknown };\n\n/**\n * How long an approval stays usable. Long enough to survive a client restart\n * and a person walking away from their desk, short enough that a decision made\n * this morning cannot quietly authorise the same call tomorrow.\n */\nconst APPROVAL_WINDOW_MS = 60 * 60 * 1000;\n\n/**\n * Results are read through loose schemas. The SDK's typed schemas strip fields\n * they do not know about, which would quietly erase any metadata an upstream\n * added; only the names this proxy has to rewrite are described here.\n */\nconst PassthroughResult = z.looseObject({});\nconst ToolList = z.looseObject({\n tools: z.array(z.looseObject({ name: z.string() })),\n nextCursor: z.string().optional(),\n});\nconst PromptList = z.looseObject({\n prompts: z.array(z.looseObject({ name: z.string() })),\n nextCursor: z.string().optional(),\n});\nconst ResourceList = z.looseObject({\n resources: z.array(z.looseObject({ uri: z.string() })),\n nextCursor: z.string().optional(),\n});\nconst TemplateList = z.looseObject({\n resourceTemplates: z.array(z.looseObject({ uriTemplate: z.string() })),\n nextCursor: z.string().optional(),\n});\n\nfunction unwrap(error: McpError): string {\n const prefix = `MCP error ${String(error.code)}: `;\n return error.message.startsWith(prefix)\n ? error.message.slice(prefix.length)\n : error.message;\n}\n\nfunction rethrow(server: string, operation: string, error: unknown): never {\n if (error instanceof McpError) {\n throw new McpError(error.code, unwrap(error), error.data);\n }\n throw new UpstreamError(server, operation, error);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * The client sees one logical server, so it must be told about anything any\n * upstream can do. Sub-objects are merged rather than replaced so that, for\n * example, one server's resources.subscribe survives another's resources {}.\n */\nfunction mergeCapabilities(\n all: readonly ServerCapabilities[],\n): ServerCapabilities {\n const merged: Record<string, unknown> = {};\n for (const capabilities of all) {\n for (const [key, value] of Object.entries(capabilities)) {\n const existing = merged[key];\n merged[key] =\n isRecord(existing) && isRecord(value)\n ? { ...existing, ...value }\n : value;\n }\n }\n return merged;\n}\n\nfunction identityFor(router: Router): Implementation {\n const only = router.upstreams[0];\n if (!router.prefixed && only !== undefined) {\n const upstream = only.client.getServerVersion();\n if (upstream !== undefined) {\n return upstream;\n }\n }\n // With several servers behind it there is no single identity to mirror.\n return { name: \"synartesis\", version: \"0.0.0\" };\n}\n\n/**\n * Told to the agent at connect time. Without it a gated call is just an opaque\n * failure, and the person watching has no idea why their agent stopped or what\n * they are supposed to do about it. With it, the agent explains itself.\n */\nconst SYNARTESIS_INSTRUCTIONS = [\n \"These tools are guarded by Synartesis, which records every change so it can be undone later.\",\n \"\",\n \"Some actions cannot be undone. Those are held until a person approves them, and the call\",\n \"will fail with a message beginning \\\"Synartesis is holding this call for approval\\\".\",\n \"When that happens:\",\n \" 1. Tell the user plainly that you are asking Synartesis for approval, and what for.\",\n \" 2. Give them the exact `synartesis approve ...` command from the error.\",\n \" 3. Once they say they have approved it, make the same call again. It will go through.\",\n \"Do not try to work around a held call by using a different tool to achieve the same thing.\",\n].join(\"\\n\");\n\nfunction instructionsFor(router: Router): string {\n const sections = router.upstreams\n .map((upstream) => ({\n name: upstream.name,\n text: upstream.client.getInstructions(),\n }))\n .filter(\n (section): section is { name: string; text: string } => section.text !== undefined,\n );\n\n const upstream = router.prefixed\n ? sections.map((section) => `Tools prefixed ${section.name}__:\\n${section.text}`).join(\"\\n\\n\")\n : (sections[0]?.text ?? \"\");\n\n return upstream === \"\" ? SYNARTESIS_INSTRUCTIONS : `${SYNARTESIS_INSTRUCTIONS}\\n\\n${upstream}`;\n}\n\n/** Walks every page so that aggregation across servers is never partial. */\nasync function drain<T>(\n fetch: (\n cursor: string | undefined,\n ) => Promise<{ items: T[]; nextCursor: string | undefined }>,\n): Promise<T[]> {\n const collected: T[] = [];\n let cursor: string | undefined;\n do {\n const page = await fetch(cursor);\n collected.push(...page.items);\n cursor = page.nextCursor;\n } while (cursor !== undefined);\n return collected;\n}\n\nexport function createProxyServer(options: ProxyOptions): ProxyServer {\n const { upstreams, manifest, journal } = options;\n const router = createRouter(upstreams, manifest);\n const policies: PolicyResolver = createPolicyResolver(manifest);\n\n const log = options.logger;\n\n const gate = options.gate ?? createRetryGate(journal, options.approveHint);\n\n const capabilities = mergeCapabilities(\n upstreams.map((upstream) => upstream.client.getServerCapabilities() ?? {}),\n );\n const instructions = instructionsFor(router);\n\n const wrapper = new McpServer(identityFor(router), { capabilities, instructions });\n const server = wrapper.server;\n\n let runId: string | undefined;\n let resolveReady: (id: string) => void = () => undefined;\n const ready = new Promise<string>((resolve) => {\n resolveReady = resolve;\n });\n\n let inflight = 0;\n const idle: (() => void)[] = [];\n const enter = (): void => {\n inflight += 1;\n };\n /**\n * Every decrement goes through here, including the one that parks a call at\n * the gate. A decrement that reached zero without waking the waiters would\n * leave a shutdown draining for ever against a counter that is already idle.\n */\n const leave = (): void => {\n inflight -= 1;\n if (inflight === 0) {\n for (const resolve of idle.splice(0)) {\n resolve();\n }\n }\n };\n const whenIdle = async (): Promise<void> => {\n if (inflight === 0) {\n return;\n }\n await new Promise<void>((resolve) => idle.push(resolve));\n };\n\n // A client that pipelines notifications/initialized ahead of the initialize\n // response can reach oninitialized before its own identity is recorded, so\n // the label is filled in at the first opportunity rather than once.\n let labelled = false;\n const ensureLabel = (): void => {\n if (labelled || runId === undefined) {\n return;\n }\n const name = server.getClientVersion()?.name;\n if (name !== undefined) {\n journal.setRunLabel(runId, name);\n labelled = true;\n }\n };\n\n const supports = (\n upstream: Upstream,\n key: keyof ServerCapabilities,\n ): boolean => upstream.client.getServerCapabilities()?.[key] !== undefined;\n\n const ask = async (\n upstream: Upstream,\n request: Request,\n signal: AbortSignal,\n ): Promise<Passthrough> => {\n try {\n return await upstream.client.request(request, PassthroughResult, {\n signal,\n });\n } catch (error: unknown) {\n return rethrow(upstream.name, request.method, error);\n }\n };\n\n // --- resource ownership -------------------------------------------------\n // A resource uri is an opaque identifier the client hands back verbatim, so\n // unlike a tool name it cannot be namespaced. Ownership therefore has to be\n // discovered from what each server advertises.\n let owners: Map<string, string> | undefined;\n let schemes: Map<string, string> | undefined;\n let conflict: string | undefined;\n\n const refreshResources = async (signal: AbortSignal): Promise<void> => {\n const nextOwners = new Map<string, string>();\n const nextSchemes = new Map<string, string>();\n let nextConflict: string | undefined;\n\n for (const upstream of router.upstreams) {\n if (!supports(upstream, \"resources\")) {\n continue;\n }\n const resources = await drain(async (cursor) => {\n const raw = await ask(\n upstream,\n {\n method: \"resources/list\",\n params: cursor === undefined ? {} : { cursor },\n },\n signal,\n );\n const page = ResourceList.parse(raw);\n return { items: page.resources, nextCursor: page.nextCursor };\n });\n for (const resource of resources) {\n const existing = nextOwners.get(resource.uri);\n if (existing !== undefined && existing !== upstream.name) {\n nextConflict ??= `resource ${resource.uri} is advertised by both ${existing} and ${upstream.name}; a uri cannot be namespaced, so one of them must stop exposing it`;\n }\n nextOwners.set(resource.uri, existing ?? upstream.name);\n const scheme = resource.uri.split(\":\")[0] ?? \"\";\n if (scheme !== \"\" && !nextSchemes.has(scheme)) {\n nextSchemes.set(scheme, upstream.name);\n }\n }\n\n const templates = await drain(async (cursor) => {\n const raw = await ask(\n upstream,\n {\n method: \"resources/templates/list\",\n params: cursor === undefined ? {} : { cursor },\n },\n signal,\n );\n const page = TemplateList.parse(raw);\n return { items: page.resourceTemplates, nextCursor: page.nextCursor };\n });\n for (const template of templates) {\n const scheme = template.uriTemplate.split(\":\")[0] ?? \"\";\n if (scheme !== \"\" && !nextSchemes.has(scheme)) {\n nextSchemes.set(scheme, upstream.name);\n }\n }\n }\n\n owners = nextOwners;\n schemes = nextSchemes;\n conflict = nextConflict;\n };\n\n const ensureResources = async (signal: AbortSignal): Promise<void> => {\n if (owners === undefined) {\n await refreshResources(signal);\n }\n if (conflict !== undefined) {\n throw new McpError(ErrorCode.InternalError, conflict);\n }\n };\n\n const ownerOf = async (\n uri: string,\n signal: AbortSignal,\n ): Promise<Upstream> => {\n await ensureResources(signal);\n const direct = owners?.get(uri);\n const scheme = uri.split(\":\")[0] ?? \"\";\n const name = direct ?? schemes?.get(scheme);\n const upstream = name === undefined ? undefined : router.byName(name);\n if (upstream === undefined) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `no configured server provides ${uri}`,\n );\n }\n return upstream;\n };\n\n // --- handlers -----------------------------------------------------------\n if (capabilities.tools !== undefined) {\n server.setRequestHandler(\n ListToolsRequestSchema,\n async (_request, extra) => {\n const tools: Passthrough[] = [];\n for (const upstream of router.upstreams) {\n if (!supports(upstream, \"tools\")) {\n continue;\n }\n const items = await drain(async (cursor) => {\n const raw = await ask(\n upstream,\n {\n method: \"tools/list\",\n params: cursor === undefined ? {} : { cursor },\n },\n extra.signal,\n );\n const page = ToolList.parse(raw);\n return { items: page.tools, nextCursor: page.nextCursor };\n });\n for (const tool of items) {\n tools.push({\n ...tool,\n name: router.expose(upstream.name, tool.name),\n });\n }\n }\n // Pagination is flattened: a cursor would have to encode a position\n // across several independent servers, and the client gains nothing.\n return { tools };\n },\n );\n\n server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {\n if (runId === undefined) {\n throw new UpstreamError(\"proxy\", \"tools/call\", \"no active run\");\n }\n // Captured: narrowing does not survive into the closures below.\n const activeRun = runId;\n ensureLabel();\n const route = router.route(request.params.name);\n if (route === undefined) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `no configured server provides tool ${request.params.name}`,\n );\n }\n\n const { policy } = policies.resolve(qualify(route.upstream.name, route.tool));\n const args = request.params.arguments ?? {};\n // Counted from here, not from the forward call: the pre-read is part of\n // the action, and a shutdown that aborts it blocks a legitimate write.\n enter();\n try {\n const wantsGate =\n policy.gate === \"always\" || (policy.gate === \"on_write\" && shouldGateOnWrite(args));\n\n // A retry after an out-of-band approval reuses the row that was\n // approved, so the approval ends up on the action that actually ran\n // rather than on an abandoned twin of it.\n const granted = wantsGate\n ? journal.findApproval({\n server: route.upstream.name,\n tool: route.tool,\n args,\n notBefore: new Date(Date.now() - APPROVAL_WINDOW_MS).toISOString(),\n })\n : undefined;\n\n // An approval granted in an earlier session cannot simply be adopted:\n // the action belongs to the run happening now, or undoing this run\n // would not include it.\n const inherited =\n granted !== undefined && granted.runId !== activeRun ? granted : undefined;\n\n // Nobody has answered yet and the agent is asking again. Reusing the\n // row it is already waiting on keeps one call to one decision, which\n // is what `synartesis gates` and `approve` both assume.\n const waiting =\n granted === undefined && wantsGate\n ? journal.findGated({\n runId: activeRun,\n server: route.upstream.name,\n tool: route.tool,\n args,\n })\n : undefined;\n\n const reusable = waiting ?? (inherited === undefined ? granted : undefined);\n const pending =\n reusable === undefined\n ? journal.recordPending({\n runId: activeRun,\n server: route.upstream.name,\n tool: route.tool,\n args,\n class: policy.class,\n })\n : {\n actionId: reusable.id,\n seq: reusable.seq,\n idempotencyKey: reusable.idempotencyKey,\n };\n\n if (inherited !== undefined) {\n journal.adoptApproval(pending.actionId, inherited);\n } else if (granted !== undefined && waiting === undefined) {\n // Reusing the approved row itself: from here its outcome stops being\n // known, so it stops being `approved`.\n journal.markInFlight(granted.id);\n }\n if (granted !== undefined) {\n log?.info(\n { action: pending.actionId, by: granted.approvedBy, from: granted.runId },\n \"proceeding on a standing approval\",\n );\n }\n\n const decide = async (why: string): Promise<void> => {\n // Parked, not working: a suspended call must not hold up shutdown,\n // and the drain exists to let real work finish.\n leave();\n let decision;\n try {\n decision = await gate.decide({\n actionId: pending.actionId,\n runId: activeRun,\n seq: pending.seq,\n server: route.upstream.name,\n tool: route.tool,\n args,\n why,\n signal: extra.signal,\n });\n } finally {\n enter();\n }\n log?.info(\n { action: pending.actionId, approved: decision.approved },\n decision.approved ? \"approved\" : \"denied\",\n );\n // An approval that lands after the client has given up would send\n // a real email that the agent has already reported as not sent.\n // Nobody is waiting for the result, so the safe reading of an\n // approval nobody can hear is that it did not happen.\n if (decision.approved && extra.signal.aborted) {\n journal.settleAsDenied(\n pending.actionId,\n decision.by,\n \"approved, but the client had already stopped waiting, so it was not sent\",\n );\n throw new McpError(\n ErrorCode.InvalidRequest,\n `synartesis blocked ${request.params.name}: it was approved after the client stopped waiting, so it was not sent. Ask the agent to try again.`,\n );\n }\n if (!decision.approved) {\n if (decision.awaiting === true) {\n log?.warn(\n {\n action: pending.actionId,\n tool: `${route.upstream.name}.${route.tool}`,\n approve: options.approveHint?.(pending.actionId) ?? pending.actionId,\n },\n \"awaiting approval\",\n );\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Synartesis is holding this call for approval, because ${why}. ${decision.reason}`,\n );\n }\n const who = decision.by === undefined ? \"\" : ` by ${decision.by}`;\n throw new McpError(\n ErrorCode.InvalidRequest,\n `synartesis blocked ${request.params.name}: ${why} and was denied${who}. ${decision.reason}`,\n );\n }\n };\n\n // D4/3.4: a policy gate suspends before anything is read or written, so\n // a gated action never even looks at the resource.\n // decide() throws on refusal, so getting past this means approved.\n const askedAlready = wantsGate;\n if (wantsGate && granted === undefined) {\n await decide(\"this action cannot be undone\");\n }\n\n // The pre-read happens before the write goes out, and a failure stops\n // the write entirely: a reversible action without a snapshot is\n // silently irreversible, which is worse than the action not happening.\n let snapshot: unknown;\n let verify: ResolvedRead | undefined;\n let missingPriorState: string | undefined;\n if (policy.snapshot !== undefined) {\n try {\n verify = planRead(policy.snapshot, { args });\n snapshot = await runRead(router, verify, extra.signal);\n journal.attachSnapshot(pending.actionId, snapshot);\n } catch (error: unknown) {\n const reason = describe(error);\n if (error instanceof SnapshotError && error.absent) {\n // Nothing exists here yet, so this call creates rather than\n // replaces and there is nothing to put back. It is an\n // irreversible action wearing a reversible policy. Refusing\n // outright would mean an agent could never create anything, so\n // it falls through to the same question the gate asks.\n missingPriorState = reason;\n verify = undefined;\n } else {\n journal.markFailed(pending.actionId, reason);\n log?.error(\n { seq: pending.seq, tool: route.tool, reason },\n \"write blocked: snapshot failed\",\n );\n throw new McpError(\n ErrorCode.InternalError,\n `synartesis blocked ${request.params.name}: ${reason}`,\n );\n }\n }\n }\n\n if (missingPriorState !== undefined && !askedAlready) {\n // An approval granted out of band counts here too. It was only ever\n // looked up for a policy that asked to be gated, so a write whose\n // prior state was missing -- an agent creating a file, the commonest\n // thing an agent does -- asked, was approved, and asked again, and\n // no number of approvals ever let it through. The instructions this\n // proxy sends to every agent promise the opposite.\n const standing = journal.findApproval({\n server: route.upstream.name,\n tool: route.tool,\n args,\n notBefore: new Date(Date.now() - APPROVAL_WINDOW_MS).toISOString(),\n });\n if (standing === undefined) {\n // Not \"nothing exists here\": every tool-level error on a pre-read\n // arrives here, so a file that exists and merely could not be read\n // came out as one that was not there. The person approving an\n // unundoable write was shown absence and given no way to learn\n // otherwise until after they had allowed it. Say what happened and\n // hand over the server's own words.\n await decide(\n `nothing was captured to restore, so this cannot be undone — the read said: ${missingPriorState}`,\n );\n } else {\n // Moved onto the row that actually runs, which also spends it: an\n // approval answers one call, not every call that looks like it.\n journal.adoptApproval(pending.actionId, standing);\n log?.info(\n { action: pending.actionId, by: standing.approvedBy, from: standing.runId },\n \"proceeding on a standing approval\",\n );\n }\n }\n\n const forwarded: Request = {\n method: \"tools/call\",\n params: { ...request.params, name: route.tool },\n };\n\n try {\n const result = await route.upstream.client.request(forwarded, PassthroughResult, {\n signal: extra.signal,\n });\n\n // The server understood the call and did not do it. Recording that\n // as an action would be worse than not recording it at all: an\n // inverse resolved from a refusal is a compensating call for\n // something that never happened, and undo would faithfully carry it\n // out. The agent still sees the refusal exactly as sent.\n const refused = refusal(result);\n if (refused !== undefined) {\n journal.markFailed(pending.actionId, `the upstream refused the call: ${refused}`);\n log?.debug(\n { seq: pending.seq, tool: route.tool, reason: refused },\n \"refused by the upstream\",\n );\n return result;\n }\n\n const context = { args, snapshot, result: toPayload(result) };\n const warnings: string[] = [];\n if (missingPriorState !== undefined) {\n warnings.push(\n `no prior state existed, so there is nothing to restore: ${missingPriorState}`,\n );\n }\n\n // Resolved now rather than at rollback time (D5).\n let inverse: unknown;\n if (policy.inverse !== undefined && missingPriorState === undefined) {\n try {\n inverse = planInverse(policy.inverse, context);\n } catch (error: unknown) {\n warnings.push(`inverse could not be resolved: ${describe(error)}`);\n }\n }\n\n // Best effort: the write has already applied, so a failed post-read\n // cannot undo it. Phase 4 fails closed when the post-state is\n // missing. A resource that is now absent is a captured post-state,\n // not a missing one.\n let postSnapshot: unknown;\n if (verify !== undefined) {\n try {\n postSnapshot = await observeState(router, verify, extra.signal);\n } catch (error: unknown) {\n warnings.push(`post-state could not be captured: ${describe(error)}`);\n }\n }\n\n if (warnings.length > 0) {\n log?.warn({ seq: pending.seq, tool: route.tool, warnings }, \"applied with reservations\");\n }\n log?.debug(\n { seq: pending.seq, server: route.upstream.name, tool: route.tool, class: policy.class },\n \"applied\",\n );\n journal.markApplied(pending.actionId, {\n result,\n ...(inverse === undefined ? {} : { inverse }),\n ...(verify === undefined ? {} : { verify }),\n ...(postSnapshot === undefined ? {} : { postSnapshot }),\n ...(warnings.length === 0 ? {} : { warning: warnings.join(\"; \") }),\n });\n return result;\n } catch (error: unknown) {\n const disconnected = isDisconnected(error);\n if (extra.signal.aborted || mayHaveArrived(error)) {\n // A transport that closed while a reply was still owed says\n // nothing about whether the call arrived. Recording that as failed\n // asserts it did not, and undo would then step over an action that\n // may well have applied. Having had no connection to write to at\n // all is the other case, and that one really did not happen.\n journal.markUnknown(pending.actionId, describe(error));\n } else {\n journal.markFailed(pending.actionId, describe(error));\n }\n if (disconnected && route.upstream.reconnect !== undefined) {\n // Not to retry this call -- a write must never be sent twice on a\n // guess -- but so the rest of the session is not lost with it.\n await route.upstream.reconnect().catch(() => undefined);\n }\n return rethrow(route.upstream.name, \"tools/call\", error);\n }\n } finally {\n leave();\n }\n });\n }\n\n if (capabilities.resources !== undefined) {\n server.setRequestHandler(\n ListResourcesRequestSchema,\n async (_request, extra) => {\n await refreshResources(extra.signal);\n await ensureResources(extra.signal);\n const resources: Passthrough[] = [];\n for (const upstream of router.upstreams) {\n if (!supports(upstream, \"resources\")) {\n continue;\n }\n const items = await drain(async (cursor) => {\n const raw = await ask(\n upstream,\n {\n method: \"resources/list\",\n params: cursor === undefined ? {} : { cursor },\n },\n extra.signal,\n );\n const page = ResourceList.parse(raw);\n return { items: page.resources, nextCursor: page.nextCursor };\n });\n resources.push(...items);\n }\n return { resources };\n },\n );\n\n server.setRequestHandler(\n ListResourceTemplatesRequestSchema,\n async (_request, extra) => {\n const resourceTemplates: Passthrough[] = [];\n for (const upstream of router.upstreams) {\n if (!supports(upstream, \"resources\")) {\n continue;\n }\n const items = await drain(async (cursor) => {\n const raw = await ask(\n upstream,\n {\n method: \"resources/templates/list\",\n params: cursor === undefined ? {} : { cursor },\n },\n extra.signal,\n );\n const page = TemplateList.parse(raw);\n return {\n items: page.resourceTemplates,\n nextCursor: page.nextCursor,\n };\n });\n resourceTemplates.push(...items);\n }\n return { resourceTemplates };\n },\n );\n\n server.setRequestHandler(\n ReadResourceRequestSchema,\n async (request, extra) => {\n const upstream = await ownerOf(request.params.uri, extra.signal);\n return ask(upstream, request, extra.signal);\n },\n );\n\n if (capabilities.resources.subscribe === true) {\n for (const schema of [SubscribeRequestSchema, UnsubscribeRequestSchema]) {\n server.setRequestHandler(schema, async (request, extra) => {\n const upstream = await ownerOf(request.params.uri, extra.signal);\n return ask(upstream, request, extra.signal);\n });\n }\n }\n }\n\n if (capabilities.prompts !== undefined) {\n server.setRequestHandler(\n ListPromptsRequestSchema,\n async (_request, extra) => {\n const prompts: Passthrough[] = [];\n for (const upstream of router.upstreams) {\n if (!supports(upstream, \"prompts\")) {\n continue;\n }\n const items = await drain(async (cursor) => {\n const raw = await ask(\n upstream,\n {\n method: \"prompts/list\",\n params: cursor === undefined ? {} : { cursor },\n },\n extra.signal,\n );\n const page = PromptList.parse(raw);\n return { items: page.prompts, nextCursor: page.nextCursor };\n });\n for (const prompt of items) {\n prompts.push({\n ...prompt,\n name: router.expose(upstream.name, prompt.name),\n });\n }\n }\n return { prompts };\n },\n );\n\n server.setRequestHandler(GetPromptRequestSchema, async (request, extra) => {\n const route = router.route(request.params.name);\n if (route === undefined) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `no configured server provides prompt ${request.params.name}`,\n );\n }\n return ask(\n route.upstream,\n {\n method: \"prompts/get\",\n params: { ...request.params, name: route.tool },\n },\n extra.signal,\n );\n });\n }\n\n if (capabilities.completions !== undefined) {\n server.setRequestHandler(CompleteRequestSchema, async (request, extra) => {\n const reference = request.params.ref;\n if (reference.type === \"ref/prompt\") {\n const route = router.route(reference.name);\n if (route === undefined) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `unknown prompt ${reference.name}`,\n );\n }\n return ask(\n route.upstream,\n {\n method: \"completion/complete\",\n params: {\n ...request.params,\n ref: { ...reference, name: route.tool },\n },\n },\n extra.signal,\n );\n }\n const upstream = await ownerOf(reference.uri, extra.signal);\n return ask(upstream, request, extra.signal);\n });\n }\n\n if (capabilities.logging !== undefined) {\n server.setRequestHandler(SetLevelRequestSchema, async (request, extra) => {\n // Broadcast: the client is configuring one logical server.\n for (const upstream of router.upstreams) {\n if (supports(upstream, \"logging\")) {\n await ask(upstream, request, extra.signal);\n }\n }\n return {};\n });\n }\n\n // --- lifecycle ----------------------------------------------------------\n let connected = false;\n for (const upstream of router.upstreams) {\n upstream.client.fallbackNotificationHandler = async (\n notification,\n ): Promise<void> => {\n if (notification.method.endsWith(\"list_changed\")) {\n owners = undefined;\n schemes = undefined;\n conflict = undefined;\n }\n if (connected) {\n await server.notification(notification);\n }\n };\n }\n\n server.oninitialized = (): void => {\n connected = true;\n const name = server.getClientVersion()?.name;\n const id = journal.beginRun(name);\n runId = id;\n labelled = name !== undefined;\n resolveReady(id);\n };\n\n const previousOnClose = server.onclose;\n server.onclose = (): void => {\n connected = false;\n if (runId !== undefined) {\n journal.endRun(runId, \"complete\");\n runId = undefined;\n }\n previousOnClose?.();\n };\n\n return {\n server: wrapper,\n ready,\n whenIdle,\n busy: (): boolean => inflight > 0,\n get runId(): string | undefined {\n return runId;\n },\n };\n}\n","/**\n * The `on_write` heuristic for tools whose destructiveness cannot be decided\n * statically, such as a raw SQL runner.\n *\n * This is a heuristic and is documented as one. It exists because the\n * alternative for `postgres.query` is to gate every SELECT, which no operator\n * would tolerate for long. Anything it cannot confidently read as a read is\n * gated (D4): failing to recognise a statement is not evidence that it is safe.\n * `always` remains the correct choice wherever certainty matters.\n */\nconst READ_ONLY = /^(select|with|show|explain|describe|desc|values|table)\\b/;\n\nfunction isReadOnlyStatement(text: string): boolean {\n const stripped = text\n .replace(/--[^\\n]*/g, \" \")\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \" \")\n .trim();\n if (!READ_ONLY.test(stripped.toLowerCase())) {\n return false;\n }\n // More than one statement means the leading SELECT says nothing about what\n // follows it.\n return stripped.replace(/;\\s*$/, \"\").indexOf(\";\") === -1;\n}\n\nexport function shouldGateOnWrite(args: unknown): boolean {\n if (typeof args !== \"object\" || args === null) {\n return true;\n }\n const strings = Object.values(args).filter(\n (value): value is string => typeof value === \"string\",\n );\n if (strings.length === 0) {\n return true;\n }\n return !strings.every(isReadOnlyStatement);\n}\n"],"mappings":"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","names":["upstream","resolve","resolve"]}
1
+ {"version":3,"sources":["../src/proxy/stdio.ts","../src/proxy/http.ts","../src/gate/gate.ts","../src/logging.ts","../src/proxy/proxy.ts","../src/gate/heuristic.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * better-sqlite3 requires Node 22, and on Node 20 it does not fail politely:\n * it segfaults the moment a database is opened. Saying so is better than\n * letting somebody meet exit code 139.\n */\nconst NODE_MAJOR = Number(process.versions.node.split(\".\")[0]);\nif (NODE_MAJOR < 22) {\n process.stderr.write(\n `synartesis: needs Node 22 or newer, and this is ${process.version}.\\n`,\n );\n process.exit(2);\n}\n\nimport { resolve } from \"node:path\";\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nimport { serveHttp } from \"./http.js\";\n\nimport { describe } from \"../errors.js\";\nimport { DEFAULT_GATE_TIMEOUT_MS } from \"../gate/gate.js\";\nimport { cliCommandFrom } from \"../invocation.js\";\nimport { findJournal, findManifest } from \"../locate.js\";\nimport { createLogger, isLogLevel, LOG_LEVELS, type LogLevel } from \"../logging.js\";\nimport { mark } from \"../style.js\";\nimport { openJournal } from \"../journal/journal.js\";\nimport { loadManifest } from \"../manifest/load.js\";\nimport { verifyAgainstServers } from \"../manifest/verify.js\";\nimport { createProxyServer } from \"./proxy.js\";\nimport { connectStdioUpstream, type Upstream } from \"./upstream.js\";\n\n/**\n * The manifest is the configuration (D3): it already declares every server and\n * how to start it, so there is nothing left for flags to say.\n *\n * synartesis-proxy [--manifest synartesis.yaml] [--journal .synartesis/journal.db]\n * [--gate-timeout <seconds>] [--log-level <level>]\n */\ninterface Argv {\n readonly manifest: string;\n readonly journal: string;\n readonly gateTimeoutMs: number;\n /** Whether --gate-timeout was actually typed, as against defaulted. */\n readonly gateTimeoutGiven: boolean;\n /** Serve over http instead of stdio, for a client that will not start one. */\n readonly http?: { readonly port: number; readonly host: string; readonly token: string };\n readonly logLevel: LogLevel;\n}\n\nfunction parseArgv(argv: readonly string[]): Argv {\n const read = (flag: string): string | undefined => {\n const at = argv.indexOf(flag);\n return at === -1 ? undefined : argv[at + 1];\n };\n const known = [\"--manifest\", \"--journal\", \"--gate-timeout\", \"--log-level\", \"--http\", \"--http-host\", \"--token\"];\n const unknown = argv.find((token) => token.startsWith(\"--\") && !known.includes(token));\n if (unknown !== undefined) {\n throw new Error(`unknown flag ${unknown}; expected one of ${known.join(\", \")}`);\n }\n\n const rawTimeout = read(\"--gate-timeout\");\n const seconds = rawTimeout === undefined ? undefined : Number(rawTimeout);\n if (seconds !== undefined && (!Number.isFinite(seconds) || seconds <= 0)) {\n throw new Error(\"--gate-timeout needs a positive number of seconds\");\n }\n\n const level = read(\"--log-level\") ?? \"info\";\n if (!isLogLevel(level)) {\n throw new Error(`--log-level must be one of ${LOG_LEVELS.join(\", \")}`);\n }\n\n const httpPort = read(\"--http\");\n let http: Argv[\"http\"];\n if (httpPort !== undefined) {\n const port = Number(httpPort);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new Error(\"--http needs a port number\");\n }\n // Refused rather than defaulted. What is served here can write through\n // every server in the policy, and a default of \"no auth\" is the kind of\n // convenience that ends up on someone's public tunnel.\n const token = read(\"--token\") ?? process.env[\"SYNARTESIS_TOKEN\"];\n if (token === undefined || token.length < 16) {\n throw new Error(\n \"--http needs --token, or SYNARTESIS_TOKEN, of at least 16 characters: this serves write access over a socket\",\n );\n }\n http = { port, host: read(\"--http-host\") ?? \"127.0.0.1\", token };\n }\n\n const manifest = findManifest(read(\"--manifest\"));\n return {\n manifest,\n journal: findJournal(read(\"--journal\"), manifest),\n gateTimeoutMs: seconds === undefined ? DEFAULT_GATE_TIMEOUT_MS : seconds * 1000,\n gateTimeoutGiven: seconds !== undefined,\n ...(http === undefined ? {} : { http }),\n logLevel: level,\n };\n}\n\nasync function main(): Promise<void> {\n const argv = parseArgv(process.argv.slice(2));\n const log = createLogger(argv.logLevel);\n if (argv.gateTimeoutGiven) {\n // Accepted, validated, threaded through, and read by nothing: this proxy\n // refuses a held call straight away rather than holding the connection\n // open, so there is no wait for a timeout to cut short. Saying so is\n // better than a flag that quietly does nothing, and better than rejecting\n // one that earlier versions took.\n log.warn(\n \"--gate-timeout has no effect: a held call is refused immediately and the agent makes it again once you approve\",\n );\n }\n // Only on a real terminal. A client collecting our stderr into a log file\n // wants the structured records and nothing else.\n if (process.stderr.isTTY) {\n process.stderr.write(mark());\n }\n // Loaded before anything is spawned: never start with a broken policy.\n const manifest = loadManifest(argv.manifest);\n const journal = openJournal(argv.journal);\n\n const upstreams: Upstream[] = [];\n for (const [name, spec] of Object.entries(manifest.servers)) {\n upstreams.push(\n await connectStdioUpstream({\n name,\n command: spec.command,\n args: spec.args,\n ...(spec.env === undefined ? {} : { env: spec.env }),\n }),\n );\n }\n\n // Never serve a request under a policy that calls tools the servers do not\n // have: at run time that is indistinguishable from a missing resource.\n await verifyAgainstServers(upstreams, manifest);\n\n log.info(\n {\n manifest: argv.manifest,\n journal: argv.journal,\n servers: upstreams.map((upstream) => upstream.name),\n policies: manifest.tools.length,\n },\n \"proxy ready\",\n );\n\n const build = (): ReturnType<typeof createProxyServer> =>\n createProxyServer({\n upstreams,\n manifest,\n journal,\n gateTimeoutMs: argv.gateTimeoutMs,\n logger: log,\n // Absolute, because whoever approves may be in any directory at all.\n approveHint: (actionId: string): string =>\n `${cliCommandFrom(import.meta.url)} approve ${actionId.slice(0, 8)} --journal ${resolve(argv.journal)}`,\n });\n\n if (argv.http !== undefined) {\n // One server, many sessions. Each session is a connection and a connection\n // is a run, so each gets a proxy of its own; the upstreams and the journal\n // are shared, which is what makes them one story.\n const served = await serveHttp({\n ...argv.http,\n create: build,\n log: {\n info: (data, message) => {\n log.info(data, message);\n },\n warn: (message) => {\n log.warn(message);\n },\n },\n });\n const stop = (): void => {\n void (async (): Promise<void> => {\n await served.close();\n for (const upstream of upstreams) {\n await upstream.close();\n }\n journal.close();\n process.exit(0);\n })();\n };\n process.on(\"SIGINT\", stop);\n process.on(\"SIGTERM\", stop);\n return;\n }\n\n const proxy = build();\n\n let shuttingDown = false;\n const shutdown = (code: number): void => {\n if (shuttingDown) {\n return;\n }\n shuttingDown = true;\n void (async (): Promise<void> => {\n // Let in-flight calls settle before tearing the connection down. An\n // aborted write leaves the journal unable to say whether it applied.\n await Promise.race([\n proxy.whenIdle(),\n new Promise<void>((resolve) => setTimeout(resolve, 5000).unref()),\n ]);\n await proxy.server.close();\n for (const upstream of upstreams) {\n await upstream.close();\n }\n journal.close();\n process.exit(code);\n })();\n };\n\n process.on(\"SIGINT\", () => {\n shutdown(0);\n });\n process.on(\"SIGTERM\", () => {\n shutdown(0);\n });\n\n // StdioServerTransport only reports a close that we initiate; it never\n // reacts to the parent closing the pipe. Without these listeners the proxy\n // survives its own client, holding every upstream child open until whoever\n // spawned us escalates to a signal.\n // The pipe closing means no more requests are coming, not that the ones\n // already delivered can be dropped. The transport hands only a few buffered\n // frames to handlers per turn of the event loop, so wait until the proxy has\n // been quiet for several consecutive turns rather than yielding a fixed\n // number of times, which is guesswork. The cap stops a wedged upstream from\n // holding the process open.\n const pipeClosed = (): void => {\n const giveUpAt = Date.now() + 5000;\n let quiet = 0;\n const settle = (): void => {\n quiet = proxy.busy() ? 0 : quiet + 1;\n if (quiet >= 10 || Date.now() > giveUpAt) {\n shutdown(0);\n return;\n }\n setImmediate(settle);\n };\n setImmediate(settle);\n };\n process.stdin.on(\"end\", pipeClosed);\n process.stdin.on(\"close\", pipeClosed);\n\n const inner = proxy.server.server;\n const onclose = inner.onclose;\n inner.onclose = (): void => {\n onclose?.();\n shutdown(0);\n };\n\n await proxy.server.connect(new StdioServerTransport());\n}\n\ntry {\n await main();\n} catch (error: unknown) {\n // stdout carries protocol frames only; diagnostics must not corrupt it.\n process.stderr.write(`synartesis: ${describe(error)}\\n`);\n process.exit(1);\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { randomUUID, timingSafeEqual } from \"node:crypto\";\n\nimport { WebStandardStreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\n\nimport type { ProxyServer } from \"./proxy.js\";\n\n/**\n * Serving the proxy over HTTP, for clients that will not start a process.\n *\n * ChatGPT's connectors are the reason this exists: they take a remote https\n * endpoint and nothing else, so stdio -- which every other client speaks -- is\n * not an option there.\n *\n * What is served is an undo layer with write access to real systems, so this\n * refuses to run without a token and binds to the loopback interface unless\n * told otherwise. Reaching it from the internet is a tunnel in front of it,\n * deliberately: that is a decision someone should have to make out loud.\n */\nexport interface HttpOptions {\n readonly port: number;\n readonly host: string;\n readonly token: string;\n /** A fresh proxy per session, since a run belongs to one client's connection. */\n readonly create: () => ProxyServer;\n readonly log: {\n info: (data: Record<string, unknown>, message: string) => void;\n warn: (message: string) => void;\n };\n}\n\nexport interface HttpServer {\n readonly port: number;\n close(): Promise<void>;\n}\n\n\n/**\n * Node's request and response, in the shapes the sdk's transport speaks.\n *\n * The node-native transport in this sdk declares onclose as a getter/setter\n * pair typed `(() => void) | undefined`, which an exactOptionalPropertyTypes\n * project cannot accept without an assertion. The web-standard one declares it\n * plainly, so it is used instead and the twenty lines below are the price.\n */\nfunction toRequest(req: IncomingMessage, body: Buffer, origin: string): Request {\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === \"string\") {\n headers.set(key, value);\n } else if (Array.isArray(value)) {\n for (const one of value) {\n headers.append(key, one);\n }\n }\n }\n const method = req.method ?? \"GET\";\n return new Request(new URL(req.url ?? \"/\", origin), {\n method,\n headers,\n // A GET or HEAD may not carry one, and node sends an empty buffer anyway.\n ...(method === \"GET\" || method === \"HEAD\" ? {} : { body }),\n });\n}\n\nasync function writeResponse(res: ServerResponse, response: Response): Promise<void> {\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key] = value;\n });\n res.writeHead(response.status, headers);\n if (response.body === null) {\n res.end();\n return;\n }\n // Streamed rather than buffered: this is how an SSE reply stays live.\n for await (const chunk of response.body) {\n res.write(Buffer.from(chunk));\n }\n res.end();\n}\n\n/** Constant time, so a wrong token cannot be found one character at a time. */\nfunction tokenMatches(given: string, expected: string): boolean {\n const a = Buffer.from(given);\n const b = Buffer.from(expected);\n return a.length === b.length && timingSafeEqual(a, b);\n}\n\nfunction bearer(req: IncomingMessage): string | undefined {\n const header = req.headers.authorization;\n if (typeof header !== \"string\") {\n return undefined;\n }\n const match = /^Bearer[ ]+(.+)$/i.exec(header.trim());\n return match?.[1];\n}\n\nfunction refuse(res: ServerResponse, status: number, message: string): void {\n res.writeHead(status, {\n \"content-type\": \"application/json\",\n // Told the same way twice: the header is what a client acts on, the body\n // is what a person reads in a terminal.\n ...(status === 401 ? { \"www-authenticate\": 'Bearer realm=\"synartesis\"' } : {}),\n });\n res.end(JSON.stringify({ error: message }));\n}\n\nasync function readRaw(req: IncomingMessage): Promise<Buffer> {\n const chunks: Buffer[] = [];\n let size = 0;\n for await (const chunk of req) {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));\n size += buffer.length;\n // A cap, because this listens on a socket: an unbounded body is a way to\n // exhaust memory without ever authenticating.\n if (size > 8 * 1024 * 1024) {\n throw new Error(\"request body too large\");\n }\n chunks.push(buffer);\n }\n return Buffer.concat(chunks);\n}\n\nfunction isInitialize(body: unknown): boolean {\n const one = (message: unknown): boolean =>\n typeof message === \"object\" &&\n message !== null &&\n \"method\" in message &&\n message.method === \"initialize\";\n return Array.isArray(body) ? body.some(one) : one(body);\n}\n\nexport async function serveHttp(options: HttpOptions): Promise<HttpServer> {\n const sessions = new Map<string, { transport: WebStandardStreamableHTTPServerTransport; proxy: ProxyServer }>();\n\n const server = createServer((req, res) => {\n void (async (): Promise<void> => {\n try {\n const token = bearer(req);\n if (token === undefined || !tokenMatches(token, options.token)) {\n // Before anything is parsed or routed. An unauthenticated request\n // must not be able to reach the proxy, the journal or an upstream.\n refuse(res, 401, \"a bearer token is required\");\n return;\n }\n if (req.url !== undefined && !req.url.startsWith(\"/mcp\")) {\n refuse(res, 404, \"the endpoint is /mcp\");\n return;\n }\n\n const origin = `http://${options.host}:${String(options.port)}`;\n const raw = await readRaw(req);\n const sessionId = req.headers[\"mcp-session-id\"];\n const existing = typeof sessionId === \"string\" ? sessions.get(sessionId) : undefined;\n if (existing !== undefined) {\n await writeResponse(res, await existing.transport.handleRequest(toRequest(req, raw, origin)));\n return;\n }\n\n const body: unknown =\n req.method === \"POST\" && raw.length > 0 ? JSON.parse(raw.toString(\"utf8\")) : undefined;\n if (req.method !== \"POST\" || !isInitialize(body)) {\n refuse(res, 400, \"no such session; start one with an initialize request\");\n return;\n }\n\n // A session is a connection, and a connection is a run: each one gets\n // its own proxy so its actions are journalled as one story.\n const proxy = options.create();\n const transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n onsessioninitialized: (id: string) => {\n sessions.set(id, { transport, proxy });\n options.log.info({ session: id }, \"http session opened\");\n },\n });\n await proxy.server.connect(transport);\n transport.onclose = (): void => {\n const id = transport.sessionId;\n if (id !== undefined) {\n sessions.delete(id);\n }\n };\n await writeResponse(res, await transport.handleRequest(toRequest(req, raw, origin)));\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n if (!res.headersSent) {\n refuse(res, 400, message);\n } else {\n res.end();\n }\n }\n })();\n });\n\n await new Promise<void>((resolve) => {\n server.listen(options.port, options.host, resolve);\n });\n const address = server.address();\n const port = typeof address === \"object\" && address !== null ? address.port : options.port;\n\n if (options.host !== \"127.0.0.1\" && options.host !== \"localhost\") {\n options.log.warn(\n `listening on ${options.host}, which is not loopback: anything that can reach this port and holds the token can write through your servers`,\n );\n }\n options.log.info({ host: options.host, port, endpoint: \"/mcp\" }, \"http proxy ready\");\n\n return {\n port,\n close: async (): Promise<void> => {\n for (const { transport } of sessions.values()) {\n await transport.close().catch(() => undefined);\n }\n sessions.clear();\n await new Promise<void>((resolve) => {\n server.close(() => {\n resolve();\n });\n });\n },\n };\n}\n","import type { Journal } from \"../journal/journal.js\";\n\nexport interface GateRequest {\n readonly actionId: string;\n readonly runId: string;\n readonly seq: number;\n readonly server: string;\n readonly tool: string;\n readonly args: unknown;\n /** Why this is being asked about, in the words the agent is given. */\n readonly why: string;\n readonly signal: AbortSignal;\n}\n\nexport type GateDecision =\n | { readonly approved: true; readonly by: string }\n | {\n readonly approved: false;\n readonly by?: string;\n readonly reason: string;\n /**\n * Nobody has refused; the request is simply waiting for a person. The\n * agent should tell its user how to approve and then try again.\n */\n readonly awaiting?: boolean;\n };\n\nexport interface Gate {\n decide(request: GateRequest): Promise<GateDecision>;\n}\n\nexport const DEFAULT_GATE_TIMEOUT_MS = 300_000;\n\n/**\n * Records the request and refuses immediately, rather than holding the call\n * open until someone answers.\n *\n * Holding it open cannot work against a real client. Measured against Claude\n * Code: a suspended call sat for the full five minutes while the client had\n * long since reported it as failed, and any approval in that gap would have\n * sent something the agent had already said it had not sent. Every useful\n * window for a person to notice, open a terminal and decide is longer than a\n * client will wait, so the two cannot be reconciled by choosing a better\n * timeout. Refusing at once and letting the agent retry removes the conflict\n * instead of tuning it.\n */\n/**\n * `approveHint` builds the command a person on this machine would actually\n * run, journal path and all. A hint that omits an argument the caller needs is\n * an instruction that fails the moment somebody follows it.\n */\nexport type ApproveHint = (actionId: string) => string;\n\nconst DEFAULT_HINT: ApproveHint = (actionId) => `synartesis approve ${actionId.slice(0, 8)}`;\n\nexport function createRetryGate(journal: Journal, approveHint: ApproveHint = DEFAULT_HINT): Gate {\n return {\n decide(request: GateRequest): Promise<GateDecision> {\n journal.markGated(request.actionId, request.why);\n return Promise.resolve({\n approved: false,\n awaiting: true,\n reason:\n \"it is waiting for a person to approve it. Ask them to run: \" +\n approveHint(request.actionId) +\n \" --- then make this exact call again.\",\n });\n },\n };\n}\n\nexport interface JournalGateOptions {\n readonly timeoutMs?: number;\n readonly pollMs?: number;\n /** Where the operator is told that something is waiting. */\n readonly notify?: (request: GateRequest) => void;\n}\n\n/**\n * Approval arrives out of band, through the journal, rather than from a prompt\n * on stdin.\n *\n * The proxy speaks MCP over stdin and stdout: that pipe carries protocol\n * frames, so there is nothing to prompt on. A prompt written to the\n * controlling terminal would work only when one exists, which rules out every\n * desktop client. The journal is already a transactional, WAL-mode, multi\n * process store, so `synartesis approve` in any other terminal is the natural\n * channel, and it behaves identically wherever the proxy was launched from.\n */\nexport function createJournalGate(journal: Journal, options: JournalGateOptions = {}): Gate {\n const timeoutMs = options.timeoutMs ?? DEFAULT_GATE_TIMEOUT_MS;\n const pollMs = options.pollMs ?? 100;\n const notify = options.notify ?? ((): void => undefined);\n\n return {\n async decide(request: GateRequest): Promise<GateDecision> {\n journal.markGated(request.actionId, request.why);\n notify(request);\n\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const action = journal.getAction(request.actionId);\n if (action === undefined) {\n return { approved: false, reason: \"the journal entry disappeared while awaiting approval\" };\n }\n if (action.status !== \"gated\") {\n return action.status === \"denied\"\n ? {\n approved: false,\n ...(action.approvedBy === undefined ? {} : { by: action.approvedBy }),\n reason: action.error ?? \"denied\",\n }\n : { approved: true, by: action.approvedBy ?? \"unknown\" };\n }\n\n if (request.signal.aborted) {\n journal.deny(request.actionId, undefined, \"the client disconnected before a decision\");\n return { approved: false, reason: \"the client disconnected before a decision\" };\n }\n if (Date.now() >= deadline) {\n // Deny by default (3.4): silence is not consent.\n const reason = `no answer within ${String(Math.round(timeoutMs / 1000))}s, so it was denied`;\n journal.deny(request.actionId, undefined, reason);\n return { approved: false, reason };\n }\n\n await new Promise<void>((resolve) => setTimeout(resolve, pollMs).unref());\n }\n },\n };\n}\n","import pino, { type Logger } from \"pino\";\n\nexport type { Logger };\n\nexport const LOG_LEVELS = [\"trace\", \"debug\", \"info\", \"warn\", \"error\", \"silent\"] as const;\nexport type LogLevel = (typeof LOG_LEVELS)[number];\n\nexport function isLogLevel(value: string): value is LogLevel {\n return LOG_LEVELS.some((level) => level === value);\n}\n\n/**\n * Always fd 2. stdout carries MCP protocol frames, and a single stray log line\n * on it corrupts the session for every client. Synchronous so that the last\n * lines before an exit are not lost, which is exactly when they matter.\n */\nexport function createLogger(level: LogLevel): Logger {\n return pino(\n { level, base: { name: \"synartesis\" } },\n pino.destination({ dest: 2, sync: true }),\n );\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport {\n CallToolRequestSchema,\n CompleteRequestSchema,\n ErrorCode,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourceTemplatesRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n McpError,\n ReadResourceRequestSchema,\n SetLevelRequestSchema,\n SubscribeRequestSchema,\n UnsubscribeRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type {\n Implementation,\n Request,\n ServerCapabilities,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\n\nimport { SnapshotError, UpstreamError, describe } from \"../errors.js\";\nimport { createRetryGate, type ApproveHint, type Gate } from \"../gate/gate.js\";\nimport { shouldGateOnWrite } from \"../gate/heuristic.js\";\nimport type { Journal } from \"../journal/journal.js\";\nimport type { Logger } from \"../logging.js\";\nimport {\n createPolicyResolver,\n type PolicyResolver,\n} from \"../manifest/match.js\";\nimport { qualify, type Manifest } from \"../manifest/types.js\";\nimport { createRouter, type Router } from \"./routing.js\";\nimport {\n observeState,\n planInverse,\n isDisconnected,\n mayHaveArrived,\n planRead,\n refusal,\n runRead,\n toPayload,\n type ResolvedRead,\n} from \"./snapshot.js\";\nimport type { Upstream } from \"./upstream.js\";\n\nexport interface ProxyOptions {\n readonly upstreams: readonly Upstream[];\n readonly manifest: Manifest;\n readonly journal: Journal;\n /** Defaults to out-of-band approval through the journal. */\n readonly gate?: Gate;\n readonly gateTimeoutMs?: number;\n readonly logger?: Logger;\n /** Builds the exact command a person here would run to approve an action. */\n readonly approveHint?: ApproveHint;\n}\n\nexport interface ProxyServer {\n readonly server: McpServer;\n /** Resolves with the run id once the client session is initialized. */\n readonly ready: Promise<string>;\n /** Resolves when no tool call is in flight, so shutdown can drain first. */\n whenIdle(): Promise<void>;\n /** The open run, once the session has initialized. */\n readonly runId: string | undefined;\n /** Whether any forwarded request is currently in flight. */\n busy(): boolean;\n}\n\ntype Passthrough = { [key: string]: unknown };\n\n/**\n * How long an approval stays usable. Long enough to survive a client restart\n * and a person walking away from their desk, short enough that a decision made\n * this morning cannot quietly authorise the same call tomorrow.\n */\nconst APPROVAL_WINDOW_MS = 60 * 60 * 1000;\n\n/**\n * Results are read through loose schemas. The SDK's typed schemas strip fields\n * they do not know about, which would quietly erase any metadata an upstream\n * added; only the names this proxy has to rewrite are described here.\n */\nconst PassthroughResult = z.looseObject({});\nconst ToolList = z.looseObject({\n tools: z.array(z.looseObject({ name: z.string() })),\n nextCursor: z.string().optional(),\n});\nconst PromptList = z.looseObject({\n prompts: z.array(z.looseObject({ name: z.string() })),\n nextCursor: z.string().optional(),\n});\nconst ResourceList = z.looseObject({\n resources: z.array(z.looseObject({ uri: z.string() })),\n nextCursor: z.string().optional(),\n});\nconst TemplateList = z.looseObject({\n resourceTemplates: z.array(z.looseObject({ uriTemplate: z.string() })),\n nextCursor: z.string().optional(),\n});\n\nfunction unwrap(error: McpError): string {\n const prefix = `MCP error ${String(error.code)}: `;\n return error.message.startsWith(prefix)\n ? error.message.slice(prefix.length)\n : error.message;\n}\n\nfunction rethrow(server: string, operation: string, error: unknown): never {\n if (error instanceof McpError) {\n throw new McpError(error.code, unwrap(error), error.data);\n }\n throw new UpstreamError(server, operation, error);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * The client sees one logical server, so it must be told about anything any\n * upstream can do. Sub-objects are merged rather than replaced so that, for\n * example, one server's resources.subscribe survives another's resources {}.\n */\nfunction mergeCapabilities(\n all: readonly ServerCapabilities[],\n): ServerCapabilities {\n const merged: Record<string, unknown> = {};\n for (const capabilities of all) {\n for (const [key, value] of Object.entries(capabilities)) {\n const existing = merged[key];\n merged[key] =\n isRecord(existing) && isRecord(value)\n ? { ...existing, ...value }\n : value;\n }\n }\n return merged;\n}\n\nfunction identityFor(router: Router): Implementation {\n const only = router.upstreams[0];\n if (!router.prefixed && only !== undefined) {\n const upstream = only.client.getServerVersion();\n if (upstream !== undefined) {\n return upstream;\n }\n }\n // With several servers behind it there is no single identity to mirror.\n return { name: \"synartesis\", version: \"0.0.0\" };\n}\n\n/**\n * Told to the agent at connect time. Without it a gated call is just an opaque\n * failure, and the person watching has no idea why their agent stopped or what\n * they are supposed to do about it. With it, the agent explains itself.\n */\nconst SYNARTESIS_INSTRUCTIONS = [\n \"These tools are guarded by Synartesis, which records every change so it can be undone later.\",\n \"\",\n \"Some actions cannot be undone. Those are held until a person approves them, and the call\",\n \"will fail with a message beginning \\\"Synartesis is holding this call for approval\\\".\",\n \"When that happens:\",\n \" 1. Tell the user plainly that you are asking Synartesis for approval, and what for.\",\n \" 2. Give them the exact `synartesis approve ...` command from the error.\",\n \" 3. Once they say they have approved it, make the same call again. It will go through.\",\n \"Do not try to work around a held call by using a different tool to achieve the same thing.\",\n].join(\"\\n\");\n\nfunction instructionsFor(router: Router): string {\n const sections = router.upstreams\n .map((upstream) => ({\n name: upstream.name,\n text: upstream.client.getInstructions(),\n }))\n .filter(\n (section): section is { name: string; text: string } => section.text !== undefined,\n );\n\n const upstream = router.prefixed\n ? sections.map((section) => `Tools prefixed ${section.name}__:\\n${section.text}`).join(\"\\n\\n\")\n : (sections[0]?.text ?? \"\");\n\n return upstream === \"\" ? SYNARTESIS_INSTRUCTIONS : `${SYNARTESIS_INSTRUCTIONS}\\n\\n${upstream}`;\n}\n\n/** Walks every page so that aggregation across servers is never partial. */\nasync function drain<T>(\n fetch: (\n cursor: string | undefined,\n ) => Promise<{ items: T[]; nextCursor: string | undefined }>,\n): Promise<T[]> {\n const collected: T[] = [];\n let cursor: string | undefined;\n do {\n const page = await fetch(cursor);\n collected.push(...page.items);\n cursor = page.nextCursor;\n } while (cursor !== undefined);\n return collected;\n}\n\nexport function createProxyServer(options: ProxyOptions): ProxyServer {\n const { upstreams, manifest, journal } = options;\n const router = createRouter(upstreams, manifest);\n const policies: PolicyResolver = createPolicyResolver(manifest);\n\n const log = options.logger;\n\n const gate = options.gate ?? createRetryGate(journal, options.approveHint);\n\n const capabilities = mergeCapabilities(\n upstreams.map((upstream) => upstream.client.getServerCapabilities() ?? {}),\n );\n const instructions = instructionsFor(router);\n\n const wrapper = new McpServer(identityFor(router), { capabilities, instructions });\n const server = wrapper.server;\n\n let runId: string | undefined;\n let resolveReady: (id: string) => void = () => undefined;\n const ready = new Promise<string>((resolve) => {\n resolveReady = resolve;\n });\n\n let inflight = 0;\n const idle: (() => void)[] = [];\n const enter = (): void => {\n inflight += 1;\n };\n /**\n * Every decrement goes through here, including the one that parks a call at\n * the gate. A decrement that reached zero without waking the waiters would\n * leave a shutdown draining for ever against a counter that is already idle.\n */\n const leave = (): void => {\n inflight -= 1;\n if (inflight === 0) {\n for (const resolve of idle.splice(0)) {\n resolve();\n }\n }\n };\n const whenIdle = async (): Promise<void> => {\n if (inflight === 0) {\n return;\n }\n await new Promise<void>((resolve) => idle.push(resolve));\n };\n\n // A client that pipelines notifications/initialized ahead of the initialize\n // response can reach oninitialized before its own identity is recorded, so\n // the label is filled in at the first opportunity rather than once.\n let labelled = false;\n const ensureLabel = (): void => {\n if (labelled || runId === undefined) {\n return;\n }\n const name = server.getClientVersion()?.name;\n if (name !== undefined) {\n journal.setRunLabel(runId, name);\n labelled = true;\n }\n };\n\n const supports = (\n upstream: Upstream,\n key: keyof ServerCapabilities,\n ): boolean => upstream.client.getServerCapabilities()?.[key] !== undefined;\n\n const ask = async (\n upstream: Upstream,\n request: Request,\n signal: AbortSignal,\n ): Promise<Passthrough> => {\n try {\n return await upstream.client.request(request, PassthroughResult, {\n signal,\n });\n } catch (error: unknown) {\n return rethrow(upstream.name, request.method, error);\n }\n };\n\n // --- resource ownership -------------------------------------------------\n // A resource uri is an opaque identifier the client hands back verbatim, so\n // unlike a tool name it cannot be namespaced. Ownership therefore has to be\n // discovered from what each server advertises.\n let owners: Map<string, string> | undefined;\n let schemes: Map<string, string> | undefined;\n let conflict: string | undefined;\n\n const refreshResources = async (signal: AbortSignal): Promise<void> => {\n const nextOwners = new Map<string, string>();\n const nextSchemes = new Map<string, string>();\n let nextConflict: string | undefined;\n\n for (const upstream of router.upstreams) {\n if (!supports(upstream, \"resources\")) {\n continue;\n }\n const resources = await drain(async (cursor) => {\n const raw = await ask(\n upstream,\n {\n method: \"resources/list\",\n params: cursor === undefined ? {} : { cursor },\n },\n signal,\n );\n const page = ResourceList.parse(raw);\n return { items: page.resources, nextCursor: page.nextCursor };\n });\n for (const resource of resources) {\n const existing = nextOwners.get(resource.uri);\n if (existing !== undefined && existing !== upstream.name) {\n nextConflict ??= `resource ${resource.uri} is advertised by both ${existing} and ${upstream.name}; a uri cannot be namespaced, so one of them must stop exposing it`;\n }\n nextOwners.set(resource.uri, existing ?? upstream.name);\n const scheme = resource.uri.split(\":\")[0] ?? \"\";\n if (scheme !== \"\" && !nextSchemes.has(scheme)) {\n nextSchemes.set(scheme, upstream.name);\n }\n }\n\n const templates = await drain(async (cursor) => {\n const raw = await ask(\n upstream,\n {\n method: \"resources/templates/list\",\n params: cursor === undefined ? {} : { cursor },\n },\n signal,\n );\n const page = TemplateList.parse(raw);\n return { items: page.resourceTemplates, nextCursor: page.nextCursor };\n });\n for (const template of templates) {\n const scheme = template.uriTemplate.split(\":\")[0] ?? \"\";\n if (scheme !== \"\" && !nextSchemes.has(scheme)) {\n nextSchemes.set(scheme, upstream.name);\n }\n }\n }\n\n owners = nextOwners;\n schemes = nextSchemes;\n conflict = nextConflict;\n };\n\n const ensureResources = async (signal: AbortSignal): Promise<void> => {\n if (owners === undefined) {\n await refreshResources(signal);\n }\n if (conflict !== undefined) {\n throw new McpError(ErrorCode.InternalError, conflict);\n }\n };\n\n const ownerOf = async (\n uri: string,\n signal: AbortSignal,\n ): Promise<Upstream> => {\n await ensureResources(signal);\n const direct = owners?.get(uri);\n const scheme = uri.split(\":\")[0] ?? \"\";\n const name = direct ?? schemes?.get(scheme);\n const upstream = name === undefined ? undefined : router.byName(name);\n if (upstream === undefined) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `no configured server provides ${uri}`,\n );\n }\n return upstream;\n };\n\n // --- handlers -----------------------------------------------------------\n if (capabilities.tools !== undefined) {\n server.setRequestHandler(\n ListToolsRequestSchema,\n async (_request, extra) => {\n const tools: Passthrough[] = [];\n for (const upstream of router.upstreams) {\n if (!supports(upstream, \"tools\")) {\n continue;\n }\n const items = await drain(async (cursor) => {\n const raw = await ask(\n upstream,\n {\n method: \"tools/list\",\n params: cursor === undefined ? {} : { cursor },\n },\n extra.signal,\n );\n const page = ToolList.parse(raw);\n return { items: page.tools, nextCursor: page.nextCursor };\n });\n for (const tool of items) {\n tools.push({\n ...tool,\n name: router.expose(upstream.name, tool.name),\n });\n }\n }\n // Pagination is flattened: a cursor would have to encode a position\n // across several independent servers, and the client gains nothing.\n return { tools };\n },\n );\n\n server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {\n if (runId === undefined) {\n throw new UpstreamError(\"proxy\", \"tools/call\", \"no active run\");\n }\n // Captured: narrowing does not survive into the closures below.\n const activeRun = runId;\n ensureLabel();\n const route = router.route(request.params.name);\n if (route === undefined) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `no configured server provides tool ${request.params.name}`,\n );\n }\n\n const { policy } = policies.resolve(qualify(route.upstream.name, route.tool));\n const args = request.params.arguments ?? {};\n // Counted from here, not from the forward call: the pre-read is part of\n // the action, and a shutdown that aborts it blocks a legitimate write.\n enter();\n try {\n const wantsGate =\n policy.gate === \"always\" || (policy.gate === \"on_write\" && shouldGateOnWrite(args));\n\n // A retry after an out-of-band approval reuses the row that was\n // approved, so the approval ends up on the action that actually ran\n // rather than on an abandoned twin of it.\n const granted = wantsGate\n ? journal.findApproval({\n server: route.upstream.name,\n tool: route.tool,\n args,\n notBefore: new Date(Date.now() - APPROVAL_WINDOW_MS).toISOString(),\n })\n : undefined;\n\n // An approval granted in an earlier session cannot simply be adopted:\n // the action belongs to the run happening now, or undoing this run\n // would not include it.\n const inherited =\n granted !== undefined && granted.runId !== activeRun ? granted : undefined;\n\n // Nobody has answered yet and the agent is asking again. Reusing the\n // row it is already waiting on keeps one call to one decision, which\n // is what `synartesis gates` and `approve` both assume.\n const waiting =\n granted === undefined && wantsGate\n ? journal.findGated({\n runId: activeRun,\n server: route.upstream.name,\n tool: route.tool,\n args,\n })\n : undefined;\n\n const reusable = waiting ?? (inherited === undefined ? granted : undefined);\n const pending =\n reusable === undefined\n ? journal.recordPending({\n runId: activeRun,\n server: route.upstream.name,\n tool: route.tool,\n args,\n class: policy.class,\n })\n : {\n actionId: reusable.id,\n seq: reusable.seq,\n idempotencyKey: reusable.idempotencyKey,\n };\n\n if (inherited !== undefined) {\n journal.adoptApproval(pending.actionId, inherited);\n } else if (granted !== undefined && waiting === undefined) {\n // Reusing the approved row itself: from here its outcome stops being\n // known, so it stops being `approved`.\n journal.markInFlight(granted.id);\n }\n if (granted !== undefined) {\n log?.info(\n { action: pending.actionId, by: granted.approvedBy, from: granted.runId },\n \"proceeding on a standing approval\",\n );\n }\n\n const decide = async (why: string): Promise<void> => {\n // Parked, not working: a suspended call must not hold up shutdown,\n // and the drain exists to let real work finish.\n leave();\n let decision;\n try {\n decision = await gate.decide({\n actionId: pending.actionId,\n runId: activeRun,\n seq: pending.seq,\n server: route.upstream.name,\n tool: route.tool,\n args,\n why,\n signal: extra.signal,\n });\n } finally {\n enter();\n }\n log?.info(\n { action: pending.actionId, approved: decision.approved },\n decision.approved ? \"approved\" : \"denied\",\n );\n // An approval that lands after the client has given up would send\n // a real email that the agent has already reported as not sent.\n // Nobody is waiting for the result, so the safe reading of an\n // approval nobody can hear is that it did not happen.\n if (decision.approved && extra.signal.aborted) {\n journal.settleAsDenied(\n pending.actionId,\n decision.by,\n \"approved, but the client had already stopped waiting, so it was not sent\",\n );\n throw new McpError(\n ErrorCode.InvalidRequest,\n `synartesis blocked ${request.params.name}: it was approved after the client stopped waiting, so it was not sent. Ask the agent to try again.`,\n );\n }\n if (!decision.approved) {\n if (decision.awaiting === true) {\n log?.warn(\n {\n action: pending.actionId,\n tool: `${route.upstream.name}.${route.tool}`,\n approve: options.approveHint?.(pending.actionId) ?? pending.actionId,\n },\n \"awaiting approval\",\n );\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Synartesis is holding this call for approval, because ${why}. ${decision.reason}`,\n );\n }\n const who = decision.by === undefined ? \"\" : ` by ${decision.by}`;\n throw new McpError(\n ErrorCode.InvalidRequest,\n `synartesis blocked ${request.params.name}: ${why} and was denied${who}. ${decision.reason}`,\n );\n }\n };\n\n // D4/3.4: a policy gate suspends before anything is read or written, so\n // a gated action never even looks at the resource.\n // decide() throws on refusal, so getting past this means approved.\n const askedAlready = wantsGate;\n if (wantsGate && granted === undefined) {\n await decide(\"this action cannot be undone\");\n }\n\n // The pre-read happens before the write goes out, and a failure stops\n // the write entirely: a reversible action without a snapshot is\n // silently irreversible, which is worse than the action not happening.\n let snapshot: unknown;\n let verify: ResolvedRead | undefined;\n let missingPriorState: string | undefined;\n if (policy.snapshot !== undefined) {\n try {\n verify = planRead(policy.snapshot, { args });\n snapshot = await runRead(router, verify, extra.signal);\n journal.attachSnapshot(pending.actionId, snapshot);\n } catch (error: unknown) {\n const reason = describe(error);\n if (error instanceof SnapshotError && error.absent) {\n // Nothing exists here yet, so this call creates rather than\n // replaces and there is nothing to put back. It is an\n // irreversible action wearing a reversible policy. Refusing\n // outright would mean an agent could never create anything, so\n // it falls through to the same question the gate asks.\n missingPriorState = reason;\n verify = undefined;\n } else {\n journal.markFailed(pending.actionId, reason);\n log?.error(\n { seq: pending.seq, tool: route.tool, reason },\n \"write blocked: snapshot failed\",\n );\n throw new McpError(\n ErrorCode.InternalError,\n `synartesis blocked ${request.params.name}: ${reason}`,\n );\n }\n }\n }\n\n if (missingPriorState !== undefined && !askedAlready) {\n // An approval granted out of band counts here too. It was only ever\n // looked up for a policy that asked to be gated, so a write whose\n // prior state was missing -- an agent creating a file, the commonest\n // thing an agent does -- asked, was approved, and asked again, and\n // no number of approvals ever let it through. The instructions this\n // proxy sends to every agent promise the opposite.\n const standing = journal.findApproval({\n server: route.upstream.name,\n tool: route.tool,\n args,\n notBefore: new Date(Date.now() - APPROVAL_WINDOW_MS).toISOString(),\n });\n if (standing === undefined) {\n // Not \"nothing exists here\": every tool-level error on a pre-read\n // arrives here, so a file that exists and merely could not be read\n // came out as one that was not there. The person approving an\n // unundoable write was shown absence and given no way to learn\n // otherwise until after they had allowed it. Say what happened and\n // hand over the server's own words.\n await decide(\n `nothing was captured to restore, so this cannot be undone — the read said: ${missingPriorState}`,\n );\n } else {\n // Moved onto the row that actually runs, which also spends it: an\n // approval answers one call, not every call that looks like it.\n journal.adoptApproval(pending.actionId, standing);\n log?.info(\n { action: pending.actionId, by: standing.approvedBy, from: standing.runId },\n \"proceeding on a standing approval\",\n );\n }\n }\n\n const forwarded: Request = {\n method: \"tools/call\",\n params: { ...request.params, name: route.tool },\n };\n\n try {\n const result = await route.upstream.client.request(forwarded, PassthroughResult, {\n signal: extra.signal,\n });\n\n // The server understood the call and did not do it. Recording that\n // as an action would be worse than not recording it at all: an\n // inverse resolved from a refusal is a compensating call for\n // something that never happened, and undo would faithfully carry it\n // out. The agent still sees the refusal exactly as sent.\n const refused = refusal(result);\n if (refused !== undefined) {\n journal.markFailed(pending.actionId, `the upstream refused the call: ${refused}`);\n log?.debug(\n { seq: pending.seq, tool: route.tool, reason: refused },\n \"refused by the upstream\",\n );\n return result;\n }\n\n const context = { args, snapshot, result: toPayload(result) };\n const warnings: string[] = [];\n if (missingPriorState !== undefined) {\n warnings.push(\n `no prior state existed, so there is nothing to restore: ${missingPriorState}`,\n );\n }\n\n // Resolved now rather than at rollback time (D5).\n let inverse: unknown;\n if (policy.inverse !== undefined && missingPriorState === undefined) {\n try {\n inverse = planInverse(policy.inverse, context);\n } catch (error: unknown) {\n warnings.push(`inverse could not be resolved: ${describe(error)}`);\n }\n }\n\n // Best effort: the write has already applied, so a failed post-read\n // cannot undo it. Phase 4 fails closed when the post-state is\n // missing. A resource that is now absent is a captured post-state,\n // not a missing one.\n let postSnapshot: unknown;\n if (verify !== undefined) {\n try {\n postSnapshot = await observeState(router, verify, extra.signal);\n } catch (error: unknown) {\n warnings.push(`post-state could not be captured: ${describe(error)}`);\n }\n }\n\n if (warnings.length > 0) {\n log?.warn({ seq: pending.seq, tool: route.tool, warnings }, \"applied with reservations\");\n }\n log?.debug(\n { seq: pending.seq, server: route.upstream.name, tool: route.tool, class: policy.class },\n \"applied\",\n );\n journal.markApplied(pending.actionId, {\n result,\n ...(inverse === undefined ? {} : { inverse }),\n ...(verify === undefined ? {} : { verify }),\n ...(postSnapshot === undefined ? {} : { postSnapshot }),\n ...(warnings.length === 0 ? {} : { warning: warnings.join(\"; \") }),\n });\n return result;\n } catch (error: unknown) {\n const disconnected = isDisconnected(error);\n if (extra.signal.aborted || mayHaveArrived(error)) {\n // A transport that closed while a reply was still owed says\n // nothing about whether the call arrived. Recording that as failed\n // asserts it did not, and undo would then step over an action that\n // may well have applied. Having had no connection to write to at\n // all is the other case, and that one really did not happen.\n journal.markUnknown(pending.actionId, describe(error));\n } else {\n journal.markFailed(pending.actionId, describe(error));\n }\n if (disconnected && route.upstream.reconnect !== undefined) {\n // Not to retry this call -- a write must never be sent twice on a\n // guess -- but so the rest of the session is not lost with it.\n await route.upstream.reconnect().catch(() => undefined);\n }\n return rethrow(route.upstream.name, \"tools/call\", error);\n }\n } finally {\n leave();\n }\n });\n }\n\n if (capabilities.resources !== undefined) {\n server.setRequestHandler(\n ListResourcesRequestSchema,\n async (_request, extra) => {\n await refreshResources(extra.signal);\n await ensureResources(extra.signal);\n const resources: Passthrough[] = [];\n for (const upstream of router.upstreams) {\n if (!supports(upstream, \"resources\")) {\n continue;\n }\n const items = await drain(async (cursor) => {\n const raw = await ask(\n upstream,\n {\n method: \"resources/list\",\n params: cursor === undefined ? {} : { cursor },\n },\n extra.signal,\n );\n const page = ResourceList.parse(raw);\n return { items: page.resources, nextCursor: page.nextCursor };\n });\n resources.push(...items);\n }\n return { resources };\n },\n );\n\n server.setRequestHandler(\n ListResourceTemplatesRequestSchema,\n async (_request, extra) => {\n const resourceTemplates: Passthrough[] = [];\n for (const upstream of router.upstreams) {\n if (!supports(upstream, \"resources\")) {\n continue;\n }\n const items = await drain(async (cursor) => {\n const raw = await ask(\n upstream,\n {\n method: \"resources/templates/list\",\n params: cursor === undefined ? {} : { cursor },\n },\n extra.signal,\n );\n const page = TemplateList.parse(raw);\n return {\n items: page.resourceTemplates,\n nextCursor: page.nextCursor,\n };\n });\n resourceTemplates.push(...items);\n }\n return { resourceTemplates };\n },\n );\n\n server.setRequestHandler(\n ReadResourceRequestSchema,\n async (request, extra) => {\n const upstream = await ownerOf(request.params.uri, extra.signal);\n return ask(upstream, request, extra.signal);\n },\n );\n\n if (capabilities.resources.subscribe === true) {\n for (const schema of [SubscribeRequestSchema, UnsubscribeRequestSchema]) {\n server.setRequestHandler(schema, async (request, extra) => {\n const upstream = await ownerOf(request.params.uri, extra.signal);\n return ask(upstream, request, extra.signal);\n });\n }\n }\n }\n\n if (capabilities.prompts !== undefined) {\n server.setRequestHandler(\n ListPromptsRequestSchema,\n async (_request, extra) => {\n const prompts: Passthrough[] = [];\n for (const upstream of router.upstreams) {\n if (!supports(upstream, \"prompts\")) {\n continue;\n }\n const items = await drain(async (cursor) => {\n const raw = await ask(\n upstream,\n {\n method: \"prompts/list\",\n params: cursor === undefined ? {} : { cursor },\n },\n extra.signal,\n );\n const page = PromptList.parse(raw);\n return { items: page.prompts, nextCursor: page.nextCursor };\n });\n for (const prompt of items) {\n prompts.push({\n ...prompt,\n name: router.expose(upstream.name, prompt.name),\n });\n }\n }\n return { prompts };\n },\n );\n\n server.setRequestHandler(GetPromptRequestSchema, async (request, extra) => {\n const route = router.route(request.params.name);\n if (route === undefined) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `no configured server provides prompt ${request.params.name}`,\n );\n }\n return ask(\n route.upstream,\n {\n method: \"prompts/get\",\n params: { ...request.params, name: route.tool },\n },\n extra.signal,\n );\n });\n }\n\n if (capabilities.completions !== undefined) {\n server.setRequestHandler(CompleteRequestSchema, async (request, extra) => {\n const reference = request.params.ref;\n if (reference.type === \"ref/prompt\") {\n const route = router.route(reference.name);\n if (route === undefined) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `unknown prompt ${reference.name}`,\n );\n }\n return ask(\n route.upstream,\n {\n method: \"completion/complete\",\n params: {\n ...request.params,\n ref: { ...reference, name: route.tool },\n },\n },\n extra.signal,\n );\n }\n const upstream = await ownerOf(reference.uri, extra.signal);\n return ask(upstream, request, extra.signal);\n });\n }\n\n if (capabilities.logging !== undefined) {\n server.setRequestHandler(SetLevelRequestSchema, async (request, extra) => {\n // Broadcast: the client is configuring one logical server.\n for (const upstream of router.upstreams) {\n if (supports(upstream, \"logging\")) {\n await ask(upstream, request, extra.signal);\n }\n }\n return {};\n });\n }\n\n // --- lifecycle ----------------------------------------------------------\n let connected = false;\n for (const upstream of router.upstreams) {\n upstream.client.fallbackNotificationHandler = async (\n notification,\n ): Promise<void> => {\n if (notification.method.endsWith(\"list_changed\")) {\n owners = undefined;\n schemes = undefined;\n conflict = undefined;\n }\n if (connected) {\n await server.notification(notification);\n }\n };\n }\n\n server.oninitialized = (): void => {\n connected = true;\n const name = server.getClientVersion()?.name;\n const id = journal.beginRun(name);\n runId = id;\n labelled = name !== undefined;\n resolveReady(id);\n };\n\n const previousOnClose = server.onclose;\n server.onclose = (): void => {\n connected = false;\n if (runId !== undefined) {\n journal.endRun(runId, \"complete\");\n runId = undefined;\n }\n previousOnClose?.();\n };\n\n return {\n server: wrapper,\n ready,\n whenIdle,\n busy: (): boolean => inflight > 0,\n get runId(): string | undefined {\n return runId;\n },\n };\n}\n","/**\n * The `on_write` heuristic for tools whose destructiveness cannot be decided\n * statically, such as a raw SQL runner.\n *\n * This is a heuristic and is documented as one. It exists because the\n * alternative for `postgres.query` is to gate every SELECT, which no operator\n * would tolerate for long. Anything it cannot confidently read as a read is\n * gated (D4): failing to recognise a statement is not evidence that it is safe.\n * `always` remains the correct choice wherever certainty matters.\n */\nconst READ_ONLY = /^(select|with|show|explain|describe|desc|values|table)\\b/;\n\nfunction isReadOnlyStatement(text: string): boolean {\n const stripped = text\n .replace(/--[^\\n]*/g, \" \")\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, \" \")\n .trim();\n if (!READ_ONLY.test(stripped.toLowerCase())) {\n return false;\n }\n // More than one statement means the leading SELECT says nothing about what\n // follows it.\n return stripped.replace(/;\\s*$/, \"\").indexOf(\";\") === -1;\n}\n\nexport function shouldGateOnWrite(args: unknown): boolean {\n if (typeof args !== \"object\" || args === null) {\n return true;\n }\n const strings = Object.values(args).filter(\n (value): value is string => typeof value === \"string\",\n );\n if (strings.length === 0) {\n return true;\n }\n return !strings.every(isReadOnlyStatement);\n}\n"],"mappings":"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","names":["resolve","upstream","resolve","resolve"]}