@ai-ecoverse/mcpcat 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -48,9 +48,11 @@ __export(node_stub_exports, {
48
48
  readFile: () => readFile,
49
49
  rename: () => rename,
50
50
  resolve: () => resolve,
51
+ rmdir: () => rmdir,
51
52
  spawn: () => spawn,
52
53
  stat: () => stat,
53
54
  tmpdir: () => tmpdir,
55
+ unlink: () => unlink,
54
56
  workerData: () => workerData,
55
57
  writeFile: () => writeFile
56
58
  });
@@ -78,6 +80,12 @@ function rename() {
78
80
  function stat() {
79
81
  throw new Error("not available in kernel");
80
82
  }
83
+ function rmdir() {
84
+ throw new Error("not available in kernel");
85
+ }
86
+ function unlink() {
87
+ throw new Error("not available in kernel");
88
+ }
81
89
  function isatty() {
82
90
  return false;
83
91
  }
@@ -5957,6 +5965,133 @@ var McpOAuthProvider = class {
5957
5965
  }
5958
5966
  };
5959
5967
 
5968
+ // src/auth/jerk-token-source.ts
5969
+ var MIN_VERSION = [0, 13, 0];
5970
+ var VERSION_TIMEOUT_MS = 2e3;
5971
+ var cachedAvailable;
5972
+ function parseVersion(raw) {
5973
+ const match = raw.trim().match(/(\d+)\.(\d+)\.(\d+)/);
5974
+ if (!match) return void 0;
5975
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
5976
+ }
5977
+ function versionSatisfied(actual) {
5978
+ for (let i = 0; i < 3; i++) {
5979
+ if (actual[i] > MIN_VERSION[i]) return true;
5980
+ if (actual[i] < MIN_VERSION[i]) return false;
5981
+ }
5982
+ return true;
5983
+ }
5984
+ async function readAll(stream) {
5985
+ const reader = stream.getReader();
5986
+ const chunks = [];
5987
+ for (; ; ) {
5988
+ const { done, value } = await reader.read();
5989
+ if (done) break;
5990
+ chunks.push(value);
5991
+ }
5992
+ const total = chunks.reduce((s, c) => s + c.length, 0);
5993
+ const buf = new Uint8Array(total);
5994
+ let off = 0;
5995
+ for (const c of chunks) {
5996
+ buf.set(c, off);
5997
+ off += c.length;
5998
+ }
5999
+ return new TextDecoder().decode(buf);
6000
+ }
6001
+ async function jerkOnPath(platform2) {
6002
+ const pathVar = platform2.env.PATH ?? platform2.env.Path ?? "";
6003
+ const sep = pathVar.includes(";") ? ";" : ":";
6004
+ const dirs = pathVar.split(sep).filter(Boolean);
6005
+ for (const dir of dirs) {
6006
+ if (await platform2.fs.exists(`${dir}/jerk`)) return true;
6007
+ }
6008
+ return false;
6009
+ }
6010
+ async function jerkAvailable(platform2) {
6011
+ if (cachedAvailable !== void 0) return cachedAvailable;
6012
+ if (!await jerkOnPath(platform2)) {
6013
+ cachedAvailable = false;
6014
+ return false;
6015
+ }
6016
+ try {
6017
+ const proc = await platform2.spawn({ argv: ["jerk", "--version"] });
6018
+ const timeout = new Promise((_, reject) => {
6019
+ setTimeout(() => reject(new Error("timeout")), VERSION_TIMEOUT_MS);
6020
+ });
6021
+ const work = Promise.all([readAll(proc.stdout), proc.wait()]).then(([out, result]) => {
6022
+ proc.stderr.cancel().catch(() => {
6023
+ });
6024
+ if (result.status !== 0) return false;
6025
+ const ver = parseVersion(out);
6026
+ return ver !== void 0 && versionSatisfied(ver);
6027
+ });
6028
+ cachedAvailable = await Promise.race([work, timeout]);
6029
+ } catch {
6030
+ cachedAvailable = false;
6031
+ }
6032
+ return cachedAvailable;
6033
+ }
6034
+ function jerkId(name) {
6035
+ if (name.startsWith("mcp:")) return name;
6036
+ if (name.startsWith("url:")) return `mcp:${name}`;
6037
+ return `mcp:${name}`;
6038
+ }
6039
+ var JERK_EXIT_SIGN_IN = 3;
6040
+ var JERK_EXIT_UNAVAILABLE = 4;
6041
+ var JerkSignInRequiredError = class extends Error {
6042
+ id;
6043
+ scope;
6044
+ constructor(id, scope) {
6045
+ const scopeHint = scope ? ` --scope ${scope}` : "";
6046
+ super(`sign-in required. Run: jerk ${id} create${scopeHint}`);
6047
+ this.name = "JerkSignInRequiredError";
6048
+ this.id = id;
6049
+ this.scope = scope;
6050
+ }
6051
+ };
6052
+ var JerkTokenSource = class {
6053
+ platform;
6054
+ constructor(platform2) {
6055
+ this.platform = platform2;
6056
+ }
6057
+ async getToken(name, serverUrl, scope) {
6058
+ const id = jerkId(name);
6059
+ const argv = ["jerk", id, "get", "--reveal", "--json"];
6060
+ if (scope) {
6061
+ argv.push("--scope", scope);
6062
+ }
6063
+ const proc = await this.platform.spawn({ argv });
6064
+ const [outRaw, , result] = await Promise.all([
6065
+ readAll(proc.stdout),
6066
+ readAll(proc.stderr),
6067
+ proc.wait()
6068
+ ]);
6069
+ if (result.status === JERK_EXIT_SIGN_IN) {
6070
+ throw new JerkSignInRequiredError(id, scope);
6071
+ }
6072
+ if (result.status === JERK_EXIT_UNAVAILABLE) {
6073
+ return void 0;
6074
+ }
6075
+ if (result.status !== 0) {
6076
+ return void 0;
6077
+ }
6078
+ const parsed = JSON.parse(outRaw.trim());
6079
+ if (!parsed.token) return void 0;
6080
+ return {
6081
+ accessToken: parsed.token,
6082
+ expiresAt: parsed.expires_at
6083
+ };
6084
+ }
6085
+ async load(_name, _serverUrl) {
6086
+ return void 0;
6087
+ }
6088
+ async save(_name, _serverUrl, _state) {
6089
+ }
6090
+ async remove(_name, _serverUrl) {
6091
+ return false;
6092
+ }
6093
+ };
6094
+
5960
6095
  // src/auth/auth-provider.ts
5961
6096
  var CIMD_URL = "https://mcpcat.sliccy.ai/oauth/client.json";
5962
6097
  var CLIENT_NAME = "mcpcat";
@@ -6009,6 +6144,47 @@ var AuthStepUpError = class extends Error {
6009
6144
  }
6010
6145
  };
6011
6146
  function createMcpAuthProvider(options) {
6147
+ if (options.source instanceof JerkTokenSource) {
6148
+ return createJerkAuthProvider({ ...options, source: options.source });
6149
+ }
6150
+ return createPiAuthProvider(options);
6151
+ }
6152
+ function createJerkAuthProvider(options) {
6153
+ const { name, serverUrl, source } = options;
6154
+ let currentToken;
6155
+ return {
6156
+ async token() {
6157
+ const result = await source.getToken(name, serverUrl);
6158
+ currentToken = result?.accessToken;
6159
+ return currentToken;
6160
+ },
6161
+ async onUnauthorized(context) {
6162
+ const challenge = parseWwwAuthenticate(context.response.headers.get("www-authenticate"));
6163
+ if (challenge.error === "insufficient_scope") {
6164
+ try {
6165
+ const result2 = await source.getToken(name, serverUrl, challenge.scope);
6166
+ if (result2) {
6167
+ currentToken = result2.accessToken;
6168
+ return;
6169
+ }
6170
+ } catch (err) {
6171
+ if (err instanceof JerkSignInRequiredError) {
6172
+ throw new AuthStepUpError(challenge.scope, options.name);
6173
+ }
6174
+ throw err;
6175
+ }
6176
+ throw new AuthStepUpError(challenge.scope, options.name);
6177
+ }
6178
+ const result = await source.getToken(name, serverUrl);
6179
+ if (result) {
6180
+ currentToken = result.accessToken;
6181
+ return;
6182
+ }
6183
+ throw new McpOAuthAuthorizationRequiredError();
6184
+ }
6185
+ };
6186
+ }
6187
+ function createPiAuthProvider(options) {
6012
6188
  const { name, serverUrl, source, oauth, platform: platform2 } = options;
6013
6189
  const store = new SourceBackedStore(source, name, serverUrl);
6014
6190
  let refreshing;
@@ -7454,6 +7630,53 @@ function buildServerInfo(discoverResult2) {
7454
7630
  instructions: discoverResult2?.instructions
7455
7631
  };
7456
7632
  }
7633
+ function buildStatelessMethods(requestFn, discoverResult2) {
7634
+ let serverInfoCache;
7635
+ return {
7636
+ async info() {
7637
+ if (serverInfoCache) return serverInfoCache;
7638
+ serverInfoCache = buildServerInfo(discoverResult2);
7639
+ return serverInfoCache;
7640
+ },
7641
+ async listTools() {
7642
+ return (await requestFn("tools/list", {}))?.tools ?? [];
7643
+ },
7644
+ async callTool(name, args, _callOptions) {
7645
+ const params = {
7646
+ name,
7647
+ arguments: args ?? {}
7648
+ };
7649
+ if (_callOptions?.inputResponses) {
7650
+ params._inputResponses = _callOptions.inputResponses;
7651
+ }
7652
+ const result = await requestFn("tools/call", params);
7653
+ if ((result.resultType ?? "complete") === "input_required") {
7654
+ throw new InputRequiredError(
7655
+ JSON.stringify({ name, arguments: args ?? {}, result }, null, 2)
7656
+ );
7657
+ }
7658
+ return result;
7659
+ },
7660
+ async listResources() {
7661
+ return (await requestFn("resources/list", {}))?.resources ?? [];
7662
+ },
7663
+ async listResourceTemplates() {
7664
+ return (await requestFn("resources/templates/list", {}))?.resourceTemplates ?? [];
7665
+ },
7666
+ async readResource(uri) {
7667
+ return await requestFn("resources/read", { uri });
7668
+ },
7669
+ async listPrompts() {
7670
+ return (await requestFn("prompts/list", {}))?.prompts ?? [];
7671
+ },
7672
+ async getPrompt(name, args) {
7673
+ return await requestFn("prompts/get", {
7674
+ name,
7675
+ arguments: args ?? {}
7676
+ });
7677
+ }
7678
+ };
7679
+ }
7457
7680
  async function createStatelessConnection(options) {
7458
7681
  const {
7459
7682
  url,
@@ -7557,11 +7780,16 @@ async function createStatelessConnection(options) {
7557
7780
  protocolVersion: negotiatedVersion ?? STATELESS_PROTOCOL_VERSION,
7558
7781
  clientInfo
7559
7782
  };
7783
+ const cleanParams = { ...params };
7784
+ if (cleanParams._inputResponses) {
7785
+ meta.inputResponses = cleanParams._inputResponses;
7786
+ delete cleanParams._inputResponses;
7787
+ }
7560
7788
  const body = JSON.stringify({
7561
7789
  jsonrpc: "2.0",
7562
7790
  id,
7563
7791
  method,
7564
- params: { ...params, _meta: meta }
7792
+ params: { ...cleanParams, _meta: meta }
7565
7793
  });
7566
7794
  for (let attempt = 0; attempt < 2; attempt++) {
7567
7795
  const { res, token } = await doFetch(method, params, body);
@@ -7590,28 +7818,9 @@ async function createStatelessConnection(options) {
7590
7818
  );
7591
7819
  }
7592
7820
  negotiatedVersion = STATELESS_PROTOCOL_VERSION;
7593
- let serverInfoCache;
7821
+ const extra = buildStatelessMethods(request, discoverResult2);
7594
7822
  return {
7595
- async info() {
7596
- if (serverInfoCache) return serverInfoCache;
7597
- serverInfoCache = buildServerInfo(discoverResult2);
7598
- return serverInfoCache;
7599
- },
7600
- async listTools() {
7601
- return (await request("tools/list", {}))?.tools ?? [];
7602
- },
7603
- async callTool(name, args, _callOptions) {
7604
- const result = await request("tools/call", {
7605
- name,
7606
- arguments: args ?? {}
7607
- });
7608
- if ((result.resultType ?? "complete") === "input_required") {
7609
- throw new InputRequiredError(
7610
- JSON.stringify({ name, arguments: args ?? {}, result }, null, 2)
7611
- );
7612
- }
7613
- return result;
7614
- },
7823
+ ...extra,
7615
7824
  async close() {
7616
7825
  }
7617
7826
  };
@@ -7661,7 +7870,9 @@ async function createPiConnection(options) {
7661
7870
  const clientOptions = {
7662
7871
  name: "mcpcat",
7663
7872
  version: options.version ?? "0.0.0",
7664
- requestTimeoutMs: options.timeoutMs
7873
+ requestTimeoutMs: options.timeoutMs,
7874
+ capabilities: { elicitation: {} },
7875
+ roots: options.roots
7665
7876
  };
7666
7877
  const client = new McpClient(clientOptions);
7667
7878
  const transport = buildTransport(options);
@@ -7707,7 +7918,32 @@ async function createPiConnection(options) {
7707
7918
  inner?.(p);
7708
7919
  };
7709
7920
  }
7710
- return client.callTool(name, args, o);
7921
+ const callArgs = callOptions?.inputResponses ? { ...args, _meta: { inputResponses: callOptions.inputResponses } } : args;
7922
+ return client.callTool(name, callArgs, o);
7923
+ },
7924
+ async listResources() {
7925
+ return client.listResources(reqOpts2());
7926
+ },
7927
+ async listResourceTemplates() {
7928
+ return client.listResourceTemplates(reqOpts2());
7929
+ },
7930
+ async readResource(uri) {
7931
+ return client.readResource(uri, reqOpts2());
7932
+ },
7933
+ async listPrompts() {
7934
+ const result = await client.request(
7935
+ "prompts/list",
7936
+ {},
7937
+ reqOpts2()
7938
+ );
7939
+ return result?.prompts ?? [];
7940
+ },
7941
+ async getPrompt(name, args) {
7942
+ return client.request(
7943
+ "prompts/get",
7944
+ { name, arguments: args ?? {} },
7945
+ reqOpts2()
7946
+ );
7711
7947
  },
7712
7948
  async close() {
7713
7949
  await client.close();
@@ -7725,6 +7961,192 @@ async function createPiConnection(options) {
7725
7961
  }
7726
7962
  }
7727
7963
 
7964
+ // src/client/stdio-stateless.ts
7965
+ var PROTOCOL_VERSION = "2026-07-28";
7966
+ function createStdioJsonRpc(proc, platform2, options) {
7967
+ const encoder2 = new TextEncoder();
7968
+ const decoder3 = new TextDecoder();
7969
+ let nextId2 = 1;
7970
+ let readBuffer = "";
7971
+ const reader = proc.stdout.getReader();
7972
+ let negotiatedVersion;
7973
+ const { version = "0.0.0", timeoutMs = 6e4, signal: externalSignal, verbose } = options;
7974
+ async function readLine2() {
7975
+ while (true) {
7976
+ const nlIdx = readBuffer.indexOf("\n");
7977
+ if (nlIdx >= 0) {
7978
+ const line = readBuffer.slice(0, nlIdx).trim();
7979
+ readBuffer = readBuffer.slice(nlIdx + 1);
7980
+ if (line) return line;
7981
+ continue;
7982
+ }
7983
+ const { done, value } = await reader.read();
7984
+ if (done) throw new Error("stdio server closed unexpectedly");
7985
+ readBuffer += decoder3.decode(value, { stream: true });
7986
+ }
7987
+ }
7988
+ async function send(msg) {
7989
+ const writer = proc.stdin.getWriter();
7990
+ try {
7991
+ await writer.write(encoder2.encode(`${msg}
7992
+ `));
7993
+ } finally {
7994
+ writer.releaseLock();
7995
+ }
7996
+ }
7997
+ async function request(method, params) {
7998
+ const id = nextId2++;
7999
+ const meta = {
8000
+ protocolVersion: negotiatedVersion ?? PROTOCOL_VERSION,
8001
+ clientInfo: { name: "mcpcat", version }
8002
+ };
8003
+ const cleanParams = { ...params };
8004
+ if (cleanParams._inputResponses) {
8005
+ meta.inputResponses = cleanParams._inputResponses;
8006
+ delete cleanParams._inputResponses;
8007
+ }
8008
+ const body = JSON.stringify({
8009
+ jsonrpc: "2.0",
8010
+ id,
8011
+ method,
8012
+ params: { ...cleanParams, _meta: meta }
8013
+ });
8014
+ if (verbose?.enabled) verbose.request(method, body);
8015
+ await send(body);
8016
+ const deadline = Date.now() + timeoutMs;
8017
+ while (Date.now() < deadline) {
8018
+ const remaining = deadline - Date.now();
8019
+ if (externalSignal?.aborted) throw new McpTimeoutError2(method, timeoutMs);
8020
+ const linePromise = readLine2();
8021
+ const timeoutPromise = new Promise(
8022
+ (_, reject) => setTimeout(() => reject(new McpTimeoutError2(method, timeoutMs)), remaining)
8023
+ );
8024
+ const line = await Promise.race([linePromise, timeoutPromise]);
8025
+ const parsed = JSON.parse(line);
8026
+ if (verbose?.enabled) verbose.response(line);
8027
+ if (parsed.id === id) {
8028
+ if (parsed.error) {
8029
+ throw new StatelessRpcError(parsed.error.code, parsed.error.message, parsed.error.data);
8030
+ }
8031
+ return parsed.result;
8032
+ }
8033
+ }
8034
+ throw new McpTimeoutError2(method, timeoutMs);
8035
+ }
8036
+ return {
8037
+ request,
8038
+ close() {
8039
+ try {
8040
+ reader.releaseLock();
8041
+ } catch {
8042
+ }
8043
+ }
8044
+ };
8045
+ }
8046
+ function buildServerInfo2(discoverResult2) {
8047
+ return {
8048
+ name: discoverResult2?.serverInfo?.name,
8049
+ version: discoverResult2?.serverInfo?.version,
8050
+ title: discoverResult2?.serverInfo?.title,
8051
+ protocolVersion: PROTOCOL_VERSION,
8052
+ capabilities: discoverResult2?.capabilities ?? {},
8053
+ instructions: discoverResult2?.instructions
8054
+ };
8055
+ }
8056
+ function buildConnectionMethods(rpc, discoverResult2, proc) {
8057
+ return {
8058
+ async info() {
8059
+ return buildServerInfo2(discoverResult2);
8060
+ },
8061
+ async listTools() {
8062
+ return (await rpc.request("tools/list", {}))?.tools ?? [];
8063
+ },
8064
+ async callTool(name, args, callOptions) {
8065
+ const params = { name, arguments: args ?? {} };
8066
+ if (callOptions?.inputResponses) {
8067
+ params._inputResponses = callOptions.inputResponses;
8068
+ }
8069
+ const result = await rpc.request("tools/call", params);
8070
+ if ((result.resultType ?? "complete") === "input_required") {
8071
+ throw new InputRequiredError(
8072
+ JSON.stringify({ name, arguments: args ?? {}, result }, null, 2)
8073
+ );
8074
+ }
8075
+ return result;
8076
+ },
8077
+ async listResources() {
8078
+ return (await rpc.request("resources/list", {}))?.resources ?? [];
8079
+ },
8080
+ async listResourceTemplates() {
8081
+ return (await rpc.request("resources/templates/list", {}))?.resourceTemplates ?? [];
8082
+ },
8083
+ async readResource(uri) {
8084
+ return await rpc.request("resources/read", { uri });
8085
+ },
8086
+ async listPrompts() {
8087
+ return (await rpc.request("prompts/list", {}))?.prompts ?? [];
8088
+ },
8089
+ async getPrompt(name, args) {
8090
+ return await rpc.request("prompts/get", { name, arguments: args ?? {} });
8091
+ },
8092
+ async close() {
8093
+ rpc.close();
8094
+ try {
8095
+ proc.kill("SIGTERM");
8096
+ } catch {
8097
+ }
8098
+ }
8099
+ };
8100
+ }
8101
+ async function createStdioStatelessConnection(options) {
8102
+ const inheritedEnv = {
8103
+ ...options.platform.env,
8104
+ ...options.env
8105
+ };
8106
+ const proc = await options.platform.spawn({
8107
+ argv: [options.command, ...options.args ?? []],
8108
+ env: inheritedEnv,
8109
+ cwd: options.cwd
8110
+ });
8111
+ void readStderr(proc, options.verbose);
8112
+ const rpc = createStdioJsonRpc(proc, options.platform, {
8113
+ version: options.version,
8114
+ timeoutMs: options.timeoutMs,
8115
+ signal: options.signal,
8116
+ verbose: options.verbose
8117
+ });
8118
+ const discoverRaw = await rpc.request("server/discover", {
8119
+ protocolVersion: PROTOCOL_VERSION
8120
+ });
8121
+ const discoverResult2 = discoverRaw;
8122
+ const supported = (discoverResult2?.supportedVersions ?? []).filter(
8123
+ (v) => typeof v === "string"
8124
+ );
8125
+ if (!supported.includes(PROTOCOL_VERSION)) {
8126
+ proc.kill("SIGTERM");
8127
+ throw new Error(
8128
+ `Server does not support ${PROTOCOL_VERSION} (advertised: ${supported.join(", ") || "none"})`
8129
+ );
8130
+ }
8131
+ return buildConnectionMethods(rpc, discoverResult2, proc);
8132
+ }
8133
+ async function readStderr(proc, verbose) {
8134
+ if (!verbose?.enabled) return;
8135
+ const reader = proc.stderr.getReader();
8136
+ const decoder3 = new TextDecoder();
8137
+ try {
8138
+ while (true) {
8139
+ const { done, value } = await reader.read();
8140
+ if (done) break;
8141
+ const chunk = decoder3.decode(value, { stream: true });
8142
+ for (const line of chunk.split("\n")) {
8143
+ if (line.trim()) verbose.server(line);
8144
+ }
8145
+ }
8146
+ } catch {
8147
+ }
8148
+ }
8149
+
7728
8150
  // src/client/connect.ts
7729
8151
  async function connect(platform2, options) {
7730
8152
  const shared = {
@@ -7735,11 +8157,32 @@ async function connect(platform2, options) {
7735
8157
  platform: platform2
7736
8158
  };
7737
8159
  if (options.command) {
8160
+ if (!options.protocolVersion || options.protocolVersion.startsWith("2026")) {
8161
+ try {
8162
+ return await createStdioStatelessConnection({
8163
+ command: options.command,
8164
+ args: options.args,
8165
+ env: options.env,
8166
+ cwd: options.cwd,
8167
+ platform: platform2,
8168
+ version: options.version,
8169
+ timeoutMs: options.timeoutMs,
8170
+ signal: options.signal,
8171
+ verbose: options.verbose
8172
+ });
8173
+ } catch (err) {
8174
+ const isMethodNotFound = err instanceof StatelessRpcError && err.code === -32601;
8175
+ const isUnsupported = err instanceof Error && err.message.includes("does not support");
8176
+ if (options.protocolVersion?.startsWith("2026")) throw err;
8177
+ if (!isMethodNotFound && !isUnsupported) throw err;
8178
+ }
8179
+ }
7738
8180
  return createPiConnection({
7739
8181
  command: options.command,
7740
8182
  args: options.args,
7741
8183
  env: options.env,
7742
8184
  cwd: options.cwd,
8185
+ roots: options.roots,
7743
8186
  ...shared
7744
8187
  });
7745
8188
  }
@@ -7782,27 +8225,123 @@ async function connect(platform2, options) {
7782
8225
  }
7783
8226
  }
7784
8227
 
7785
- // src/client/targets.ts
7786
- function splitShellStyle(input) {
7787
- const result = [];
7788
- let current = "";
7789
- let inSingle = false;
7790
- let inDouble = false;
7791
- for (let i = 0; i < input.length; i++) {
7792
- const c = input[i];
7793
- if (inSingle) {
7794
- if (c === "'") {
7795
- inSingle = false;
7796
- } else {
7797
- current += c;
7798
- }
7799
- } else if (inDouble) {
7800
- if (c === '"') {
7801
- inDouble = false;
7802
- } else if (c === "\\" && i + 1 < input.length) {
7803
- current += input[++i];
7804
- } else {
7805
- current += c;
8228
+ // src/client/elicit.ts
8229
+ function readLine(platform2) {
8230
+ return new Promise((resolve2, reject) => {
8231
+ const reader = platform2.stdin.getReader();
8232
+ const chunks = [];
8233
+ function pump() {
8234
+ reader.read().then(({ done, value }) => {
8235
+ if (done) {
8236
+ reader.releaseLock();
8237
+ resolve2(new TextDecoder().decode(concatChunks(chunks)).split("\n")[0] ?? "");
8238
+ return;
8239
+ }
8240
+ chunks.push(value);
8241
+ const text = new TextDecoder().decode(concatChunks(chunks));
8242
+ if (text.includes("\n")) {
8243
+ reader.releaseLock();
8244
+ resolve2(text.split("\n")[0]);
8245
+ return;
8246
+ }
8247
+ pump();
8248
+ }, reject);
8249
+ }
8250
+ pump();
8251
+ });
8252
+ }
8253
+ function concatChunks(chunks) {
8254
+ const total = chunks.reduce((s, c) => s + c.length, 0);
8255
+ const merged = new Uint8Array(total);
8256
+ let offset = 0;
8257
+ for (const c of chunks) {
8258
+ merged.set(c, offset);
8259
+ offset += c.length;
8260
+ }
8261
+ return merged;
8262
+ }
8263
+ function coerceValue(raw, type) {
8264
+ if (!type || type === "string") return raw;
8265
+ if (type === "integer") return Number.parseInt(raw, 10);
8266
+ if (type === "number") return Number.parseFloat(raw);
8267
+ if (type === "boolean") return raw === "true" || raw === "1" || raw === "yes";
8268
+ return raw;
8269
+ }
8270
+ async function promptForSchema(platform2, schema) {
8271
+ const answers = {};
8272
+ const properties = schema.properties ?? {};
8273
+ const required = new Set(schema.required ?? []);
8274
+ for (const [key, prop] of Object.entries(properties)) {
8275
+ const req = required.has(key) ? " (required)" : "";
8276
+ const desc = prop.description ? ` - ${prop.description}` : "";
8277
+ const def = prop.default !== void 0 ? ` [${String(prop.default)}]` : "";
8278
+ const choices = prop.enum ? ` (${prop.enum.join("|")})` : "";
8279
+ await platform2.write(2, `${key}${req}${desc}${choices}${def}: `);
8280
+ const line = await readLine(platform2);
8281
+ const value = line.trim();
8282
+ if (value === "" && prop.default !== void 0) {
8283
+ answers[key] = prop.default;
8284
+ } else if (value !== "") {
8285
+ answers[key] = coerceValue(value, prop.type);
8286
+ }
8287
+ }
8288
+ return answers;
8289
+ }
8290
+ function parseInputRequest(result) {
8291
+ const ir = result.inputRequest;
8292
+ if (!ir && !result.inputRequests) return void 0;
8293
+ if (ir) return ir;
8294
+ const reqs = result.inputRequests;
8295
+ if (Array.isArray(reqs) && reqs.length > 0) return reqs[0];
8296
+ return void 0;
8297
+ }
8298
+ async function handleElicitation(platform2, inputRequest, isTty, respondData) {
8299
+ if (inputRequest.url) {
8300
+ if (isTty) {
8301
+ await platform2.write(2, `Open this URL to continue: ${inputRequest.url}
8302
+ `);
8303
+ await platform2.write(2, "Press Enter when done...");
8304
+ await readLine(platform2);
8305
+ return { answers: {} };
8306
+ }
8307
+ return { exitCode: 6 };
8308
+ }
8309
+ if (respondData) {
8310
+ return { answers: respondData };
8311
+ }
8312
+ if (!isTty) {
8313
+ return { exitCode: 6 };
8314
+ }
8315
+ const schema = inputRequest.schema ?? { type: "object", properties: {} };
8316
+ if (inputRequest.message) {
8317
+ await platform2.write(2, `${inputRequest.message}
8318
+ `);
8319
+ }
8320
+ const answers = await promptForSchema(platform2, schema);
8321
+ return { answers };
8322
+ }
8323
+
8324
+ // src/client/targets.ts
8325
+ function splitShellStyle(input) {
8326
+ const result = [];
8327
+ let current = "";
8328
+ let inSingle = false;
8329
+ let inDouble = false;
8330
+ for (let i = 0; i < input.length; i++) {
8331
+ const c = input[i];
8332
+ if (inSingle) {
8333
+ if (c === "'") {
8334
+ inSingle = false;
8335
+ } else {
8336
+ current += c;
8337
+ }
8338
+ } else if (inDouble) {
8339
+ if (c === '"') {
8340
+ inDouble = false;
8341
+ } else if (c === "\\" && i + 1 < input.length) {
8342
+ current += input[++i];
8343
+ } else {
8344
+ current += c;
7806
8345
  }
7807
8346
  } else if (c === "'") {
7808
8347
  inSingle = true;
@@ -8191,10 +8730,19 @@ function buildShellGlobal(platform2) {
8191
8730
  }
8192
8731
  };
8193
8732
  }
8733
+ var DEFAULT_MEMORY_LIMIT_MIB = 256;
8734
+ var MAX_MEMORY_LIMIT_MIB = 1024;
8194
8735
  async function runCode(targets, source, options) {
8195
8736
  const { platform: platform2, json } = options;
8196
8737
  const { code, options: sourceOptions } = parseCodemodeSource(source);
8197
- const timeoutMs = sourceOptions.timeoutMs ?? options.timeoutMs ?? 3e5;
8738
+ const callerTimeoutMs = options.timeoutMs ?? 3e5;
8739
+ const scriptTimeoutMs = sourceOptions.timeoutMs;
8740
+ const timeoutMs = scriptTimeoutMs !== void 0 ? Math.min(callerTimeoutMs, scriptTimeoutMs) : callerTimeoutMs;
8741
+ const memoryLimitMiB = Math.min(
8742
+ options.memoryLimitMiB ?? DEFAULT_MEMORY_LIMIT_MIB,
8743
+ MAX_MEMORY_LIMIT_MIB
8744
+ );
8745
+ const memoryLimitBytes = memoryLimitMiB * 1024 * 1024;
8198
8746
  const { connections, allDeclaredTools, namespaces } = await collectTools(targets);
8199
8747
  const searchDocs = buildSearchDocs(allDeclaredTools, namespaces);
8200
8748
  const stateDir2 = options.stateDir ?? `${platform2.env.XDG_STATE_HOME ?? `${platform2.env.HOME ?? "."}/.local/state`}/mcpcat`;
@@ -8204,7 +8752,7 @@ async function runCode(targets, source, options) {
8204
8752
  ...buildDiscoveryGlobals(allDeclaredTools, namespaces, searchDocs),
8205
8753
  ...options.allowShell ? [buildShellGlobal(platform2)] : []
8206
8754
  ];
8207
- const sandbox = await createSandbox(platform2, toolImpls, globals, timeoutMs);
8755
+ const sandbox = await createSandbox(platform2, toolImpls, globals, timeoutMs, memoryLimitBytes);
8208
8756
  try {
8209
8757
  const result = await sandbox.execute(code, { store });
8210
8758
  if (result.ok) {
@@ -8248,12 +8796,12 @@ async function formatOutput(platform2, result, json) {
8248
8796
  const isTimeout = !result.ok && result.error && result.error.kind === "timeout";
8249
8797
  return result.ok ? 0 : isTimeout ? 124 : 1;
8250
8798
  }
8251
- async function createSandbox(platform2, tools, globals, timeoutMs) {
8799
+ async function createSandbox(platform2, tools, globals, timeoutMs, memoryLimitBytes) {
8252
8800
  const isKernel2 = platform2.packageDir !== void 0;
8253
8801
  if (isKernel2) {
8254
8802
  const { InlineCodemodeSandbox: InlineCodemodeSandbox2 } = await Promise.resolve().then(() => (init_portable(), portable_exports));
8255
8803
  const wasm = await loadWasmKernel(platform2);
8256
- return new InlineCodemodeSandbox2({ tools, globals, timeoutMs, wasm });
8804
+ return new InlineCodemodeSandbox2({ tools, globals, timeoutMs, wasm, memoryLimitBytes });
8257
8805
  }
8258
8806
  const { CodemodeSandbox: CodemodeSandbox2, loadQuickJSWasm: loadQuickJSWasm2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
8259
8807
  const getSeaDir = globalThis.__mcpcat_get_sea_asset_dir;
@@ -8264,6 +8812,7 @@ async function createSandbox(platform2, tools, globals, timeoutMs) {
8264
8812
  tools,
8265
8813
  globals,
8266
8814
  timeoutMs,
8815
+ memoryLimitBytes,
8267
8816
  wasm: loadQuickJSWasm2(wasmPath),
8268
8817
  workerUrl
8269
8818
  });
@@ -8339,6 +8888,14 @@ function containsPlaceholders(value) {
8339
8888
  function expandPlaceholders(value, env2) {
8340
8889
  return value.replace(PLACEHOLDER, (_, name) => env2[name] ?? "");
8341
8890
  }
8891
+ function missingPlaceholders(value, env2) {
8892
+ const missing = [];
8893
+ for (const m of value.matchAll(PLACEHOLDER)) {
8894
+ const name = m[1];
8895
+ if (env2[name] === void 0 && !missing.includes(name)) missing.push(name);
8896
+ }
8897
+ return missing;
8898
+ }
8342
8899
  function expandPlaceholdersStrict(value, env2) {
8343
8900
  let hasUndefined = false;
8344
8901
  const result = value.replace(PLACEHOLDER, (_, name) => {
@@ -8351,6 +8908,20 @@ function expandPlaceholdersStrict(value, env2) {
8351
8908
  if (hasUndefined) return void 0;
8352
8909
  return result;
8353
8910
  }
8911
+ function expandHeaders(headers, env2, warn) {
8912
+ if (!headers) return void 0;
8913
+ const result = {};
8914
+ for (const [k, v] of Object.entries(headers)) {
8915
+ const missing = missingPlaceholders(v, env2);
8916
+ if (missing.length > 0) {
8917
+ if (warn)
8918
+ warn(`header "${k}": unset variable${missing.length > 1 ? "s" : ""} ${missing.join(", ")}`);
8919
+ continue;
8920
+ }
8921
+ result[k] = expandPlaceholders(v, env2);
8922
+ }
8923
+ return Object.keys(result).length > 0 ? result : void 0;
8924
+ }
8354
8925
 
8355
8926
  // src/config/parse.ts
8356
8927
  var SERVER_NAME = /^[A-Za-z0-9_-]+$/;
@@ -8541,7 +9112,12 @@ async function readFile2(fs, path) {
8541
9112
  try {
8542
9113
  const data = await fs.readFile(path);
8543
9114
  return new TextDecoder().decode(data);
8544
- } catch {
9115
+ } catch (err) {
9116
+ if (err.code === "EACCES") {
9117
+ const e = new Error(`permission denied: ${path}`);
9118
+ e.code = "EACCES";
9119
+ throw e;
9120
+ }
8545
9121
  return void 0;
8546
9122
  }
8547
9123
  }
@@ -8706,6 +9282,13 @@ async function removeStaleLock(fs, lockPath) {
8706
9282
  async function acquireLock(fs, configPath) {
8707
9283
  const lockPath = `${configPath}.lock`;
8708
9284
  const start = Date.now();
9285
+ const parentDir = lockPath.slice(0, lockPath.lastIndexOf("/"));
9286
+ if (parentDir) {
9287
+ try {
9288
+ await fs.mkdir(parentDir);
9289
+ } catch {
9290
+ }
9291
+ }
8709
9292
  while (true) {
8710
9293
  try {
8711
9294
  await fs.mkdirExclusive(lockPath);
@@ -8767,7 +9350,15 @@ async function editMcpServers(fs, path, edit) {
8767
9350
  }
8768
9351
  const tmpPath = `${path}.tmp.${Date.now()}`;
8769
9352
  await fs.writeFile(tmpPath, output);
8770
- await fs.rename(tmpPath, path);
9353
+ try {
9354
+ await fs.rename(tmpPath, path);
9355
+ } catch (renameErr) {
9356
+ try {
9357
+ if (fs.unlink) await fs.unlink(tmpPath);
9358
+ } catch {
9359
+ }
9360
+ throw renameErr;
9361
+ }
8771
9362
  return { changed: true };
8772
9363
  } finally {
8773
9364
  await release();
@@ -9144,6 +9735,31 @@ async function getCandidate(fs, stateDir2, id) {
9144
9735
  }
9145
9736
 
9146
9737
  // src/discovery/probe.ts
9738
+ var LOOPBACK_RE = /^(127\.\d+\.\d+\.\d+|\[?::1\]?|localhost)$/i;
9739
+ var LINK_LOCAL_RE = /^(169\.254\.|fe80:)/i;
9740
+ var PRIVATE_RE = /^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|fc|fd)/i;
9741
+ var LOCALHOST_SUFFIX_RE = /\.localhost$/i;
9742
+ var SLICC_INTERNAL_RE = /^host\.slicc\.internal$/i;
9743
+ function isPrivateOrLoopback(hostname) {
9744
+ const bare = hostname.replace(/^\[|\]$/g, "");
9745
+ return LOOPBACK_RE.test(bare) || LINK_LOCAL_RE.test(bare) || PRIVATE_RE.test(bare) || LOCALHOST_SUFFIX_RE.test(bare) || SLICC_INTERNAL_RE.test(bare);
9746
+ }
9747
+ function isSameOrigin(base, candidate) {
9748
+ try {
9749
+ const a = new URL(base);
9750
+ const b = new URL(candidate);
9751
+ return a.origin === b.origin;
9752
+ } catch {
9753
+ return false;
9754
+ }
9755
+ }
9756
+ function isUserUrlPrivate(userUrl) {
9757
+ try {
9758
+ return isPrivateOrLoopback(new URL(userUrl).hostname);
9759
+ } catch {
9760
+ return false;
9761
+ }
9762
+ }
9147
9763
  function isJsonContentType(ct) {
9148
9764
  if (!ct) return false;
9149
9765
  const lower = ct.toLowerCase();
@@ -9278,13 +9894,44 @@ async function probeWellKnown(fetcher, origin, page) {
9278
9894
  }
9279
9895
  return events;
9280
9896
  }
9281
- async function probeLinkHeaders(fetcher, targetUrl, origin, page) {
9897
+ async function probeLinkHeaders(fetcher, targetUrl, origin, page, userUrlIsPrivate) {
9282
9898
  const events = [];
9283
9899
  const targetResult = await probeUrl(fetcher, targetUrl);
9284
9900
  if (!targetResult) return events;
9285
9901
  const links = parseLinkHeaders(targetResult.headers);
9286
9902
  for (const link of links) {
9287
9903
  const resolved = new URL(link.url, targetUrl).href;
9904
+ if (!isSameOrigin(targetUrl, resolved)) {
9905
+ events.push(
9906
+ makeEvent(
9907
+ link.rel,
9908
+ origin,
9909
+ resolved,
9910
+ "link-header",
9911
+ page,
9912
+ "invalid",
9913
+ null,
9914
+ `skipped: cross-origin Link target (${new URL(resolved).origin})`
9915
+ )
9916
+ );
9917
+ continue;
9918
+ }
9919
+ const resolvedHost = new URL(resolved).hostname;
9920
+ if (!userUrlIsPrivate && isPrivateOrLoopback(resolvedHost)) {
9921
+ events.push(
9922
+ makeEvent(
9923
+ link.rel,
9924
+ origin,
9925
+ resolved,
9926
+ "link-header",
9927
+ page,
9928
+ "invalid",
9929
+ null,
9930
+ `skipped: private/loopback target (${resolvedHost})`
9931
+ )
9932
+ );
9933
+ continue;
9934
+ }
9288
9935
  const classifier = link.rel === "ard" ? classifyArd : link.rel === "ai-catalog" ? classifyAiCatalog : null;
9289
9936
  if (!classifier) continue;
9290
9937
  const linkResult = await probeUrl(fetcher, resolved, "application/json");
@@ -9381,9 +10028,10 @@ async function probeDiscover(platform2, targetUrl) {
9381
10028
  }
9382
10029
  const page = targetUrl;
9383
10030
  const fetcher = platform2.fetch;
10031
+ const userPrivate = isUserUrlPrivate(targetUrl);
9384
10032
  const events = [
9385
10033
  ...await probeWellKnown(fetcher, origin, page),
9386
- ...await probeLinkHeaders(fetcher, targetUrl, origin, page),
10034
+ ...await probeLinkHeaders(fetcher, targetUrl, origin, page, userPrivate),
9387
10035
  ...await probeMcp(fetcher, origin, page, targetUrl)
9388
10036
  ];
9389
10037
  return { events };
@@ -9640,13 +10288,27 @@ function renderCallResultJson(result) {
9640
10288
  return JSON.stringify(result, null, 2);
9641
10289
  }
9642
10290
  async function renderCallResult(platform2, result, options) {
10291
+ const r = options?.redact ?? ((s) => s);
10292
+ if (options?.outputPath) {
10293
+ const content = options.json ? renderCallResultJson(result) : renderCallResultText(result, options);
10294
+ await platform2.fs.writeFile(options.outputPath, content);
10295
+ if (result.isError) {
10296
+ await platform2.write(
10297
+ 2,
10298
+ r(`${result.isError ? "(tool reported error)" : ""}
10299
+ `)
10300
+ );
10301
+ return 1;
10302
+ }
10303
+ return 0;
10304
+ }
9643
10305
  if (options?.json) {
9644
10306
  const json = renderCallResultJson(result);
9645
10307
  if (result.isError) {
9646
10308
  await platform2.write(
9647
10309
  2,
9648
- `${JSON.stringify({ error: { kind: "tool_error", message: json } }, null, 2)}
9649
- `
10310
+ r(`${JSON.stringify({ error: { kind: "tool_error", message: json } }, null, 2)}
10311
+ `)
9650
10312
  );
9651
10313
  return 1;
9652
10314
  }
@@ -9656,8 +10318,8 @@ async function renderCallResult(platform2, result, options) {
9656
10318
  }
9657
10319
  const text = renderCallResultText(result, options);
9658
10320
  if (result.isError) {
9659
- await platform2.write(2, `${text || "(tool reported error)"}
9660
- `);
10321
+ await platform2.write(2, r(`${text || "(tool reported error)"}
10322
+ `));
9661
10323
  return 1;
9662
10324
  }
9663
10325
  if (text) {
@@ -9674,13 +10336,33 @@ async function renderInfoJson(platform2, info) {
9674
10336
  await platform2.write(1, `${JSON.stringify(info, null, 2)}
9675
10337
  `);
9676
10338
  }
9677
- async function renderErrorJson(platform2, kind, message, code, data) {
9678
- const errorObj = { kind, message };
10339
+ async function renderErrorJson(platform2, kind, message, code, data, redact2) {
10340
+ const r = redact2 ?? ((s) => s);
10341
+ const errorObj = { kind, message: r(message) };
9679
10342
  if (code !== void 0) errorObj.code = code;
9680
10343
  if (data !== void 0) errorObj.data = data;
9681
10344
  await platform2.write(2, `${JSON.stringify({ error: errorObj }, null, 2)}
9682
10345
  `);
9683
10346
  }
10347
+ async function renderResourcesJson(platform2, resources, templates) {
10348
+ await platform2.write(
10349
+ 1,
10350
+ `${JSON.stringify({ resources, resourceTemplates: templates }, null, 2)}
10351
+ `
10352
+ );
10353
+ }
10354
+ async function renderPromptsJson(platform2, prompts) {
10355
+ await platform2.write(1, `${JSON.stringify({ prompts }, null, 2)}
10356
+ `);
10357
+ }
10358
+ async function renderPromptResultJson(platform2, result) {
10359
+ await platform2.write(1, `${JSON.stringify(result, null, 2)}
10360
+ `);
10361
+ }
10362
+ async function renderReadResourceJson(platform2, result) {
10363
+ await platform2.write(1, `${JSON.stringify(result, null, 2)}
10364
+ `);
10365
+ }
9684
10366
 
9685
10367
  // src/serve/catalog.ts
9686
10368
  var NAME_RE = /^[A-Za-z][\w-]*$/;
@@ -10448,38 +11130,46 @@ function hasOwn(value, key) {
10448
11130
  }
10449
11131
 
10450
11132
  // src/serve/origin.ts
10451
- var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
10452
- function isLoopbackOrigin(origin) {
10453
- try {
10454
- const url = new URL(origin);
10455
- const hostname = url.hostname;
10456
- if (LOOPBACK_HOSTS.has(hostname)) return true;
10457
- if (hostname === "::1") return true;
10458
- if (hostname.endsWith(".localhost")) return true;
10459
- return false;
10460
- } catch {
10461
- return false;
10462
- }
11133
+ function isLoopbackBind(host) {
11134
+ return host === "127.0.0.1" || host === "localhost" || host === "::1" || host === "[::1]";
10463
11135
  }
10464
- function isLoopbackHost(host) {
10465
- const hostname = host.replace(/:\d+$/, "");
10466
- if (LOOPBACK_HOSTS.has(hostname)) return true;
10467
- if (hostname === "::1") return true;
10468
- if (hostname.endsWith(".localhost")) return true;
10469
- return false;
11136
+ function buildServerOrigins(host, port) {
11137
+ const origins = /* @__PURE__ */ new Set();
11138
+ origins.add(`http://${host}:${port}`);
11139
+ origins.add(`http://${port}.kernel.localhost`);
11140
+ if (isLoopbackBind(host)) {
11141
+ origins.add(`http://localhost:${port}`);
11142
+ origins.add(`http://127.0.0.1:${port}`);
11143
+ origins.add(`http://[::1]:${port}`);
11144
+ }
11145
+ return origins;
10470
11146
  }
10471
- function checkOrigin(origin, allowedOrigins, boundLoopback) {
11147
+ function checkOrigin(origin, serverOrigins, allowedOrigins) {
10472
11148
  if (origin === void 0) return { ok: true };
11149
+ if (serverOrigins.has(origin)) return { ok: true };
10473
11150
  if (allowedOrigins.has(origin)) return { ok: true };
10474
- if (isLoopbackOrigin(origin)) return { ok: true };
10475
- if (!boundLoopback) return { ok: true };
10476
11151
  return { ok: false, reason: `origin ${origin} is not allowed` };
10477
11152
  }
10478
- function checkHost(host, boundLoopback) {
10479
- if (!boundLoopback) return { ok: true };
11153
+ function buildAllowedHosts(host, port, extraHosts) {
11154
+ const hosts = /* @__PURE__ */ new Set();
11155
+ hosts.add(`${host}:${port}`);
11156
+ hosts.add(host);
11157
+ hosts.add(`${port}.kernel.localhost`);
11158
+ const loopback2 = ["localhost", "127.0.0.1", "[::1]"];
11159
+ for (const h of loopback2) {
11160
+ hosts.add(h);
11161
+ hosts.add(`${h}:${port}`);
11162
+ }
11163
+ for (const h of extraHosts) hosts.add(h);
11164
+ return hosts;
11165
+ }
11166
+ function checkHost(host, allowedHosts) {
10480
11167
  if (!host) return { ok: false, reason: "missing Host header" };
10481
- if (isLoopbackHost(host)) return { ok: true };
10482
- return { ok: false, reason: `host ${host} is not loopback` };
11168
+ if (allowedHosts.has(host)) return { ok: true };
11169
+ const hostname = host.replace(/:\d+$/, "");
11170
+ if (allowedHosts.has(hostname)) return { ok: true };
11171
+ if (hostname.endsWith(".localhost")) return { ok: true };
11172
+ return { ok: false, reason: `host ${host} is not allowed` };
10483
11173
  }
10484
11174
 
10485
11175
  // src/serve/proxy.ts
@@ -10716,6 +11406,8 @@ function parseSingleFlag(args, i, opts) {
10716
11406
  return parsed.next;
10717
11407
  } else if (arg === "--json") {
10718
11408
  opts.json = true;
11409
+ } else if (arg === "--no-auth") {
11410
+ opts.noAuth = true;
10719
11411
  } else if (!arg.startsWith("-")) {
10720
11412
  opts.commands.push(parseCommandTarget(arg));
10721
11413
  } else {
@@ -10736,6 +11428,9 @@ function parseValueFlag(args, i, opts) {
10736
11428
  case "--allow-origin":
10737
11429
  opts.allowOrigins.push(args[++i]);
10738
11430
  break;
11431
+ case "--allow-host":
11432
+ opts.allowHosts.push(args[++i]);
11433
+ break;
10739
11434
  case "--env":
10740
11435
  opts.envAllow.push(args[++i]);
10741
11436
  break;
@@ -10774,7 +11469,9 @@ function parseServeFlags(args) {
10774
11469
  host: "127.0.0.1",
10775
11470
  port: 0,
10776
11471
  bearer: void 0,
11472
+ noAuth: false,
10777
11473
  allowOrigins: [],
11474
+ allowHosts: [],
10778
11475
  envAllow: [],
10779
11476
  toolTemplates: [],
10780
11477
  callTimeout: CALL_TIMEOUT_MS,
@@ -10803,6 +11500,20 @@ function constantTimeEqual(a, b) {
10803
11500
  }
10804
11501
  return mismatch === 0;
10805
11502
  }
11503
+ function generateToken() {
11504
+ const bytes = new Uint8Array(32);
11505
+ crypto.getRandomValues(bytes);
11506
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
11507
+ }
11508
+ function checkContentType(ct) {
11509
+ if (!ct) return false;
11510
+ return ct.toLowerCase().startsWith("application/json");
11511
+ }
11512
+ function checkAcceptHeader(accept) {
11513
+ if (!accept) return false;
11514
+ const lower = accept.toLowerCase();
11515
+ return lower.includes("application/json") || lower.includes("text/event-stream") || lower.includes("*/*");
11516
+ }
10806
11517
  function buildChildEnv(platform2, envAllow) {
10807
11518
  const env2 = {};
10808
11519
  for (const key of ALLOWED_ENV_KEYS) {
@@ -10851,7 +11562,7 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
10851
11562
  }
10852
11563
  const stdoutChunks = [];
10853
11564
  const stderrChunks = [];
10854
- const readStream2 = async (stream, into) => {
11565
+ const readStream3 = async (stream, into) => {
10855
11566
  const r = stream.getReader();
10856
11567
  try {
10857
11568
  for (; ; ) {
@@ -10867,8 +11578,8 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
10867
11578
  (resolve2) => setTimeout(() => resolve2("timeout"), timeoutMs)
10868
11579
  );
10869
11580
  const work = Promise.all([
10870
- readStream2(proc.stdout, stdoutChunks),
10871
- readStream2(proc.stderr, stderrChunks),
11581
+ readStream3(proc.stdout, stdoutChunks),
11582
+ readStream3(proc.stderr, stderrChunks),
10872
11583
  proc.wait()
10873
11584
  ]);
10874
11585
  const result = await Promise.race([work, timer]);
@@ -11100,7 +11811,10 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
11100
11811
  }
11101
11812
  async function runServe(platform2, options, version) {
11102
11813
  let resolvedBearer;
11103
- if (options.bearer) {
11814
+ let generatedBearer = false;
11815
+ if (options._resolvedBearer) {
11816
+ resolvedBearer = options._resolvedBearer;
11817
+ } else if (options.bearer) {
11104
11818
  const expanded = expandBearerToken(options.bearer, platform2.env);
11105
11819
  if (!expanded) {
11106
11820
  await platform2.write(
@@ -11110,9 +11824,31 @@ async function runServe(platform2, options, version) {
11110
11824
  return 2;
11111
11825
  }
11112
11826
  resolvedBearer = expanded;
11827
+ } else if (options.noAuth) {
11828
+ if (options.transport === "http" && !isLoopbackBind(options.host)) {
11829
+ await platform2.write(2, "mcpcat serve: --no-auth is only allowed on loopback\n");
11830
+ return 2;
11831
+ }
11832
+ if (options.transport === "http") {
11833
+ await platform2.write(
11834
+ 2,
11835
+ "mcpcat serve: warning: running without authentication on loopback\n"
11836
+ );
11837
+ }
11838
+ resolvedBearer = void 0;
11839
+ } else if (options.transport === "http") {
11840
+ if (platform2.kind === "kernel") {
11841
+ await platform2.write(
11842
+ 2,
11843
+ "mcpcat serve: kernel HTTP serve requires --bearer '${VAR}' or --no-auth\n"
11844
+ );
11845
+ return 2;
11846
+ }
11847
+ resolvedBearer = generateToken();
11848
+ generatedBearer = true;
11113
11849
  }
11114
11850
  if (options.proxy) {
11115
- return runProxyServe(platform2, options, version, resolvedBearer);
11851
+ return runProxyServe(platform2, options, version, resolvedBearer, generatedBearer);
11116
11852
  }
11117
11853
  const serveCwd = options.cwd ?? platform2.cwd;
11118
11854
  const childEnv = buildChildEnv(platform2, options.envAllow);
@@ -11149,12 +11885,35 @@ async function runServe(platform2, options, version) {
11149
11885
  executor.invoke,
11150
11886
  serverName,
11151
11887
  version,
11152
- resolvedBearer
11888
+ resolvedBearer,
11889
+ generatedBearer
11153
11890
  );
11154
11891
  }
11892
+ async function publishBearer(platform2, token, port, listenUrl, json) {
11893
+ if (!json && platform2.isatty(2)) {
11894
+ await platform2.write(2, `mcpcat serve: bearer ${token}
11895
+ `);
11896
+ return void 0;
11897
+ }
11898
+ const dir = stateDir(platform2);
11899
+ await platform2.fs.mkdir(dir, { mode: 448 });
11900
+ const tokenPath = `${dir}/serve-${port}.token`;
11901
+ try {
11902
+ await platform2.fs.unlink?.(tokenPath);
11903
+ } catch {
11904
+ }
11905
+ await platform2.fs.writeFile(tokenPath, token, { mode: 384 });
11906
+ await platform2.write(2, `mcpcat serve: bearer saved to ${tokenPath}
11907
+ `);
11908
+ if (json) {
11909
+ await platform2.write(1, `${JSON.stringify({ url: listenUrl, bearerFile: tokenPath })}
11910
+ `);
11911
+ }
11912
+ return tokenPath;
11913
+ }
11155
11914
  function resolveProxyStdio(target, version) {
11156
11915
  const rest = target.slice(6);
11157
- const parts = rest.split(/\s+/).filter(Boolean);
11916
+ const parts = splitShellStyle(rest);
11158
11917
  return { command: parts[0], args: parts.slice(1), version };
11159
11918
  }
11160
11919
  function resolveProxyUrl(target, headers, version) {
@@ -11224,7 +11983,7 @@ async function buildProxyConnectOpts(platform2, options, version) {
11224
11983
  }
11225
11984
  return { connectOpts, hiddenValues };
11226
11985
  }
11227
- async function runProxyServe(platform2, options, version, resolvedBearer) {
11986
+ async function runProxyServe(platform2, options, version, resolvedBearer, generatedBearer) {
11228
11987
  const built = await buildProxyConnectOpts(platform2, options, version);
11229
11988
  if ("error" in built) {
11230
11989
  await platform2.write(2, `mcpcat serve --proxy: ${built.error}
@@ -11242,7 +12001,7 @@ async function runProxyServe(platform2, options, version, resolvedBearer) {
11242
12001
  if (options.transport === "stdio") {
11243
12002
  return runProxyStdio(platform2, proxy);
11244
12003
  }
11245
- return runProxyHttp(platform2, options, proxy, resolvedBearer);
12004
+ return runProxyHttp(platform2, options, proxy, resolvedBearer, generatedBearer);
11246
12005
  }
11247
12006
  async function runProxyStdio(platform2, proxy) {
11248
12007
  const reader = platform2.stdin.getReader();
@@ -11273,12 +12032,12 @@ async function runProxyStdio(platform2, proxy) {
11273
12032
  }
11274
12033
  return 0;
11275
12034
  }
11276
- async function runProxyHttp(platform2, options, proxy, resolvedBearer) {
12035
+ async function runProxyHttp(platform2, options, proxy, resolvedBearer, generatedBearer) {
11277
12036
  if (!platform2.listen) {
11278
12037
  await platform2.write(2, "mcpcat serve --proxy: HTTP transport requires platform.listen\n");
11279
12038
  return 3;
11280
12039
  }
11281
- const boundLoopback = options.host === "127.0.0.1" || options.host === "localhost" || options.host === "::1";
12040
+ const boundLoopback = isLoopbackBind(options.host);
11282
12041
  if (!boundLoopback && !resolvedBearer) {
11283
12042
  await platform2.write(
11284
12043
  2,
@@ -11292,10 +12051,23 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer) {
11292
12051
  port: options.port
11293
12052
  });
11294
12053
  const actualPort = listener.port;
12054
+ const serverOrigins = buildServerOrigins(options.host, actualPort);
12055
+ const allowedHosts = buildAllowedHosts(options.host, actualPort, options.allowHosts);
11295
12056
  const listenUrl = `http://${options.host}:${actualPort}/mcp`;
11296
- if (options.json) {
11297
- await platform2.write(1, `${JSON.stringify({ url: listenUrl })}
12057
+ let tokenFilePath;
12058
+ if (generatedBearer && resolvedBearer) {
12059
+ tokenFilePath = await publishBearer(
12060
+ platform2,
12061
+ resolvedBearer,
12062
+ actualPort,
12063
+ listenUrl,
12064
+ options.json
12065
+ );
12066
+ } else {
12067
+ if (options.json) {
12068
+ await platform2.write(1, `${JSON.stringify({ url: listenUrl })}
11298
12069
  `);
12070
+ }
11299
12071
  }
11300
12072
  await platform2.write(2, `mcpcat serve --proxy: listening on ${listenUrl}
11301
12073
  `);
@@ -11308,12 +12080,23 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer) {
11308
12080
  shutdownRequested = true;
11309
12081
  listener.close();
11310
12082
  void proxy.shutdown();
12083
+ if (tokenFilePath) {
12084
+ platform2.fs.unlink?.(tokenFilePath).catch(() => {
12085
+ });
12086
+ }
11311
12087
  };
11312
12088
  if (platform2.onSignal) {
11313
12089
  platform2.onSignal("SIGINT", shutdown);
11314
12090
  platform2.onSignal("SIGTERM", shutdown);
11315
12091
  }
11316
- const handler = (request) => handleProxyMcpRequest(request, proxy, allowedOrigins, boundLoopback, resolvedBearer);
12092
+ const handler = (request) => handleProxyMcpRequest(
12093
+ request,
12094
+ proxy,
12095
+ serverOrigins,
12096
+ allowedOrigins,
12097
+ allowedHosts,
12098
+ resolvedBearer
12099
+ );
11317
12100
  try {
11318
12101
  for await (const connection of listener) {
11319
12102
  if (shutdownRequested) break;
@@ -11331,20 +12114,40 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer) {
11331
12114
  }
11332
12115
  return 0;
11333
12116
  }
11334
- async function handleProxyMcpRequest(request, proxy, allowedOrigins, boundLoopback, resolvedBearer) {
11335
- if (resolvedBearer) {
11336
- const authHeader = request.headers.authorization;
11337
- const provided = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : "";
11338
- if (!provided || !constantTimeEqual(provided, resolvedBearer)) {
11339
- return {
11340
- status: 401,
11341
- reason: "Unauthorized",
11342
- headers: { "content-type": "text/plain" },
11343
- body: "Unauthorized"
11344
- };
11345
- }
12117
+ async function handleProxyMcpRequest(request, proxy, serverOrigins, allowedOrigins, allowedHosts, resolvedBearer) {
12118
+ if (request.target !== "/mcp") {
12119
+ return {
12120
+ status: 404,
12121
+ reason: "Not Found",
12122
+ headers: { "content-type": "text/plain" },
12123
+ body: "Not Found"
12124
+ };
12125
+ }
12126
+ if (request.method !== "POST") {
12127
+ return {
12128
+ status: 405,
12129
+ reason: "Method Not Allowed",
12130
+ headers: { "content-type": "text/plain", allow: "POST" },
12131
+ body: "Method Not Allowed"
12132
+ };
12133
+ }
12134
+ if (!checkContentType(request.headers["content-type"])) {
12135
+ return {
12136
+ status: 415,
12137
+ reason: "Unsupported Media Type",
12138
+ headers: { "content-type": "text/plain" },
12139
+ body: "Content-Type must be application/json"
12140
+ };
12141
+ }
12142
+ if (!checkAcceptHeader(request.headers.accept)) {
12143
+ return {
12144
+ status: 406,
12145
+ reason: "Not Acceptable",
12146
+ headers: { "content-type": "text/plain" },
12147
+ body: "Accept must include application/json or text/event-stream"
12148
+ };
11346
12149
  }
11347
- const originCheck = checkOrigin(request.headers.origin, allowedOrigins, boundLoopback);
12150
+ const originCheck = checkOrigin(request.headers.origin, serverOrigins, allowedOrigins);
11348
12151
  if (!originCheck.ok) {
11349
12152
  return {
11350
12153
  status: 403,
@@ -11353,7 +12156,7 @@ async function handleProxyMcpRequest(request, proxy, allowedOrigins, boundLoopba
11353
12156
  body: originCheck.reason ?? "Forbidden"
11354
12157
  };
11355
12158
  }
11356
- const hostCheck = checkHost(request.headers.host, boundLoopback);
12159
+ const hostCheck = checkHost(request.headers.host, allowedHosts);
11357
12160
  if (!hostCheck.ok) {
11358
12161
  return {
11359
12162
  status: 403,
@@ -11362,21 +12165,17 @@ async function handleProxyMcpRequest(request, proxy, allowedOrigins, boundLoopba
11362
12165
  body: hostCheck.reason ?? "Forbidden"
11363
12166
  };
11364
12167
  }
11365
- if (request.target !== "/mcp") {
11366
- return {
11367
- status: 404,
11368
- reason: "Not Found",
11369
- headers: { "content-type": "text/plain" },
11370
- body: "Not Found"
11371
- };
11372
- }
11373
- if (request.method !== "POST") {
11374
- return {
11375
- status: 405,
11376
- reason: "Method Not Allowed",
11377
- headers: { "content-type": "text/plain", allow: "POST" },
11378
- body: "Method Not Allowed"
11379
- };
12168
+ if (resolvedBearer) {
12169
+ const authHeader = request.headers.authorization;
12170
+ const provided = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : "";
12171
+ if (!provided || !constantTimeEqual(provided, resolvedBearer)) {
12172
+ return {
12173
+ status: 401,
12174
+ reason: "Unauthorized",
12175
+ headers: { "content-type": "text/plain" },
12176
+ body: "Unauthorized"
12177
+ };
12178
+ }
11380
12179
  }
11381
12180
  const outcome = await proxy.handle(request.body);
11382
12181
  return {
@@ -11414,12 +12213,12 @@ async function runStdioServer(platform2, tools, call, serverName, version) {
11414
12213
  }
11415
12214
  return 0;
11416
12215
  }
11417
- async function runHttpServer(platform2, options, tools, call, serverName, version, resolvedBearer) {
12216
+ async function runHttpServer(platform2, options, tools, call, serverName, version, resolvedBearer, generatedBearer) {
11418
12217
  if (!platform2.listen) {
11419
12218
  await platform2.write(2, "mcpcat serve: HTTP transport requires platform.listen\n");
11420
12219
  return 3;
11421
12220
  }
11422
- const boundLoopback = options.host === "127.0.0.1" || options.host === "localhost" || options.host === "::1";
12221
+ const boundLoopback = isLoopbackBind(options.host);
11423
12222
  if (!boundLoopback && !resolvedBearer) {
11424
12223
  await platform2.write(2, "mcpcat serve: --bearer is required when binding off loopback\n");
11425
12224
  return 2;
@@ -11427,10 +12226,23 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
11427
12226
  const allowedOrigins = new Set(options.allowOrigins);
11428
12227
  const listener = await platform2.listen({ host: options.host, port: options.port });
11429
12228
  const actualPort = listener.port;
12229
+ const serverOrigins = buildServerOrigins(options.host, actualPort);
12230
+ const allowedHosts = buildAllowedHosts(options.host, actualPort, options.allowHosts);
11430
12231
  const listenUrl = `http://${options.host}:${actualPort}/mcp`;
11431
- if (options.json) {
11432
- await platform2.write(1, `${JSON.stringify({ url: listenUrl })}
12232
+ let tokenFilePath;
12233
+ if (generatedBearer && resolvedBearer) {
12234
+ tokenFilePath = await publishBearer(
12235
+ platform2,
12236
+ resolvedBearer,
12237
+ actualPort,
12238
+ listenUrl,
12239
+ options.json
12240
+ );
12241
+ } else {
12242
+ if (options.json) {
12243
+ await platform2.write(1, `${JSON.stringify({ url: listenUrl })}
11433
12244
  `);
12245
+ }
11434
12246
  }
11435
12247
  await platform2.write(2, `mcpcat serve: listening on ${listenUrl}
11436
12248
  `);
@@ -11440,6 +12252,10 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
11440
12252
  shutdownRequested = true;
11441
12253
  listener.close();
11442
12254
  void killInflight();
12255
+ if (tokenFilePath) {
12256
+ platform2.fs.unlink?.(tokenFilePath).catch(() => {
12257
+ });
12258
+ }
11443
12259
  };
11444
12260
  if (platform2.onSignal) {
11445
12261
  platform2.onSignal("SIGINT", shutdown);
@@ -11451,8 +12267,9 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
11451
12267
  call,
11452
12268
  serverName,
11453
12269
  version,
12270
+ serverOrigins,
11454
12271
  allowedOrigins,
11455
- boundLoopback,
12272
+ allowedHosts,
11456
12273
  resolvedBearer
11457
12274
  );
11458
12275
  try {
@@ -11472,37 +12289,7 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
11472
12289
  }
11473
12290
  return 0;
11474
12291
  }
11475
- async function handleMcpRequest(request, tools, call, serverName, version, allowedOrigins, boundLoopback, resolvedBearer) {
11476
- if (resolvedBearer) {
11477
- const authHeader = request.headers.authorization;
11478
- const provided = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : "";
11479
- if (!provided || !constantTimeEqual(provided, resolvedBearer)) {
11480
- return {
11481
- status: 401,
11482
- reason: "Unauthorized",
11483
- headers: { "content-type": "text/plain" },
11484
- body: "Unauthorized"
11485
- };
11486
- }
11487
- }
11488
- const originCheck = checkOrigin(request.headers.origin, allowedOrigins, boundLoopback);
11489
- if (!originCheck.ok) {
11490
- return {
11491
- status: 403,
11492
- reason: "Forbidden",
11493
- headers: { "content-type": "text/plain" },
11494
- body: originCheck.reason ?? "Forbidden"
11495
- };
11496
- }
11497
- const hostCheck = checkHost(request.headers.host, boundLoopback);
11498
- if (!hostCheck.ok) {
11499
- return {
11500
- status: 403,
11501
- reason: "Forbidden",
11502
- headers: { "content-type": "text/plain" },
11503
- body: hostCheck.reason ?? "Forbidden"
11504
- };
11505
- }
12292
+ async function handleMcpRequest(request, tools, call, serverName, version, serverOrigins, allowedOrigins, allowedHosts, resolvedBearer) {
11506
12293
  if (request.target !== "/mcp") {
11507
12294
  return {
11508
12295
  status: 404,
@@ -11519,8 +12306,54 @@ async function handleMcpRequest(request, tools, call, serverName, version, allow
11519
12306
  body: "Method Not Allowed"
11520
12307
  };
11521
12308
  }
11522
- const outcome = await handleJsonRpc(request.body, tools, call, serverName, version);
11523
- return {
12309
+ if (!checkContentType(request.headers["content-type"])) {
12310
+ return {
12311
+ status: 415,
12312
+ reason: "Unsupported Media Type",
12313
+ headers: { "content-type": "text/plain" },
12314
+ body: "Content-Type must be application/json"
12315
+ };
12316
+ }
12317
+ if (!checkAcceptHeader(request.headers.accept)) {
12318
+ return {
12319
+ status: 406,
12320
+ reason: "Not Acceptable",
12321
+ headers: { "content-type": "text/plain" },
12322
+ body: "Accept must include application/json or text/event-stream"
12323
+ };
12324
+ }
12325
+ const originCheck = checkOrigin(request.headers.origin, serverOrigins, allowedOrigins);
12326
+ if (!originCheck.ok) {
12327
+ return {
12328
+ status: 403,
12329
+ reason: "Forbidden",
12330
+ headers: { "content-type": "text/plain" },
12331
+ body: originCheck.reason ?? "Forbidden"
12332
+ };
12333
+ }
12334
+ const hostCheck = checkHost(request.headers.host, allowedHosts);
12335
+ if (!hostCheck.ok) {
12336
+ return {
12337
+ status: 403,
12338
+ reason: "Forbidden",
12339
+ headers: { "content-type": "text/plain" },
12340
+ body: hostCheck.reason ?? "Forbidden"
12341
+ };
12342
+ }
12343
+ if (resolvedBearer) {
12344
+ const authHeader = request.headers.authorization;
12345
+ const provided = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : "";
12346
+ if (!provided || !constantTimeEqual(provided, resolvedBearer)) {
12347
+ return {
12348
+ status: 401,
12349
+ reason: "Unauthorized",
12350
+ headers: { "content-type": "text/plain" },
12351
+ body: "Unauthorized"
12352
+ };
12353
+ }
12354
+ }
12355
+ const outcome = await handleJsonRpc(request.body, tools, call, serverName, version);
12356
+ return {
11524
12357
  status: outcome.status,
11525
12358
  reason: outcome.status === 200 ? "OK" : "Accepted",
11526
12359
  headers: { "content-type": outcome.contentType },
@@ -11844,8 +12677,16 @@ var PiFileTokenSource = class {
11844
12677
  };
11845
12678
 
11846
12679
  // src/cli/auth-verbs.ts
11847
- function createTokenSource(platform2) {
11848
- return new PiFileTokenSource(platform2);
12680
+ var resolvedSource;
12681
+ async function createTokenSource(platform2) {
12682
+ if (isKernel(platform2)) return new PiFileTokenSource(platform2);
12683
+ if (resolvedSource) return resolvedSource;
12684
+ if (await jerkAvailable(platform2)) {
12685
+ resolvedSource = new JerkTokenSource(platform2);
12686
+ } else {
12687
+ resolvedSource = new PiFileTokenSource(platform2);
12688
+ }
12689
+ return resolvedSource;
11849
12690
  }
11850
12691
  function resolveServerName(target) {
11851
12692
  try {
@@ -11875,6 +12716,79 @@ async function resolveLoginTarget(platform2, target) {
11875
12716
  }
11876
12717
  throw new Error(`unknown target '${target}': not a URL and not found in mcp.json`);
11877
12718
  }
12719
+ function jerkMcpId(name) {
12720
+ if (name.startsWith("mcp:")) return name;
12721
+ if (name.startsWith("url:")) return `mcp:${name}`;
12722
+ return `mcp:${name}`;
12723
+ }
12724
+ async function readStream2(stream) {
12725
+ const reader = stream.getReader();
12726
+ const chunks = [];
12727
+ for (; ; ) {
12728
+ const { done, value } = await reader.read();
12729
+ if (done) break;
12730
+ chunks.push(value);
12731
+ }
12732
+ const total = chunks.reduce((s, c) => s + c.length, 0);
12733
+ const buf = new Uint8Array(total);
12734
+ let off = 0;
12735
+ for (const c of chunks) {
12736
+ buf.set(c, off);
12737
+ off += c.length;
12738
+ }
12739
+ return new TextDecoder().decode(buf);
12740
+ }
12741
+ async function jerkLogin(platform2, target, args) {
12742
+ const resolved = await resolveLoginTarget(platform2, target);
12743
+ const id = jerkMcpId(resolved.name);
12744
+ const argv = ["jerk", id, "create"];
12745
+ const scopeIdx = args.indexOf("--scope");
12746
+ if (scopeIdx !== -1 && args[scopeIdx + 1]) {
12747
+ argv.push("--scope", args[scopeIdx + 1]);
12748
+ }
12749
+ if (args.includes("--no-browser")) {
12750
+ argv.push("--no-browser");
12751
+ }
12752
+ const proc = await platform2.spawn({ argv });
12753
+ proc.stdin.close().catch(() => {
12754
+ });
12755
+ const [, errText, result] = await Promise.all([
12756
+ readStream2(proc.stdout),
12757
+ readStream2(proc.stderr),
12758
+ proc.wait()
12759
+ ]);
12760
+ if (result.status === 0) {
12761
+ await platform2.write(1, `Signed in to ${target}
12762
+ `);
12763
+ return 0;
12764
+ }
12765
+ if (errText.trim()) {
12766
+ await platform2.write(2, errText);
12767
+ }
12768
+ if (result.status === JERK_EXIT_SIGN_IN) return 4;
12769
+ return result.status;
12770
+ }
12771
+ async function jerkLogout(platform2, target) {
12772
+ const resolved = await resolveLoginTarget(platform2, target);
12773
+ const id = jerkMcpId(resolved.name);
12774
+ const argv = ["jerk", id, "drop", "--yes"];
12775
+ const proc = await platform2.spawn({ argv });
12776
+ proc.stdin.close().catch(() => {
12777
+ });
12778
+ const [, , result] = await Promise.all([
12779
+ readStream2(proc.stdout),
12780
+ readStream2(proc.stderr),
12781
+ proc.wait()
12782
+ ]);
12783
+ if (result.status === 0) {
12784
+ await platform2.write(1, `Signed out of ${target}
12785
+ `);
12786
+ } else {
12787
+ await platform2.write(1, `No credentials stored for ${target}
12788
+ `);
12789
+ }
12790
+ return 0;
12791
+ }
11878
12792
  async function runLogin(platform2, args, source) {
11879
12793
  if (isKernel(platform2)) {
11880
12794
  await platform2.write(
@@ -11883,13 +12797,22 @@ async function runLogin(platform2, args, source) {
11883
12797
  );
11884
12798
  return 4;
11885
12799
  }
11886
- const noBrowser = args.includes("--no-browser");
11887
12800
  const positional = args.filter((a) => !a.startsWith("-"));
11888
12801
  if (positional.length === 0) {
11889
12802
  await platform2.write(2, "mcpcat: login requires a target\n");
11890
12803
  return 2;
11891
12804
  }
11892
12805
  const target = positional[0];
12806
+ if (source instanceof JerkTokenSource) {
12807
+ try {
12808
+ return await jerkLogin(platform2, target, args);
12809
+ } catch (err) {
12810
+ await platform2.write(2, `mcpcat: login failed: ${err.message}
12811
+ `);
12812
+ return 4;
12813
+ }
12814
+ }
12815
+ const noBrowser = args.includes("--no-browser");
11893
12816
  let resolved;
11894
12817
  try {
11895
12818
  resolved = await resolveLoginTarget(platform2, target);
@@ -11971,6 +12894,15 @@ async function runLogout(platform2, args, source) {
11971
12894
  return 2;
11972
12895
  }
11973
12896
  const target = positional[0];
12897
+ if (source instanceof JerkTokenSource) {
12898
+ try {
12899
+ return await jerkLogout(platform2, target);
12900
+ } catch (err) {
12901
+ await platform2.write(2, `mcpcat: logout failed: ${err.message}
12902
+ `);
12903
+ return 1;
12904
+ }
12905
+ }
11974
12906
  let resolved;
11975
12907
  try {
11976
12908
  resolved = await resolveLoginTarget(platform2, target);
@@ -12343,6 +13275,49 @@ function checkAgentWarnings(name, config) {
12343
13275
  return warnings;
12344
13276
  }
12345
13277
 
13278
+ // src/serve/seal.ts
13279
+ async function sealSecret(platform2, name, value, options) {
13280
+ if (!platform2.seal) {
13281
+ throw Object.assign(new Error("vault device not available"), { sealErrno: "ENOTCONN" });
13282
+ }
13283
+ const request = JSON.stringify({
13284
+ v: 1,
13285
+ op: "seal",
13286
+ kind: "mcp",
13287
+ name,
13288
+ value,
13289
+ server: options.server,
13290
+ url: options.url
13291
+ });
13292
+ let responseText;
13293
+ try {
13294
+ responseText = await platform2.seal(request);
13295
+ } catch (err) {
13296
+ const code = err.code;
13297
+ if (code === "ENOENT" || code === "ENOTCONN") {
13298
+ throw Object.assign(new Error("vault device not available"), { sealErrno: "ENOTCONN" });
13299
+ }
13300
+ throw Object.assign(new Error(`vault: ${err.message}`), {
13301
+ sealErrno: code ?? "EACCES"
13302
+ });
13303
+ }
13304
+ const response = JSON.parse(responseText);
13305
+ if (response.error) {
13306
+ throw Object.assign(new Error(response.error), { sealErrno: response.errno ?? "EACCES" });
13307
+ }
13308
+ return { placeholder: response.placeholder };
13309
+ }
13310
+ function sealExitCode(errno) {
13311
+ if (errno === "EEXIST") return 2;
13312
+ return 4;
13313
+ }
13314
+ function sealErrorMessage(errno, name) {
13315
+ if (errno === "EEXIST") return `a non-bridge secret named ${name} already exists`;
13316
+ if (errno === "ENOTCONN") return "no vault available; use --bearer '${VAR}' or --no-auth";
13317
+ if (errno === "EACCES") return "vault locked; use --bearer '${VAR}' or --no-auth";
13318
+ return `vault error: ${errno}`;
13319
+ }
13320
+
12346
13321
  // src/cli/config-verbs.ts
12347
13322
  function parseHeader(hdr) {
12348
13323
  const colonIdx = hdr.indexOf(":");
@@ -12364,6 +13339,10 @@ function parseAddFlagAt(flagArgs, i, flags) {
12364
13339
  flags.noProbe = true;
12365
13340
  return { next: i + 1 };
12366
13341
  }
13342
+ if (arg === "--no-auth") {
13343
+ flags.noAuth = true;
13344
+ return { next: i + 1 };
13345
+ }
12367
13346
  if (arg === "--bridge") {
12368
13347
  flags.bridge = true;
12369
13348
  return { next: i + 1 };
@@ -12409,6 +13388,7 @@ function parseAddFlags(args) {
12409
13388
  local: false,
12410
13389
  noProbe: false,
12411
13390
  bridge: false,
13391
+ noAuth: false,
12412
13392
  headers: [],
12413
13393
  envPairs: [],
12414
13394
  positional: [],
@@ -12771,6 +13751,11 @@ async function runImport(platform2, args) {
12771
13751
  }
12772
13752
  return 0;
12773
13753
  }
13754
+ function generateBridgeToken() {
13755
+ const bytes = new Uint8Array(32);
13756
+ crypto.getRandomValues(bytes);
13757
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
13758
+ }
12774
13759
  async function runBridge(platform2, args, version) {
12775
13760
  if (platform2.kind === "host") {
12776
13761
  await platform2.write(
@@ -12797,16 +13782,56 @@ async function runBridge(platform2, args, version) {
12797
13782
  if (flags.bearer) {
12798
13783
  await platform2.write(
12799
13784
  2,
12800
- "mcpcat add --bridge: --bearer is not needed on the kernel loopback.\nThe kernel loopback is only reachable from inside the kernel and the agent.\n"
13785
+ "mcpcat add --bridge: --bearer is not supported for bridge; authentication is automatic via the vault\n"
12801
13786
  );
12802
13787
  return 2;
12803
13788
  }
12804
13789
  const serveOptions = parseServeFlags(["--proxy", "__bridge__", "--http", "0", "--json"]);
12805
13790
  serveOptions.proxyCommand = flags.cmdArgs;
12806
13791
  const path = flags.configFile ?? (flags.local ? projectConfigPath(platform2) : userConfigPath(platform2));
13792
+ if (flags.noAuth) {
13793
+ await platform2.write(
13794
+ 2,
13795
+ "mcpcat add --bridge: warning: running without authentication on loopback\n"
13796
+ );
13797
+ serveOptions.noAuth = true;
13798
+ serveOptions.onListening = async (actualPort) => {
13799
+ const bridgeUrl = `http://${actualPort}.kernel.localhost/mcp`;
13800
+ const httpConfig = { url: bridgeUrl };
13801
+ if (flags.description) httpConfig.description = flags.description;
13802
+ if (flags.exposure) httpConfig.exposure = flags.exposure;
13803
+ const { replaced } = await addServer(platform2.fs, path, name, httpConfig);
13804
+ await platform2.write(
13805
+ 1,
13806
+ `${replaced ? "replaced" : "added"} ${name} in ${path}
13807
+ bridge does not survive a reload; runs in the foreground (start with &)
13808
+ `
13809
+ );
13810
+ };
13811
+ return runServe(platform2, serveOptions, version);
13812
+ }
13813
+ const token = generateBridgeToken();
13814
+ const secretName = `MCP_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_TOKEN`;
13815
+ serveOptions._resolvedBearer = token;
12807
13816
  serveOptions.onListening = async (actualPort) => {
13817
+ if (actualPort === 9222) {
13818
+ await platform2.write(2, "mcpcat add --bridge: port 9222 is reserved; please retry\n");
13819
+ return;
13820
+ }
12808
13821
  const bridgeUrl = `http://${actualPort}.kernel.localhost/mcp`;
12809
- const httpConfig = { url: bridgeUrl };
13822
+ try {
13823
+ await sealSecret(platform2, secretName, token, { server: name, url: bridgeUrl });
13824
+ } catch (err) {
13825
+ const errno = err.sealErrno ?? "EACCES";
13826
+ const code = sealExitCode(errno);
13827
+ await platform2.write(2, `mcpcat add --bridge: ${sealErrorMessage(errno, secretName)}
13828
+ `);
13829
+ platform2.exit(code);
13830
+ }
13831
+ const httpConfig = {
13832
+ url: bridgeUrl,
13833
+ headers: { Authorization: "Bearer ${" + secretName + "}" }
13834
+ };
12810
13835
  if (flags.description) httpConfig.description = flags.description;
12811
13836
  if (flags.exposure) httpConfig.exposure = flags.exposure;
12812
13837
  const { replaced } = await addServer(platform2.fs, path, name, httpConfig);
@@ -12943,8 +13968,18 @@ var CONFIG_VERBS = {
12943
13968
  exposure: true,
12944
13969
  import: true
12945
13970
  };
12946
- var UNIMPLEMENTED_VERBS = /* @__PURE__ */ new Set(["resources", "read", "prompts", "prompt", "complete", "task"]);
12947
- var CLIENT_VERBS = /* @__PURE__ */ new Set(["info", "tools", "call", "describe", "serve"]);
13971
+ var UNIMPLEMENTED_VERBS = /* @__PURE__ */ new Set(["complete", "task"]);
13972
+ var CLIENT_VERBS = /* @__PURE__ */ new Set([
13973
+ "info",
13974
+ "tools",
13975
+ "call",
13976
+ "describe",
13977
+ "serve",
13978
+ "resources",
13979
+ "read",
13980
+ "prompts",
13981
+ "prompt"
13982
+ ]);
12948
13983
  function helpText(version) {
12949
13984
  return `mcpcat ${version} \u2013 a curl for the Model Context Protocol
12950
13985
 
@@ -12983,6 +14018,7 @@ Options:
12983
14018
  -H, --header 'N: V' Extra header
12984
14019
  -d <json> Tool arguments as JSON (or @file, @- for stdin)
12985
14020
  -m, --timeout <s> Request timeout (default 60)
14021
+ --memory <MiB> Code mode memory limit (default 256, max 1024)
12986
14022
  --protocol-version <v> Pin protocol version
12987
14023
  --schema Print raw JSON Schema (describe)
12988
14024
  --ts TypeScript declarations (describe)
@@ -12999,6 +14035,31 @@ Options:
12999
14035
  -h, --help Show this help
13000
14036
  `;
13001
14037
  }
14038
+ var VERBS_WITH_TOOL = /* @__PURE__ */ new Set(["call", "prompt", "describe", "read"]);
14039
+ var VERBS_WITH_TARGET = /* @__PURE__ */ new Set([
14040
+ "info",
14041
+ "tools",
14042
+ "call",
14043
+ "describe",
14044
+ "serve",
14045
+ "grep",
14046
+ "code",
14047
+ "discover",
14048
+ "login",
14049
+ "logout",
14050
+ "resources",
14051
+ "read",
14052
+ "prompts",
14053
+ "prompt"
14054
+ ]);
14055
+ function resolveMaxPositional(positional) {
14056
+ if (positional.length === 0) return 3;
14057
+ const first = positional[0];
14058
+ if (!VERBS.includes(first)) return 2;
14059
+ if (VERBS_WITH_TOOL.has(first)) return 3;
14060
+ if (VERBS_WITH_TARGET.has(first)) return 2;
14061
+ return 2;
14062
+ }
13002
14063
  function parseFlags(args) {
13003
14064
  const headers = {};
13004
14065
  let dataArg;
@@ -13013,10 +14074,16 @@ function parseFlags(args) {
13013
14074
  const serverTargets = [];
13014
14075
  let allowShell = false;
13015
14076
  let timeoutMs;
14077
+ let memoryLimitMiB;
13016
14078
  let verbose = false;
13017
14079
  let silent = false;
13018
14080
  const positional = [];
13019
14081
  let trailingCommand;
14082
+ let respondArg;
14083
+ const roots = [];
14084
+ let outputPath;
14085
+ let noValidate = false;
14086
+ const rest = [];
13020
14087
  for (let i = 0; i < args.length; i++) {
13021
14088
  const arg = args[i];
13022
14089
  if (arg === "--") {
@@ -13057,8 +14124,41 @@ function parseFlags(args) {
13057
14124
  silent = true;
13058
14125
  } else if ((arg === "-m" || arg === "--timeout") && args[i + 1]) {
13059
14126
  timeoutMs = Number(args[++i]) * 1e3;
14127
+ } else if (arg === "--respond" && args[i + 1]) {
14128
+ respondArg = args[++i];
14129
+ } else if (arg === "--root" && args[i + 1]) {
14130
+ roots.push(args[++i]);
14131
+ } else if ((arg === "-o" || arg === "--output") && args[i + 1]) {
14132
+ outputPath = args[++i];
14133
+ } else if (arg === "--memory" && args[i + 1]) {
14134
+ memoryLimitMiB = Number(args[++i]);
14135
+ } else if (arg === "--no-validate") {
14136
+ noValidate = true;
13060
14137
  } else if (!arg.startsWith("-")) {
13061
14138
  positional.push(arg);
14139
+ const maxPositional = resolveMaxPositional(positional);
14140
+ if (positional.length >= maxPositional) {
14141
+ rest.push(...args.slice(i + 1));
14142
+ break;
14143
+ }
14144
+ }
14145
+ }
14146
+ const toolArgs = [];
14147
+ for (let j = 0; j < rest.length; j++) {
14148
+ const ra = rest[j];
14149
+ if (ra === "-d" && rest[j + 1] !== void 0) {
14150
+ dataArg = rest[++j];
14151
+ } else if (ra === "--json") {
14152
+ jsonOutput = true;
14153
+ } else if (ra === "--no-validate") {
14154
+ noValidate = true;
14155
+ } else if ((ra === "-o" || ra === "--output") && rest[j + 1] !== void 0) {
14156
+ outputPath = rest[++j];
14157
+ } else if (ra === "--help" || ra === "-h") {
14158
+ toolArgs.length = 0;
14159
+ break;
14160
+ } else {
14161
+ toolArgs.push(ra);
13062
14162
  }
13063
14163
  }
13064
14164
  return {
@@ -13075,10 +14175,16 @@ function parseFlags(args) {
13075
14175
  serverTargets,
13076
14176
  allowShell,
13077
14177
  timeoutMs,
14178
+ memoryLimitMiB,
13078
14179
  verbose,
13079
14180
  silent,
13080
14181
  positional,
13081
- trailingCommand
14182
+ trailingCommand,
14183
+ respondArg,
14184
+ roots,
14185
+ outputPath,
14186
+ noValidate,
14187
+ rest: toolArgs
13082
14188
  };
13083
14189
  }
13084
14190
  function resolveVerb(positional, trailingCommand) {
@@ -13113,6 +14219,7 @@ function parseArgs(args) {
13113
14219
  const resolved = resolveVerb(flags.positional, flags.trailingCommand);
13114
14220
  return {
13115
14221
  ...resolved,
14222
+ toolArgs: flags.rest,
13116
14223
  headers: flags.headers,
13117
14224
  dataArg: flags.dataArg,
13118
14225
  protocolVersion: flags.protocolVersion,
@@ -13126,8 +14233,13 @@ function parseArgs(args) {
13126
14233
  serverTargets: flags.serverTargets,
13127
14234
  allowShell: flags.allowShell,
13128
14235
  timeoutMs: flags.timeoutMs,
14236
+ memoryLimitMiB: flags.memoryLimitMiB,
13129
14237
  verbose: flags.verbose,
13130
- silent: flags.silent
14238
+ silent: flags.silent,
14239
+ noValidate: flags.noValidate,
14240
+ respondArg: flags.respondArg,
14241
+ roots: flags.roots,
14242
+ outputPath: flags.outputPath
13131
14243
  };
13132
14244
  }
13133
14245
  function formatToolLine(tool) {
@@ -13250,7 +14362,8 @@ async function runCall(platform2, conn, toolName, dataArg, toolArgs, opts) {
13250
14362
  const tools = await conn.listTools();
13251
14363
  const tool = tools.find((t) => t.name === toolName);
13252
14364
  const schema = tool?.inputSchema ?? { type: "object", properties: {} };
13253
- const c = coerceArgsBySchema(toolArgs, schema, { noValidate: opts.noValidate });
14365
+ const skipRequired = opts.noValidate || !!dataArg;
14366
+ const c = coerceArgsBySchema(toolArgs, schema, { noValidate: skipRequired });
13254
14367
  if (!c.ok) {
13255
14368
  await writeErr(platform2, opts.json ?? false, "usage", c.error);
13256
14369
  return EXIT_USAGE;
@@ -13259,8 +14372,11 @@ async function runCall(platform2, conn, toolName, dataArg, toolArgs, opts) {
13259
14372
  }
13260
14373
  return renderCallResult(
13261
14374
  platform2,
13262
- await conn.callTool(toolName, fin, { onProgress: opts.onProgress }),
13263
- { json: opts.json }
14375
+ await conn.callTool(toolName, fin, {
14376
+ onProgress: opts.onProgress,
14377
+ inputResponses: opts.inputResponses
14378
+ }),
14379
+ { json: opts.json, outputPath: opts.outputPath }
13264
14380
  );
13265
14381
  }
13266
14382
  async function runDescribe(platform2, conn, toolName, showSchema, json) {
@@ -13297,19 +14413,168 @@ async function runDescribe(platform2, conn, toolName, showSchema, json) {
13297
14413
  }
13298
14414
  return EXIT_OK;
13299
14415
  }
13300
- async function runVerb(platform2, parsed, conn, onProgress) {
14416
+ async function runResources(platform2, conn, json) {
14417
+ const resources = await conn.listResources();
14418
+ const templates = await conn.listResourceTemplates();
14419
+ if (json) {
14420
+ await renderResourcesJson(platform2, resources, templates);
14421
+ return EXIT_OK;
14422
+ }
14423
+ if (resources.length === 0 && templates.length === 0) {
14424
+ await platform2.write(1, "(no resources)\n");
14425
+ return EXIT_OK;
14426
+ }
14427
+ for (const r of resources) {
14428
+ const desc = r.description ? ` ${r.description}` : "";
14429
+ const mime = r.mimeType ? ` [${r.mimeType}]` : "";
14430
+ await platform2.write(1, `${r.uri} ${r.name}${mime}${desc}
14431
+ `);
14432
+ }
14433
+ if (templates.length > 0) {
14434
+ if (resources.length > 0) await platform2.write(1, "\n");
14435
+ await platform2.write(1, "Templates:\n");
14436
+ for (const t of templates) {
14437
+ const desc = t.description ? ` ${t.description}` : "";
14438
+ await platform2.write(1, ` ${t.uriTemplate} ${t.name}${desc}
14439
+ `);
14440
+ }
14441
+ }
14442
+ return EXIT_OK;
14443
+ }
14444
+ async function runRead(platform2, conn, uri, json, outputPath) {
14445
+ const result = await conn.readResource(uri);
14446
+ if (json) {
14447
+ await renderReadResourceJson(platform2, result);
14448
+ return EXIT_OK;
14449
+ }
14450
+ for (const content of result.contents) {
14451
+ if ("text" in content) {
14452
+ if (outputPath) {
14453
+ await platform2.fs.writeFile(outputPath, content.text);
14454
+ await platform2.write(1, `${outputPath}
14455
+ `);
14456
+ } else {
14457
+ await platform2.write(1, `${content.text}
14458
+ `);
14459
+ }
14460
+ } else if ("blob" in content) {
14461
+ if (outputPath) {
14462
+ const bytes = Uint8Array.from(atob(content.blob), (c) => c.charCodeAt(0));
14463
+ await platform2.fs.writeFile(outputPath, bytes);
14464
+ await platform2.write(1, `${outputPath}
14465
+ `);
14466
+ } else {
14467
+ await platform2.write(1, `${content.blob}
14468
+ `);
14469
+ }
14470
+ }
14471
+ }
14472
+ return EXIT_OK;
14473
+ }
14474
+ async function runPrompts(platform2, conn, json) {
14475
+ const prompts = await conn.listPrompts();
14476
+ if (json) {
14477
+ await renderPromptsJson(platform2, prompts);
14478
+ return EXIT_OK;
14479
+ }
14480
+ if (prompts.length === 0) {
14481
+ await platform2.write(1, "(no prompts)\n");
14482
+ return EXIT_OK;
14483
+ }
14484
+ for (const p of prompts) {
14485
+ const desc = p.description ? ` ${p.description}` : "";
14486
+ await platform2.write(1, `${p.name}${desc}
14487
+ `);
14488
+ }
14489
+ return EXIT_OK;
14490
+ }
14491
+ async function runPrompt(platform2, conn, name, dataArg, toolArgs, json) {
14492
+ let args = {};
14493
+ if (dataArg) {
14494
+ args = await readDataArg(platform2, dataArg);
14495
+ }
14496
+ const prompts = await conn.listPrompts();
14497
+ const prompt = prompts.find((p) => p.name === name);
14498
+ const schema = {
14499
+ type: "object",
14500
+ properties: {},
14501
+ required: []
14502
+ };
14503
+ if (prompt?.arguments) {
14504
+ const props = {};
14505
+ const req = [];
14506
+ for (const a of prompt.arguments) {
14507
+ props[a.name] = { type: "string", description: a.description };
14508
+ if (a.required) req.push(a.name);
14509
+ }
14510
+ schema.properties = props;
14511
+ schema.required = req;
14512
+ }
14513
+ if (toolArgs.length > 0) {
14514
+ const skipRequired = !!dataArg;
14515
+ const c = coerceArgsBySchema(toolArgs, schema, { noValidate: skipRequired });
14516
+ if (!c.ok) {
14517
+ await writeErr(platform2, json, "usage", c.error);
14518
+ return EXIT_USAGE;
14519
+ }
14520
+ args = mergeArgs(args, c.value);
14521
+ } else if (!dataArg) {
14522
+ const required = Array.isArray(schema.required) ? schema.required : [];
14523
+ for (const r of required) {
14524
+ if (!Object.hasOwn(args, r)) {
14525
+ await writeErr(platform2, json, "usage", `missing required flag --${r}`);
14526
+ return EXIT_USAGE;
14527
+ }
14528
+ }
14529
+ }
14530
+ const result = await conn.getPrompt(name, args);
14531
+ if (json) {
14532
+ await renderPromptResultJson(platform2, result);
14533
+ return EXIT_OK;
14534
+ }
14535
+ if (result.description) {
14536
+ await platform2.write(1, `${result.description}
14537
+
14538
+ `);
14539
+ }
14540
+ for (const msg of result.messages) {
14541
+ const text = msg.content?.text ?? (msg.content?.data ? `[${msg.content.mimeType ?? "binary"}]` : "");
14542
+ await platform2.write(1, `[${msg.role}] ${text}
14543
+ `);
14544
+ }
14545
+ return EXIT_OK;
14546
+ }
14547
+ async function runVerb(platform2, parsed, conn, onProgress, respondData) {
13301
14548
  switch (parsed.verb) {
13302
14549
  case "info":
13303
14550
  return runInfo(platform2, conn, parsed.jsonOutput);
13304
14551
  case "tools":
13305
14552
  return runTools(platform2, conn, parsed.jsonOutput);
13306
14553
  case "call":
13307
- return runCall(platform2, conn, parsed.toolName, parsed.dataArg, [], {
14554
+ return runCall(platform2, conn, parsed.toolName, parsed.dataArg, parsed.toolArgs, {
13308
14555
  json: parsed.jsonOutput,
13309
- onProgress
14556
+ noValidate: parsed.noValidate,
14557
+ outputPath: parsed.outputPath,
14558
+ onProgress,
14559
+ inputResponses: respondData
13310
14560
  });
13311
14561
  case "describe":
13312
14562
  return runDescribe(platform2, conn, parsed.toolName, parsed.showSchema, parsed.jsonOutput);
14563
+ case "resources":
14564
+ return runResources(platform2, conn, parsed.jsonOutput);
14565
+ case "read":
14566
+ return runRead(platform2, conn, parsed.toolName, parsed.jsonOutput, parsed.outputPath);
14567
+ case "prompts":
14568
+ return runPrompts(platform2, conn, parsed.jsonOutput);
14569
+ case "prompt":
14570
+ return runPrompt(
14571
+ platform2,
14572
+ conn,
14573
+ parsed.toolName,
14574
+ parsed.dataArg,
14575
+ parsed.toolArgs,
14576
+ parsed.jsonOutput
14577
+ );
13313
14578
  default:
13314
14579
  await platform2.write(2, `mcpcat: unknown command '${parsed.verb}'
13315
14580
  `);
@@ -13447,6 +14712,18 @@ async function resolveTargetNames(platform2, parsed) {
13447
14712
  return config.list().filter((e) => e.config.enabled !== false).map((e) => e.name);
13448
14713
  }
13449
14714
  async function dispatchMultiTarget(platform2, parsed, version) {
14715
+ let activeCleanup;
14716
+ async function killTreeAndExit(exitCode) {
14717
+ if (activeCleanup) {
14718
+ try {
14719
+ await Promise.race([activeCleanup(), new Promise((r) => setTimeout(r, 3e3))]);
14720
+ } catch {
14721
+ }
14722
+ }
14723
+ platform2.exit(exitCode);
14724
+ }
14725
+ platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143));
14726
+ platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129));
13450
14727
  const config = await MergedConfigSource.load(platform2);
13451
14728
  if (parsed.verb === "code") {
13452
14729
  let source;
@@ -13478,10 +14755,12 @@ async function dispatchMultiTarget(platform2, parsed, version) {
13478
14755
  parsed,
13479
14756
  version
13480
14757
  );
14758
+ activeCleanup = cleanup;
13481
14759
  try {
13482
14760
  return await runCode(targets, source, {
13483
14761
  json: parsed.jsonOutput,
13484
14762
  timeoutMs: parsed.timeoutMs,
14763
+ memoryLimitMiB: parsed.memoryLimitMiB,
13485
14764
  allowShell: parsed.allowShell,
13486
14765
  platform: platform2
13487
14766
  });
@@ -13507,6 +14786,7 @@ async function dispatchMultiTarget(platform2, parsed, version) {
13507
14786
  parsed,
13508
14787
  version
13509
14788
  );
14789
+ activeCleanup = cleanup;
13510
14790
  try {
13511
14791
  return await runGrep(platform2, pattern, targets, {
13512
14792
  rank: parsed.rank,
@@ -13525,6 +14805,7 @@ async function dispatchMultiTarget(platform2, parsed, version) {
13525
14805
  parsed,
13526
14806
  version
13527
14807
  );
14808
+ activeCleanup = cleanup;
13528
14809
  try {
13529
14810
  return await runDescribeTs(platform2, targets, parsed.tsBudget);
13530
14811
  } finally {
@@ -13534,6 +14815,18 @@ async function dispatchMultiTarget(platform2, parsed, version) {
13534
14815
  return 2;
13535
14816
  }
13536
14817
  async function run(platform2, version, args) {
14818
+ try {
14819
+ return await runInner(platform2, version, args);
14820
+ } catch (err) {
14821
+ if (err.code === "EACCES") {
14822
+ await platform2.write(2, `mcpcat: ${err.message}
14823
+ `);
14824
+ return EXIT_USAGE;
14825
+ }
14826
+ throw err;
14827
+ }
14828
+ }
14829
+ async function runInner(platform2, version, args) {
13537
14830
  if (args.includes("--version") || args.includes("-V")) {
13538
14831
  await platform2.write(1, `${version}
13539
14832
  `);
@@ -13560,11 +14853,11 @@ async function run(platform2, version, args) {
13560
14853
  const discResult = await dispatchDiscoveryVerb(platform2, parsed, args);
13561
14854
  if (discResult !== void 0) return discResult;
13562
14855
  if (parsed.verb === "login") {
13563
- const source = createTokenSource(platform2);
14856
+ const source = await createTokenSource(platform2);
13564
14857
  return runLogin(platform2, args.slice(1), source);
13565
14858
  }
13566
14859
  if (parsed.verb === "logout") {
13567
- const source = createTokenSource(platform2);
14860
+ const source = await createTokenSource(platform2);
13568
14861
  return runLogout(platform2, args.slice(1), source);
13569
14862
  }
13570
14863
  if (UNIMPLEMENTED_VERBS.has(parsed.verb)) {
@@ -13587,6 +14880,14 @@ async function run(platform2, version, args) {
13587
14880
  await platform2.write(2, "mcpcat: call requires a tool name\n");
13588
14881
  return 2;
13589
14882
  }
14883
+ if (parsed.verb === "read" && !parsed.toolName) {
14884
+ await platform2.write(2, "mcpcat: read requires a URI\n");
14885
+ return 2;
14886
+ }
14887
+ if (parsed.verb === "prompt" && !parsed.toolName) {
14888
+ await platform2.write(2, "mcpcat: prompt requires a name\n");
14889
+ return 2;
14890
+ }
13590
14891
  if (!parsed.target) {
13591
14892
  await platform2.write(2, `mcpcat: ${parsed.verb} requires a target
13592
14893
  `);
@@ -13597,13 +14898,19 @@ async function run(platform2, version, args) {
13597
14898
  function expandAndCollect(headers, env2) {
13598
14899
  const secrets = [];
13599
14900
  const result = {};
14901
+ const unset = [];
13600
14902
  for (const [k, v] of Object.entries(headers)) {
14903
+ const missing = missingPlaceholders(v, env2);
14904
+ if (missing.length > 0) {
14905
+ unset.push(...missing);
14906
+ continue;
14907
+ }
13601
14908
  const e = expandPlaceholders(v, env2);
13602
14909
  result[k] = e;
13603
14910
  if (e !== v) secrets.push(e);
13604
14911
  if (v.length > 0) secrets.push(v);
13605
14912
  }
13606
- return { expanded: result, secrets };
14913
+ return { expanded: result, secrets, unset };
13607
14914
  }
13608
14915
  function classifyError(err) {
13609
14916
  if (err instanceof InputRequiredError) {
@@ -13615,6 +14922,9 @@ function classifyError(err) {
13615
14922
  if (err instanceof McpAuthError) {
13616
14923
  return { code: EXIT_AUTH, kind: "auth", msg: err.message };
13617
14924
  }
14925
+ if (err instanceof JerkSignInRequiredError) {
14926
+ return { code: EXIT_AUTH, kind: "auth", msg: err.message };
14927
+ }
13618
14928
  if (err instanceof AuthStepUpError) {
13619
14929
  const scopeHint = err.scope ? ` --scope ${err.scope}` : "";
13620
14930
  return {
@@ -13636,25 +14946,112 @@ function classifyError(err) {
13636
14946
  }
13637
14947
  return { code: EXIT_RPC_ERROR, kind: "rpc_error", msg };
13638
14948
  }
13639
- function resolveAuthProvider(platform2, hasAuthHeader, url, target, config) {
14949
+ async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config) {
13640
14950
  if (hasAuthHeader || !url) return void 0;
13641
- const source = createTokenSource(platform2);
14951
+ const source = await createTokenSource(platform2);
13642
14952
  const entry = config.entry(target);
13643
14953
  const oauth = entry && "url" in entry.config ? entry.config.oauth : void 0;
13644
14954
  const name = entry ? target : `url:${new URL(url).origin}${new URL(url).pathname}`;
13645
14955
  return createMcpAuthProvider({ name, serverUrl: url, source, oauth, platform: platform2 });
13646
14956
  }
14957
+ function classifyConnectionError(err, target, interrupted) {
14958
+ if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError) {
14959
+ return classifyError(err);
14960
+ }
14961
+ if (err.name === "McpOAuthAuthorizationRequiredError" || err.name === "McpAuthRequiredError") {
14962
+ return { code: EXIT_AUTH, kind: "auth", msg: `unauthorized. Run: mcpcat login ${target}` };
14963
+ }
14964
+ if (interrupted) {
14965
+ return { code: EXIT_INTERRUPTED, kind: "interrupted", msg: "interrupted" };
14966
+ }
14967
+ const msg = transportHint(err) ?? `connection failed: ${err.message}`;
14968
+ return { code: EXIT_CONNECTION, kind: "connection", msg };
14969
+ }
14970
+ function buildRoots(rootArgs, isStdio, cwd) {
14971
+ const roots = rootArgs.map((r) => ({
14972
+ uri: r.startsWith("file://") ? r : `file://${r}`
14973
+ }));
14974
+ if (roots.length === 0 && isStdio) {
14975
+ roots.push({ uri: `file://${cwd}` });
14976
+ }
14977
+ return roots.length > 0 ? roots : void 0;
14978
+ }
14979
+ async function loadRespondData(platform2, respondArg, json) {
14980
+ if (!respondArg) return { data: void 0 };
14981
+ try {
14982
+ return { data: await readDataArg(platform2, respondArg) };
14983
+ } catch (err) {
14984
+ await writeErr(
14985
+ platform2,
14986
+ json,
14987
+ "usage",
14988
+ `invalid --respond argument: ${err.message}`
14989
+ );
14990
+ return { exitCode: EXIT_USAGE };
14991
+ }
14992
+ }
14993
+ async function handleInputRequired(platform2, conn, err, respondData, json) {
14994
+ const parsed2 = JSON.parse(err.requestJson);
14995
+ const inputReq = parseInputRequest(parsed2.result ?? parsed2);
14996
+ if (!inputReq) {
14997
+ await platform2.write(2, `${err.requestJson}
14998
+ `);
14999
+ return EXIT_INPUT_REQUIRED;
15000
+ }
15001
+ const isTty = platform2.isatty(0);
15002
+ const elicitResult = await handleElicitation(platform2, inputReq, isTty, respondData);
15003
+ if ("exitCode" in elicitResult) {
15004
+ await platform2.write(2, `${err.requestJson}
15005
+ `);
15006
+ return EXIT_INPUT_REQUIRED;
15007
+ }
15008
+ try {
15009
+ const retryResult = await conn.callTool(parsed2.name, {
15010
+ ...parsed2.arguments,
15011
+ _meta: { inputResponses: elicitResult.answers }
15012
+ });
15013
+ return renderCallResult(platform2, retryResult, { json });
15014
+ } catch (retryErr) {
15015
+ const e2 = classifyError(retryErr);
15016
+ await writeErr(platform2, json, e2.kind, e2.msg);
15017
+ return e2.code;
15018
+ }
15019
+ }
13647
15020
  async function runClientVerb(platform2, version, parsed) {
13648
- const { expanded, secrets } = expandAndCollect(parsed.headers, platform2.env);
15021
+ const { expanded, secrets, unset } = expandAndCollect(parsed.headers, platform2.env);
15022
+ if (unset.length > 0) {
15023
+ await platform2.write(
15024
+ 2,
15025
+ `mcpcat: unset variable${unset.length > 1 ? "s" : ""} in -H: ${unset.join(", ")}
15026
+ `
15027
+ );
15028
+ return EXIT_USAGE;
15029
+ }
13649
15030
  const verbose = createVerboseLogger(platform2, parsed.verbose);
13650
15031
  for (const s of secrets) verbose.secrets.add(s);
13651
15032
  const timeoutMs = parsed.timeoutMs;
13652
15033
  const sigintController = new AbortController();
13653
15034
  let interrupted = false;
15035
+ let termExitCode = EXIT_INTERRUPTED;
15036
+ let activeConn;
15037
+ async function killTreeAndExit(exitCode) {
15038
+ interrupted = true;
15039
+ termExitCode = exitCode;
15040
+ sigintController.abort();
15041
+ if (activeConn) {
15042
+ try {
15043
+ await Promise.race([activeConn.close(), new Promise((r) => setTimeout(r, 3e3))]);
15044
+ } catch {
15045
+ }
15046
+ }
15047
+ platform2.exit(exitCode);
15048
+ }
13654
15049
  platform2.onSignal?.("SIGINT", () => {
13655
15050
  interrupted = true;
13656
15051
  sigintController.abort();
13657
15052
  });
15053
+ platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143));
15054
+ platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129));
13658
15055
  const showProgress = !parsed.silent && (parsed.verbose || platform2.isatty(2));
13659
15056
  const onProgress = (progress, total, message) => {
13660
15057
  if (!showProgress) return;
@@ -13673,59 +15070,91 @@ async function runClientVerb(platform2, version, parsed) {
13673
15070
  await writeErr(platform2, parsed.jsonOutput, "usage", err.message);
13674
15071
  return EXIT_USAGE;
13675
15072
  }
13676
- const hasAuthHeader = "Authorization" in expanded;
13677
- const authProvider = resolveAuthProvider(platform2, hasAuthHeader, resolved.url, target, config);
15073
+ const configHeaders = expandHeaders(resolved.headers, platform2.env, (msg) => {
15074
+ platform2.write(2, `mcpcat: warning: ${msg}
15075
+ `).catch(() => {
15076
+ });
15077
+ });
15078
+ const configSecrets = collectConfigSecrets(resolved.headers, platform2.env);
15079
+ for (const s of configSecrets) verbose.secrets.add(s);
15080
+ const allSecrets = /* @__PURE__ */ new Set([...secrets, ...configSecrets]);
15081
+ const redact2 = (text) => {
15082
+ let result = text;
15083
+ for (const secret of allSecrets) {
15084
+ if (secret.length > 0) {
15085
+ while (result.includes(secret)) {
15086
+ result = result.replace(secret, "[REDACTED]");
15087
+ }
15088
+ }
15089
+ }
15090
+ return result;
15091
+ };
15092
+ const mergedHeaders = { ...configHeaders, ...expanded };
15093
+ const hasAuthHeader = "Authorization" in mergedHeaders;
15094
+ const authProvider = await resolveAuthProvider(
15095
+ platform2,
15096
+ hasAuthHeader,
15097
+ resolved.url,
15098
+ target,
15099
+ config
15100
+ );
15101
+ const roots = buildRoots(parsed.roots, !!resolved.command, platform2.cwd);
15102
+ let respondData;
15103
+ if (parsed.respondArg) {
15104
+ const loaded = await loadRespondData(platform2, parsed.respondArg, parsed.jsonOutput);
15105
+ if ("exitCode" in loaded) return loaded.exitCode;
15106
+ respondData = loaded.data;
15107
+ }
13678
15108
  let conn;
13679
15109
  try {
13680
15110
  conn = await connect(platform2, {
13681
15111
  ...resolved,
13682
- headers: { ...resolved.headers, ...expanded },
15112
+ headers: mergedHeaders,
13683
15113
  protocolVersion: parsed.protocolVersion,
13684
15114
  version,
13685
15115
  timeoutMs,
13686
15116
  signal: sigintController.signal,
13687
15117
  verbose,
13688
- authProvider
15118
+ authProvider,
15119
+ roots
13689
15120
  });
15121
+ activeConn = conn;
13690
15122
  } catch (err) {
13691
- if (err instanceof McpAuthError || err instanceof AuthStepUpError) {
13692
- const e = classifyError(err);
13693
- await writeErr(platform2, parsed.jsonOutput, e.kind, e.msg);
13694
- return EXIT_AUTH;
13695
- }
13696
- if (err.name === "McpOAuthAuthorizationRequiredError" || err.name === "McpAuthRequiredError") {
13697
- await writeErr(
15123
+ if (isSpawnNotFound(err)) {
15124
+ await writeErrRedacted(
13698
15125
  platform2,
13699
15126
  parsed.jsonOutput,
13700
- "auth",
13701
- `unauthorized. Run: mcpcat login ${target}`
15127
+ "connection",
15128
+ `command not found: ${resolved.command}`,
15129
+ redact2
13702
15130
  );
13703
- return EXIT_AUTH;
15131
+ return EXIT_CONNECTION;
13704
15132
  }
13705
- const msg = transportHint(err) ?? `connection failed: ${err.message}`;
13706
- if (interrupted) {
15133
+ const e = classifyConnectionError(err, target, interrupted);
15134
+ if (e.code === EXIT_INTERRUPTED) {
13707
15135
  await platform2.write(2, "mcpcat: interrupted\n");
13708
- return EXIT_INTERRUPTED;
15136
+ return termExitCode;
13709
15137
  }
13710
- await writeErr(platform2, parsed.jsonOutput, "connection", msg);
13711
- return EXIT_CONNECTION;
15138
+ await writeErrRedacted(platform2, parsed.jsonOutput, e.kind, e.msg, redact2);
15139
+ return e.code;
13712
15140
  }
13713
15141
  try {
13714
- const code = await runVerb(platform2, parsed, conn, onProgress);
15142
+ const code = await runVerb(platform2, parsed, conn, onProgress, respondData);
13715
15143
  if (interrupted) {
13716
15144
  await platform2.write(2, "mcpcat: interrupted\n");
13717
- return EXIT_INTERRUPTED;
15145
+ return termExitCode;
13718
15146
  }
13719
15147
  return code;
13720
15148
  } catch (err) {
13721
15149
  if (interrupted) {
13722
15150
  await platform2.write(2, "mcpcat: interrupted\n");
13723
- return EXIT_INTERRUPTED;
15151
+ return termExitCode;
15152
+ }
15153
+ if (err instanceof InputRequiredError) {
15154
+ return handleInputRequired(platform2, conn, err, respondData, parsed.jsonOutput);
13724
15155
  }
13725
15156
  const e = classifyError(err);
13726
- if (err instanceof InputRequiredError) await platform2.write(2, `${e.msg}
13727
- `);
13728
- else await writeErr(platform2, parsed.jsonOutput, e.kind, e.msg);
15157
+ await writeErrRedacted(platform2, parsed.jsonOutput, e.kind, e.msg, redact2);
13729
15158
  return e.code;
13730
15159
  } finally {
13731
15160
  try {
@@ -13739,6 +15168,28 @@ async function writeErr(p, json, kind, msg) {
13739
15168
  else await p.write(2, `mcpcat: ${msg}
13740
15169
  `);
13741
15170
  }
15171
+ async function writeErrRedacted(p, json, kind, msg, redact2) {
15172
+ if (json) await renderErrorJson(p, kind, msg, void 0, void 0, redact2);
15173
+ else await p.write(2, `mcpcat: ${redact2(msg)}
15174
+ `);
15175
+ }
15176
+ function collectConfigSecrets(headers, env2) {
15177
+ if (!headers) return [];
15178
+ const secrets = [];
15179
+ for (const [, v] of Object.entries(headers)) {
15180
+ const expanded = expandPlaceholders(v, env2);
15181
+ if (expanded !== v && expanded.length > 0) secrets.push(expanded);
15182
+ if (v.length > 0 && v !== expanded) secrets.push(v);
15183
+ }
15184
+ return secrets;
15185
+ }
15186
+ function isSpawnNotFound(err) {
15187
+ if (!(err instanceof Error)) return false;
15188
+ const code = err.code;
15189
+ if (code === "ENOENT" || code === "EACCES") return true;
15190
+ const msg = err.message;
15191
+ return msg.includes("ENOENT") || msg.includes("EACCES");
15192
+ }
13742
15193
 
13743
15194
  // src/platform/kernel.ts
13744
15195
  function kernelFs(ctx) {
@@ -13750,15 +15201,20 @@ function kernelFs(ctx) {
13750
15201
  stat: (path) => ctx.fs.stat(path),
13751
15202
  exists: (path) => ctx.fs.exists(path),
13752
15203
  async mkdirExclusive(path) {
13753
- const exists = await ctx.fs.exists(path);
13754
- if (exists) {
13755
- const err = new Error(`EEXIST: directory already exists, mkdir '${path}'`);
13756
- err.code = "EEXIST";
15204
+ try {
15205
+ await ctx.fs.mkdir(path);
15206
+ } catch (err) {
15207
+ const msg = err.message ?? "";
15208
+ if (msg.includes("EEXIST") || msg.includes("already exists")) {
15209
+ const wrapped = new Error(`EEXIST: directory already exists, mkdir '${path}'`);
15210
+ wrapped.code = "EEXIST";
15211
+ throw wrapped;
15212
+ }
13757
15213
  throw err;
13758
15214
  }
13759
- await ctx.fs.mkdir(path);
13760
15215
  },
13761
- rmdir: (path) => ctx.fs.rm(path)
15216
+ rmdir: (path) => ctx.fs.rm(path),
15217
+ unlink: (path) => ctx.fs.unlink(path)
13762
15218
  };
13763
15219
  }
13764
15220
  async function kernelSpawn(ctx, options) {
@@ -13822,11 +15278,149 @@ async function kernelListen(ctx, options) {
13822
15278
  }
13823
15279
  };
13824
15280
  }
15281
+ var KERNEL_LOCALHOST_RE = /^(\d+)\.kernel\.localhost$/;
15282
+ function extractLoopbackPort(url) {
15283
+ const m = KERNEL_LOCALHOST_RE.exec(url.hostname);
15284
+ if (m) return Number(m[1]);
15285
+ return null;
15286
+ }
15287
+ function collectHeaders(init) {
15288
+ const entries = [];
15289
+ if (!init?.headers) return entries;
15290
+ if (init.headers instanceof Headers) {
15291
+ for (const [k, v] of init.headers.entries()) entries.push([k, v]);
15292
+ } else if (Array.isArray(init.headers)) {
15293
+ for (const pair of init.headers) entries.push(pair);
15294
+ } else {
15295
+ for (const [k, v] of Object.entries(init.headers)) {
15296
+ if (v != null) entries.push([k, String(v)]);
15297
+ }
15298
+ }
15299
+ return entries;
15300
+ }
15301
+ function buildHttpRequest(method, url, headerEntries, bodyBytes) {
15302
+ const enc = new TextEncoder();
15303
+ const has = (n) => headerEntries.some(([k]) => k.toLowerCase() === n.toLowerCase());
15304
+ if (!has("host")) headerEntries.push(["Host", url.host]);
15305
+ if (!has("connection")) headerEntries.push(["Connection", "close"]);
15306
+ if (bodyBytes.length > 0 && !has("content-length")) {
15307
+ headerEntries.push(["Content-Length", String(bodyBytes.length)]);
15308
+ }
15309
+ const target = url.pathname + (url.search || "");
15310
+ let reqText = `${method} ${target} HTTP/1.1\r
15311
+ `;
15312
+ for (const [k, v] of headerEntries) reqText += `${k}: ${v}\r
15313
+ `;
15314
+ reqText += "\r\n";
15315
+ const headBytes = enc.encode(reqText);
15316
+ if (bodyBytes.length === 0) return headBytes;
15317
+ const full = new Uint8Array(headBytes.length + bodyBytes.length);
15318
+ full.set(headBytes);
15319
+ full.set(bodyBytes, headBytes.length);
15320
+ return full;
15321
+ }
15322
+ function parseHttpResponse(buf, headerPos, bufLen) {
15323
+ const dec = new TextDecoder();
15324
+ const headerText = dec.decode(new Uint8Array(buf.buffer, buf.byteOffset, headerPos));
15325
+ const lines = headerText.split("\r\n");
15326
+ const statusMatch = /^HTTP\/1\.1\s+(\d+)\s*(.*)/.exec(lines[0] || "");
15327
+ const status = statusMatch ? Number(statusMatch[1]) : 500;
15328
+ const statusText = statusMatch ? statusMatch[2] : "Error";
15329
+ const respHeaders = {};
15330
+ for (let i = 1; i < lines.length; i++) {
15331
+ const colonIdx = lines[i].indexOf(":");
15332
+ if (colonIdx > 0) {
15333
+ respHeaders[lines[i].slice(0, colonIdx).trim().toLowerCase()] = lines[i].slice(colonIdx + 1).trim();
15334
+ }
15335
+ }
15336
+ const contentLength = Number(respHeaders["content-length"] ?? 0);
15337
+ const headerEndByte = headerPos + 4;
15338
+ const responseBody = dec.decode(buf.slice(headerEndByte, headerEndByte + contentLength));
15339
+ return { status, statusText, headers: respHeaders, body: responseBody };
15340
+ }
15341
+ async function kernelLoopbackFetch(ctx, url, port, init) {
15342
+ const method = (init?.method ?? "GET").toUpperCase();
15343
+ const enc = new TextEncoder();
15344
+ const dec = new TextDecoder();
15345
+ let bodyStr = "";
15346
+ if (init?.body != null) {
15347
+ bodyStr = typeof init.body === "string" ? init.body : dec.decode(init.body);
15348
+ }
15349
+ const bodyBytes = enc.encode(bodyStr);
15350
+ const headerEntries = collectHeaders(init);
15351
+ const reqBytes = buildHttpRequest(method, url, headerEntries, bodyBytes);
15352
+ const conn = await ctx.net.connect({ host: "127.0.0.1", port });
15353
+ const writer = conn.writable.getWriter();
15354
+ await writer.write(reqBytes);
15355
+ const reader = conn.readable.getReader();
15356
+ let buf = new Uint8Array(4096);
15357
+ let bufLen = 0;
15358
+ const headerResult = await readUntil(reader, buf, bufLen, "\r\n\r\n", 16384);
15359
+ if (!headerResult) {
15360
+ reader.releaseLock();
15361
+ try {
15362
+ await writer.close();
15363
+ } catch {
15364
+ }
15365
+ conn.close();
15366
+ throw new Error("incomplete HTTP response headers");
15367
+ }
15368
+ buf = headerResult.buf;
15369
+ bufLen = headerResult.bufLen;
15370
+ const contentLength = Number(
15371
+ dec.decode(new Uint8Array(buf.buffer, buf.byteOffset, headerResult.pos)).split("\r\n").find((l) => l.toLowerCase().startsWith("content-length:"))?.split(":")[1]?.trim() ?? 0
15372
+ );
15373
+ const totalNeeded = headerResult.pos + 4 + contentLength;
15374
+ if (bufLen < totalNeeded && contentLength > 0) {
15375
+ const bodyResult = await readExact(reader, buf, bufLen, totalNeeded);
15376
+ if (bodyResult) {
15377
+ buf = bodyResult.buf;
15378
+ bufLen = bodyResult.bufLen;
15379
+ }
15380
+ }
15381
+ const parsed = parseHttpResponse(buf, headerResult.pos, bufLen);
15382
+ reader.releaseLock();
15383
+ try {
15384
+ await writer.close();
15385
+ } catch {
15386
+ }
15387
+ conn.close();
15388
+ return new Response(parsed.body, {
15389
+ status: parsed.status,
15390
+ statusText: parsed.statusText,
15391
+ headers: parsed.headers
15392
+ });
15393
+ }
15394
+ function createKernelFetch(ctx) {
15395
+ return async (input, init) => {
15396
+ const urlStr = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
15397
+ let url;
15398
+ try {
15399
+ url = new URL(urlStr);
15400
+ } catch {
15401
+ return ctx.fetch(input, init);
15402
+ }
15403
+ const port = extractLoopbackPort(url);
15404
+ if (port !== null) return kernelLoopbackFetch(ctx, url, port, init);
15405
+ return ctx.fetch(input, init);
15406
+ };
15407
+ }
15408
+ async function kernelSeal(ctx, request) {
15409
+ const f = await ctx.open("/dev/sealed", { read: true, write: true });
15410
+ try {
15411
+ const enc = new TextEncoder();
15412
+ await f.write(0, enc.encode(request));
15413
+ const responseBytes = await f.read(0, 65536);
15414
+ return new TextDecoder().decode(responseBytes);
15415
+ } finally {
15416
+ await f.close();
15417
+ }
15418
+ }
13825
15419
  function createKernelPlatform(ctx, options) {
13826
15420
  const encoder2 = new TextEncoder();
13827
15421
  return {
13828
15422
  kind: "kernel",
13829
- fetch: ctx.fetch,
15423
+ fetch: createKernelFetch(ctx),
13830
15424
  spawn: (options2) => kernelSpawn(ctx, options2),
13831
15425
  listen: (options2) => kernelListen(ctx, options2),
13832
15426
  fs: kernelFs(ctx),
@@ -13845,17 +15439,26 @@ function createKernelPlatform(ctx, options) {
13845
15439
  onSignal(signal, handler) {
13846
15440
  ctx.signals.on(signal, handler);
13847
15441
  },
15442
+ async seal(request) {
15443
+ return kernelSeal(ctx, request);
15444
+ },
13848
15445
  packageDir: options?.packageDir
13849
15446
  };
13850
15447
  }
13851
15448
 
13852
15449
  // src/kernel.ts
13853
15450
  async function main(ctx) {
15451
+ if (ctx.signals?.ignore) {
15452
+ try {
15453
+ ctx.signals.ignore("SIGPIPE");
15454
+ } catch {
15455
+ }
15456
+ }
13854
15457
  const platform2 = createKernelPlatform(ctx, {
13855
15458
  packageDir: "/node_modules/@ai-ecoverse/mcpcat"
13856
15459
  });
13857
15460
  const args = ctx.argv.slice(1);
13858
- return run(platform2, "0.12.0", args);
15461
+ return run(platform2, "0.13.0", args);
13859
15462
  }
13860
15463
  export {
13861
15464
  main as default