@mcpjam/inspector 0.9.29 → 0.9.30

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.
@@ -1,7 +1,7 @@
1
1
  // index.ts
2
2
  import { serve } from "@hono/node-server";
3
3
  import fixPath from "fix-path";
4
- import { Hono as Hono11 } from "hono";
4
+ import { Hono as Hono13 } from "hono";
5
5
  import { cors } from "hono/cors";
6
6
  import { logger } from "hono/logger";
7
7
  import { serveStatic } from "@hono/node-server/serve-static";
@@ -9,7 +9,7 @@ import { readFileSync } from "fs";
9
9
  import { join } from "path";
10
10
 
11
11
  // routes/mcp/index.ts
12
- import { Hono as Hono10 } from "hono";
12
+ import { Hono as Hono12 } from "hono";
13
13
 
14
14
  // routes/mcp/connect.ts
15
15
  import { Hono } from "hono";
@@ -3516,10 +3516,6 @@ var chat_default = chat;
3516
3516
 
3517
3517
  // routes/mcp/tests.ts
3518
3518
  import { Hono as Hono7 } from "hono";
3519
- import { createAnthropic as createAnthropic2 } from "@ai-sdk/anthropic";
3520
- import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
3521
- import { createOllama as createOllama2 } from "ollama-ai-provider";
3522
- import { Agent as Agent2 } from "@mastra/core/agent";
3523
3519
 
3524
3520
  // utils/mcp-utils.ts
3525
3521
  import { MCPClient } from "@mastra/mcp";
@@ -3537,10 +3533,7 @@ function validateServerConfig(serverConfig) {
3537
3533
  if (config.url) {
3538
3534
  try {
3539
3535
  if (typeof config.url === "string") {
3540
- const parsed = new URL(config.url);
3541
- parsed.search = "";
3542
- parsed.hash = "";
3543
- config.url = parsed;
3536
+ config.url = new URL(config.url);
3544
3537
  } else if (typeof config.url === "object" && !config.url.href) {
3545
3538
  return {
3546
3539
  success: false,
@@ -3697,39 +3690,72 @@ function normalizeServerConfigName(serverName) {
3697
3690
 
3698
3691
  // routes/mcp/tests.ts
3699
3692
  var tests = new Hono7();
3693
+ function normalizeToolName(toolName, serverIds) {
3694
+ for (const id of serverIds) {
3695
+ const prefix = `${id}_`;
3696
+ if (toolName.startsWith(prefix)) {
3697
+ return toolName.slice(prefix.length);
3698
+ }
3699
+ }
3700
+ return toolName;
3701
+ }
3702
+ function extractToolSchema(tool) {
3703
+ if (!tool?.inputSchema) return {};
3704
+ const jsonSchema2 = tool.inputSchema.toJSON?.();
3705
+ if (jsonSchema2 && typeof jsonSchema2 === "object") {
3706
+ return jsonSchema2;
3707
+ }
3708
+ const zodDef = tool.inputSchema._def;
3709
+ if (zodDef?.typeName === "ZodObject") {
3710
+ const baseSchema = {
3711
+ type: "object",
3712
+ additionalProperties: false,
3713
+ properties: {},
3714
+ required: []
3715
+ };
3716
+ try {
3717
+ if (zodDef.shape && typeof zodDef.shape === "function") {
3718
+ const shape = zodDef.shape();
3719
+ const properties = {};
3720
+ const required = [];
3721
+ for (const [key, value] of Object.entries(shape)) {
3722
+ properties[key] = { type: "string" };
3723
+ if (value?._def?.typeName !== "ZodOptional") {
3724
+ required.push(key);
3725
+ }
3726
+ }
3727
+ return { ...baseSchema, properties, required };
3728
+ }
3729
+ } catch {
3730
+ }
3731
+ return baseSchema;
3732
+ }
3733
+ return {};
3734
+ }
3735
+ function resolveBackendUrl(overrideUrl) {
3736
+ return "https://industrious-stingray-146.convex.site";
3737
+ if (overrideUrl) return overrideUrl.replace(/\/$/, "");
3738
+ const explicit = process.env.CONVEX_HTTP_URL;
3739
+ if (explicit) return explicit.replace(/\/$/, "");
3740
+ const convexUrl = process.env.VITE_CONVEX_URL || process.env.CONVEX_URL;
3741
+ if (convexUrl) {
3742
+ try {
3743
+ const u = new URL(convexUrl);
3744
+ const host = u.host.replace(".convex.cloud", ".convex.site");
3745
+ return `${u.protocol}//${host}`;
3746
+ } catch {
3747
+ }
3748
+ }
3749
+ return "http://localhost:3210";
3750
+ }
3700
3751
  var tests_default = tests;
3701
3752
  tests.post("/run-all", async (c) => {
3702
3753
  const encoder = new TextEncoder();
3703
3754
  try {
3704
- let createModel2 = function(model) {
3705
- switch (model.provider) {
3706
- case "anthropic":
3707
- return createAnthropic2({
3708
- apiKey: providerApiKeys?.anthropic || process.env.ANTHROPIC_API_KEY || ""
3709
- })(model.id);
3710
- case "openai":
3711
- return createOpenAI2({
3712
- apiKey: providerApiKeys?.openai || process.env.OPENAI_API_KEY || ""
3713
- })(model.id);
3714
- case "deepseek":
3715
- return createOpenAI2({
3716
- apiKey: providerApiKeys?.deepseek || process.env.DEEPSEEK_API_KEY || "",
3717
- baseURL: "https://api.deepseek.com/v1"
3718
- })(model.id);
3719
- case "ollama":
3720
- return createOllama2({
3721
- baseURL: ollamaBaseUrl || process.env.OLLAMA_BASE_URL || "http://localhost:11434/api"
3722
- })(model.id, { simulateStreaming: true });
3723
- default:
3724
- throw new Error(`Unsupported provider: ${model.provider}`);
3725
- }
3726
- };
3727
- var createModel = createModel2;
3728
3755
  const body = await c.req.json();
3729
3756
  const testsInput = body?.tests || [];
3757
+ const overrideBackendHttpUrl = body?.backendHttpUrl;
3730
3758
  const allServers = body?.allServers || {};
3731
- const providerApiKeys = body?.providerApiKeys || {};
3732
- const ollamaBaseUrl = body?.ollamaBaseUrl;
3733
3759
  const maxConcurrency = Math.max(
3734
3760
  1,
3735
3761
  Math.min(8, body?.concurrency ?? 5)
@@ -3787,55 +3813,149 @@ tests.post("/run-all", async (c) => {
3787
3813
  throw new Error("No valid MCP server configs for test");
3788
3814
  }
3789
3815
  client = createMCPClientWithMultipleConnections(finalServers);
3790
- const model = createModel2(test.model);
3791
- const agent = new Agent2({
3792
- name: `TestAgent-${test.id}`,
3793
- instructions: "You are a helpful assistant with access to MCP tools",
3794
- model
3795
- });
3796
- const toolsets = await client.getToolsets();
3797
- const stream = await agent.streamVNext(
3798
- [{ role: "user", content: test.prompt || "" }],
3799
- {
3800
- maxSteps: 10,
3801
- toolsets
3802
- }
3816
+ const tools2 = await client.getTools();
3817
+ const toolsSchemas = Object.entries(tools2).map(
3818
+ ([name17, tool]) => ({
3819
+ toolName: name17,
3820
+ inputSchema: extractToolSchema(tool)
3821
+ })
3803
3822
  );
3804
- for await (const chunk of stream.fullStream) {
3805
- if (chunk.type === "tool-call" && chunk.payload) {
3806
- const toolName = chunk.payload.toolName;
3807
- if (toolName) {
3808
- calledTools.add(toolName);
3809
- }
3810
- }
3811
- if (chunk.type === "finish") {
3812
- step += 1;
3823
+ const runId = `${Date.now()}-${test.id}`;
3824
+ const backendUrl = resolveBackendUrl(overrideBackendHttpUrl);
3825
+ controller.enqueue(
3826
+ encoder.encode(
3827
+ `data: ${JSON.stringify({ type: "debug", testId: test.id, backendUrl })}
3828
+
3829
+ `
3830
+ )
3831
+ );
3832
+ const startRes = await fetch(`${backendUrl}/evals/agent/start`, {
3833
+ method: "POST",
3834
+ headers: { "content-type": "application/json" },
3835
+ body: JSON.stringify({
3836
+ runId,
3837
+ model: test.model,
3838
+ toolsSchemas,
3839
+ messages: [
3840
+ {
3841
+ role: "system",
3842
+ content: "You are a helpful assistant with access to MCP tools."
3843
+ },
3844
+ { role: "user", content: test.prompt || "" }
3845
+ ]
3846
+ })
3847
+ });
3848
+ if (!startRes.ok) {
3849
+ const errText = await startRes.text().catch(() => "");
3850
+ controller.enqueue(
3851
+ encoder.encode(
3852
+ `data: ${JSON.stringify({ type: "result", testId: test.id, passed: false, error: `Backend start failed: ${startRes.status} ${errText}` })}
3853
+
3854
+ `
3855
+ )
3856
+ );
3857
+ throw new Error(
3858
+ `Backend start failed: ${startRes.status} ${errText}`
3859
+ );
3860
+ }
3861
+ const startJson = await startRes.json();
3862
+ if (!startJson.ok)
3863
+ throw new Error(startJson.error || "start failed");
3864
+ let state = startJson;
3865
+ const serverIds = Object.keys(finalServers);
3866
+ while (state.kind === "tool_call") {
3867
+ const name17 = state.toolName;
3868
+ const args = state.args || {};
3869
+ const tool = tools2[name17];
3870
+ try {
3871
+ const result = await tool?.execute({ context: args });
3872
+ calledTools.add(name17);
3813
3873
  controller.enqueue(
3814
3874
  encoder.encode(
3815
3875
  `data: ${JSON.stringify({
3816
3876
  type: "trace_step",
3817
3877
  testId: test.id,
3818
- step,
3819
- text: "Test completed",
3820
- toolCalls: Array.from(calledTools),
3821
- toolResults: []
3878
+ step: ++step,
3879
+ text: "Executed tool",
3880
+ toolCalls: [normalizeToolName(name17, serverIds)],
3881
+ toolResults: [result]
3822
3882
  })}
3823
3883
 
3824
3884
  `
3825
3885
  )
3826
3886
  );
3887
+ const stepRes = await fetch(
3888
+ `${backendUrl}/evals/agent/step`,
3889
+ {
3890
+ method: "POST",
3891
+ headers: { "content-type": "application/json" },
3892
+ body: JSON.stringify({
3893
+ runId,
3894
+ model: test.model,
3895
+ toolsSchemas,
3896
+ messages: state.steps?.[state.steps.length - 1]?.messages || [
3897
+ {
3898
+ role: "system",
3899
+ content: "You are a helpful assistant with access to MCP tools."
3900
+ },
3901
+ { role: "user", content: test.prompt || "" }
3902
+ ],
3903
+ toolResultMessage: {
3904
+ role: "tool",
3905
+ content: [
3906
+ {
3907
+ type: "tool-result",
3908
+ toolCallId: state.toolCallId,
3909
+ toolName: name17,
3910
+ output: result
3911
+ }
3912
+ ]
3913
+ }
3914
+ })
3915
+ }
3916
+ );
3917
+ const stepJson = await stepRes.json();
3918
+ if (!stepJson.ok) {
3919
+ controller.enqueue(
3920
+ encoder.encode(
3921
+ `data: ${JSON.stringify({
3922
+ type: "result",
3923
+ testId: test.id,
3924
+ passed: false,
3925
+ error: stepJson.error || "step failed"
3926
+ })}
3927
+
3928
+ `
3929
+ )
3930
+ );
3931
+ throw new Error(stepJson.error || "step failed");
3932
+ }
3933
+ state = stepJson;
3934
+ } catch (err) {
3935
+ throw new Error(
3936
+ `Tool '${name17}' failed: ${err instanceof Error ? err.message : String(err)}`
3937
+ );
3827
3938
  }
3828
3939
  }
3829
- const called = Array.from(calledTools);
3940
+ const called = Array.from(calledTools).map(
3941
+ (t) => normalizeToolName(t, serverIds)
3942
+ );
3830
3943
  const missing = Array.from(expectedSet).filter(
3831
- (t) => !calledTools.has(t)
3944
+ (t) => !called.includes(t)
3832
3945
  );
3833
3946
  const unexpected = called.filter((t) => !expectedSet.has(t));
3834
3947
  const passed = missing.length === 0 && unexpected.length === 0;
3835
3948
  if (!passed) failed = true;
3836
3949
  controller.enqueue(
3837
3950
  encoder.encode(
3838
- `data: ${JSON.stringify({ type: "result", testId: test.id, passed, calledTools: called, missingTools: missing, unexpectedTools: unexpected })}
3951
+ `data: ${JSON.stringify({
3952
+ type: "result",
3953
+ testId: test.id,
3954
+ passed,
3955
+ calledTools: called,
3956
+ missingTools: missing,
3957
+ unexpectedTools: unexpected
3958
+ })}
3839
3959
 
3840
3960
  `
3841
3961
  )
@@ -3981,8 +4101,1187 @@ exporter.post("/server", async (c) => {
3981
4101
  });
3982
4102
  var export_default = exporter;
3983
4103
 
4104
+ // routes/mcp/interceptor.ts
4105
+ import { Hono as Hono10 } from "hono";
4106
+
4107
+ // services/interceptor-store.ts
4108
+ import { randomUUID } from "crypto";
4109
+ var InterceptorStore = class {
4110
+ interceptors = /* @__PURE__ */ new Map();
4111
+ sessionIndex = /* @__PURE__ */ new Map();
4112
+ byServer = /* @__PURE__ */ new Map();
4113
+ create(targetUrl, injectHeaders, serverId) {
4114
+ const id = randomUUID().replace(/-/g, "");
4115
+ const entry = {
4116
+ id,
4117
+ targetUrl,
4118
+ createdAt: Date.now(),
4119
+ logs: [],
4120
+ subscribers: /* @__PURE__ */ new Set(),
4121
+ injectHeaders,
4122
+ sessionEndpoints: /* @__PURE__ */ new Map(),
4123
+ serverId
4124
+ };
4125
+ this.interceptors.set(id, entry);
4126
+ if (serverId) {
4127
+ if (!this.byServer.has(serverId)) this.byServer.set(serverId, /* @__PURE__ */ new Set());
4128
+ this.byServer.get(serverId).add(id);
4129
+ }
4130
+ return entry;
4131
+ }
4132
+ get(id) {
4133
+ return this.interceptors.get(id);
4134
+ }
4135
+ info(id) {
4136
+ const e = this.interceptors.get(id);
4137
+ if (!e) return void 0;
4138
+ return {
4139
+ id: e.id,
4140
+ targetUrl: e.targetUrl,
4141
+ createdAt: e.createdAt,
4142
+ logCount: e.logs.length,
4143
+ hasInjectedAuth: !!e.injectHeaders?.authorization
4144
+ };
4145
+ }
4146
+ clearLogs(id) {
4147
+ const e = this.interceptors.get(id);
4148
+ if (!e) return false;
4149
+ e.logs = [];
4150
+ this.broadcast(e, { type: "cleared" });
4151
+ return true;
4152
+ }
4153
+ appendLog(id, log) {
4154
+ const e = this.interceptors.get(id);
4155
+ if (!e) return false;
4156
+ e.logs.push(log);
4157
+ this.broadcast(e, { type: "log", log });
4158
+ return true;
4159
+ }
4160
+ listLogs(id) {
4161
+ const e = this.interceptors.get(id);
4162
+ return e?.logs ?? [];
4163
+ }
4164
+ subscribe(id, subscriber) {
4165
+ const e = this.interceptors.get(id);
4166
+ if (!e) return false;
4167
+ e.subscribers.add(subscriber);
4168
+ return () => {
4169
+ e.subscribers.delete(subscriber);
4170
+ };
4171
+ }
4172
+ setSessionEndpoint(id, sessionId, url) {
4173
+ const e = this.interceptors.get(id);
4174
+ if (!e) return false;
4175
+ e.sessionEndpoints.set(sessionId, url);
4176
+ this.sessionIndex.set(sessionId, { interceptorId: id, url });
4177
+ return true;
4178
+ }
4179
+ getSessionEndpoint(id, sessionId) {
4180
+ const e = this.interceptors.get(id);
4181
+ return e?.sessionEndpoints.get(sessionId);
4182
+ }
4183
+ getSessionMapping(sessionId) {
4184
+ return this.sessionIndex.get(sessionId);
4185
+ }
4186
+ destroy(id) {
4187
+ const e = this.interceptors.get(id);
4188
+ if (!e) return false;
4189
+ for (const sub of Array.from(e.subscribers)) {
4190
+ try {
4191
+ sub.send({ type: "closed" });
4192
+ } catch {
4193
+ }
4194
+ try {
4195
+ sub.close();
4196
+ } catch {
4197
+ }
4198
+ }
4199
+ e.subscribers.clear();
4200
+ for (const sid of Array.from(e.sessionEndpoints.keys())) {
4201
+ this.sessionIndex.delete(sid);
4202
+ }
4203
+ this.interceptors.delete(id);
4204
+ return true;
4205
+ }
4206
+ destroyByServer(serverId) {
4207
+ const set = this.byServer.get(serverId);
4208
+ if (!set || set.size === 0) return 0;
4209
+ let count = 0;
4210
+ for (const id of Array.from(set)) {
4211
+ if (this.destroy(id)) count++;
4212
+ }
4213
+ this.byServer.delete(serverId);
4214
+ return count;
4215
+ }
4216
+ broadcast(e, payload) {
4217
+ for (const sub of Array.from(e.subscribers)) {
4218
+ try {
4219
+ sub.send(payload);
4220
+ } catch {
4221
+ try {
4222
+ sub.close();
4223
+ } catch {
4224
+ }
4225
+ e.subscribers.delete(sub);
4226
+ }
4227
+ }
4228
+ }
4229
+ };
4230
+ var interceptorStore = new InterceptorStore();
4231
+
4232
+ // routes/mcp/interceptor.ts
4233
+ var interceptor = new Hono10();
4234
+ function withCORS(res) {
4235
+ const headers = new Headers(res.headers);
4236
+ headers.set("Access-Control-Allow-Origin", "*");
4237
+ headers.set("Access-Control-Allow-Methods", "GET,POST,HEAD,OPTIONS");
4238
+ headers.set(
4239
+ "Access-Control-Allow-Headers",
4240
+ "Authorization, Content-Type, Accept, Accept-Language"
4241
+ );
4242
+ headers.set("Access-Control-Expose-Headers", "*");
4243
+ headers.set("Vary", "Origin, Access-Control-Request-Headers");
4244
+ headers.delete("content-length");
4245
+ headers.delete("Content-Length");
4246
+ headers.delete("transfer-encoding");
4247
+ headers.delete("Transfer-Encoding");
4248
+ return new Response(res.body, {
4249
+ status: res.status,
4250
+ statusText: res.statusText,
4251
+ headers
4252
+ });
4253
+ }
4254
+ function maskHeaders(orig) {
4255
+ const out = {};
4256
+ orig.forEach((value, key) => {
4257
+ if (key.toLowerCase() === "authorization") {
4258
+ out[key] = value.startsWith("Bearer ") ? "Bearer ***" : "***";
4259
+ } else {
4260
+ out[key] = value;
4261
+ }
4262
+ });
4263
+ return out;
4264
+ }
4265
+ interceptor.post("/create", async (c) => {
4266
+ try {
4267
+ const body = await c.req.json();
4268
+ const targetUrl = body?.targetUrl;
4269
+ const serverId = body?.serverId || body?.managerServerId;
4270
+ const urlObj = new URL(c.req.url);
4271
+ let finalTarget = targetUrl;
4272
+ let injectHeaders;
4273
+ if (serverId) {
4274
+ const connected = c.mcpJamClientManager.getConnectedServers();
4275
+ const serverMeta = connected[serverId];
4276
+ const cfg = serverMeta?.config;
4277
+ if (!cfg || serverMeta?.status !== "connected") {
4278
+ return c.json(
4279
+ { success: false, error: `Server '${serverId}' is not connected` },
4280
+ 400
4281
+ );
4282
+ }
4283
+ if (!finalTarget) {
4284
+ if (cfg.url) {
4285
+ finalTarget = typeof cfg.url === "string" ? cfg.url : cfg.url.toString();
4286
+ } else {
4287
+ const origin = new URL(c.req.url).origin;
4288
+ finalTarget = `${origin}/api/mcp/adapter-http/${encodeURIComponent(serverId)}`;
4289
+ }
4290
+ }
4291
+ const hdrs = {};
4292
+ const fromReqInit = cfg.requestInit?.headers;
4293
+ if (fromReqInit) {
4294
+ for (const [k, v] of Object.entries(fromReqInit)) {
4295
+ if (typeof v === "string") hdrs[k.toLowerCase()] = v;
4296
+ }
4297
+ }
4298
+ const token = cfg?.oauth?.access_token || cfg?.oauth?.accessToken;
4299
+ if (token && !hdrs["authorization"]) {
4300
+ hdrs["authorization"] = `Bearer ${token}`;
4301
+ hdrs["Authorization"] = `Bearer ${token}`;
4302
+ }
4303
+ injectHeaders = hdrs;
4304
+ }
4305
+ if (!finalTarget) {
4306
+ return c.json(
4307
+ { success: false, error: "targetUrl or serverId is required" },
4308
+ 400
4309
+ );
4310
+ }
4311
+ try {
4312
+ const u = new URL(finalTarget);
4313
+ if (!["http:", "https:"].includes(u.protocol)) {
4314
+ return c.json(
4315
+ {
4316
+ success: false,
4317
+ error: "Only HTTP/HTTPS MCP servers are supported"
4318
+ },
4319
+ 400
4320
+ );
4321
+ }
4322
+ } catch {
4323
+ return c.json({ success: false, error: "Invalid URL" }, 400);
4324
+ }
4325
+ const entry = interceptorStore.create(finalTarget, injectHeaders, serverId);
4326
+ const localOrigin = urlObj.origin;
4327
+ const publicOrigin = null;
4328
+ const proxyPath = `/api/mcp/interceptor/${entry.id}/proxy`;
4329
+ const localProxyUrl = `${localOrigin}${proxyPath}`;
4330
+ const publicProxyUrl = publicOrigin ? `${publicOrigin}${proxyPath}` : null;
4331
+ const proxyUrl = publicProxyUrl || localProxyUrl;
4332
+ return c.json({
4333
+ success: true,
4334
+ id: entry.id,
4335
+ targetUrl: entry.targetUrl,
4336
+ proxyUrl,
4337
+ localProxyUrl,
4338
+ publicProxyUrl
4339
+ });
4340
+ } catch (err) {
4341
+ return c.json(
4342
+ { success: false, error: err?.message || "Invalid JSON" },
4343
+ 400
4344
+ );
4345
+ }
4346
+ });
4347
+ interceptor.get("/:id", (c) => {
4348
+ const id = c.req.param("id");
4349
+ const info = interceptorStore.info(id);
4350
+ if (!info) return c.json({ success: false, error: "not found" }, 404);
4351
+ const urlObj = new URL(c.req.url);
4352
+ const publicOrigin = null;
4353
+ const proxyPath = `/api/mcp/interceptor/${id}/proxy`;
4354
+ const localProxyUrl = `${urlObj.origin}${proxyPath}`;
4355
+ const publicProxyUrl = publicOrigin ? `${publicOrigin}${proxyPath}` : null;
4356
+ const proxyUrl = publicProxyUrl || localProxyUrl;
4357
+ return c.json({
4358
+ success: true,
4359
+ ...info,
4360
+ proxyUrl,
4361
+ localProxyUrl,
4362
+ publicProxyUrl
4363
+ });
4364
+ });
4365
+ interceptor.post("/:id/clear", (c) => {
4366
+ const id = c.req.param("id");
4367
+ const ok = interceptorStore.clearLogs(id);
4368
+ if (!ok) return c.json({ success: false, error: "not found" }, 404);
4369
+ return c.json({ success: true });
4370
+ });
4371
+ interceptor.delete("/:id", (c) => {
4372
+ const id = c.req.param("id");
4373
+ const ok = interceptorStore.destroy(id);
4374
+ if (!ok) return c.json({ success: false, error: "not found" }, 404);
4375
+ return c.json({ success: true });
4376
+ });
4377
+ interceptor.delete("/by-server/:serverId", (c) => {
4378
+ const serverId = c.req.param("serverId");
4379
+ const count = interceptorStore.destroyByServer(serverId);
4380
+ return c.json({ success: true, count });
4381
+ });
4382
+ interceptor.get("/:id/stream", (c) => {
4383
+ const id = c.req.param("id");
4384
+ const entry = interceptorStore.get(id);
4385
+ if (!entry) return c.json({ success: false, error: "not found" }, 404);
4386
+ const encoder = new TextEncoder();
4387
+ let unsubscribeFn;
4388
+ const stream = new ReadableStream({
4389
+ start(controller) {
4390
+ for (const log of entry.logs) {
4391
+ controller.enqueue(
4392
+ encoder.encode(`data: ${JSON.stringify({ type: "log", log })}
4393
+
4394
+ `)
4395
+ );
4396
+ }
4397
+ const subscriber = {
4398
+ send: (event) => {
4399
+ controller.enqueue(
4400
+ encoder.encode(`data: ${JSON.stringify(event)}
4401
+
4402
+ `)
4403
+ );
4404
+ },
4405
+ close: () => controller.close()
4406
+ };
4407
+ const unsubscribe = interceptorStore.subscribe(id, subscriber);
4408
+ unsubscribeFn = unsubscribe;
4409
+ },
4410
+ cancel() {
4411
+ try {
4412
+ unsubscribeFn && unsubscribeFn();
4413
+ } catch {
4414
+ }
4415
+ }
4416
+ });
4417
+ return new Response(stream, {
4418
+ headers: {
4419
+ "Content-Type": "text/event-stream",
4420
+ "Cache-Control": "no-cache",
4421
+ Connection: "keep-alive",
4422
+ "Access-Control-Allow-Origin": "*",
4423
+ "Access-Control-Allow-Methods": "GET,POST,HEAD,OPTIONS",
4424
+ "Access-Control-Allow-Headers": "*, Authorization, Content-Type, Accept, Accept-Language",
4425
+ "X-Accel-Buffering": "no"
4426
+ }
4427
+ });
4428
+ });
4429
+ interceptor.options("/:id/proxy", (c) => {
4430
+ return c.body(null, 204, {
4431
+ "Access-Control-Allow-Origin": "*",
4432
+ "Access-Control-Allow-Methods": "GET,POST,HEAD,OPTIONS",
4433
+ "Access-Control-Allow-Headers": "Authorization, Content-Type, Accept, Accept-Language",
4434
+ "Access-Control-Max-Age": "86400",
4435
+ Vary: "Origin, Access-Control-Request-Headers"
4436
+ });
4437
+ });
4438
+ interceptor.options("/:id/proxy/*", (c) => {
4439
+ return c.body(null, 204, {
4440
+ "Access-Control-Allow-Origin": "*",
4441
+ "Access-Control-Allow-Methods": "GET,POST,HEAD,OPTIONS",
4442
+ "Access-Control-Allow-Headers": "Authorization, Content-Type, Accept, Accept-Language",
4443
+ "Access-Control-Max-Age": "86400",
4444
+ Vary: "Origin, Access-Control-Request-Headers"
4445
+ });
4446
+ });
4447
+ async function handleProxy(c) {
4448
+ const id = c.req.param("id");
4449
+ const entry = interceptorStore.get(id);
4450
+ if (!entry) return c.json({ success: false, error: "not found" }, 404);
4451
+ const req = c.req.raw;
4452
+ const requestId = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
4453
+ let requestBody;
4454
+ try {
4455
+ const clone = req.clone();
4456
+ requestBody = await clone.text();
4457
+ } catch {
4458
+ requestBody = void 0;
4459
+ }
4460
+ interceptorStore.appendLog(id, {
4461
+ id: requestId,
4462
+ timestamp: Date.now(),
4463
+ direction: "request",
4464
+ method: req.method,
4465
+ url: entry.targetUrl,
4466
+ headers: maskHeaders(req.headers),
4467
+ body: requestBody
4468
+ });
4469
+ try {
4470
+ const accept = (req.headers.get("accept") || "").toLowerCase();
4471
+ const wantsSSE = accept.includes("text/event-stream");
4472
+ const targetPathname = new URL(entry.targetUrl).pathname;
4473
+ const upstreamLooksLikeSse = /\/sse(\/|$)/i.test(targetPathname);
4474
+ if (req.method === "HEAD" && wantsSSE && !upstreamLooksLikeSse) {
4475
+ const headers = new Headers({
4476
+ "Content-Type": "text/event-stream",
4477
+ "Cache-Control": "no-cache",
4478
+ Connection: "keep-alive"
4479
+ });
4480
+ headers.delete("content-length");
4481
+ headers.delete("Content-Length");
4482
+ headers.delete("transfer-encoding");
4483
+ headers.delete("Transfer-Encoding");
4484
+ return withCORS(new Response(null, { status: 200, headers }));
4485
+ }
4486
+ if (req.method === "GET" && wantsSSE && !upstreamLooksLikeSse) {
4487
+ const xfProto = req.headers.get("x-forwarded-proto");
4488
+ const xfHost = req.headers.get("x-forwarded-host");
4489
+ const host = xfHost || req.headers.get("host");
4490
+ let proto = xfProto;
4491
+ if (!proto) {
4492
+ const originHeader = req.headers.get("origin");
4493
+ if (originHeader && /^https:/i.test(originHeader)) proto = "https";
4494
+ }
4495
+ if (!proto) proto = "http";
4496
+ const proxyOrigin = host ? `${proto}://${host}` : new URL(req.url).origin;
4497
+ const sessionId = crypto.randomUUID();
4498
+ interceptorStore.setSessionEndpoint(
4499
+ id,
4500
+ sessionId,
4501
+ new URL(entry.targetUrl).toString()
4502
+ );
4503
+ const encoder = new TextEncoder();
4504
+ const stream = new ReadableStream({
4505
+ start(controller) {
4506
+ controller.enqueue(encoder.encode(`event: ping
4507
+ `));
4508
+ controller.enqueue(encoder.encode(`data:
4509
+
4510
+ `));
4511
+ const endpoint = `${proxyOrigin}/api/mcp/interceptor/${id}/proxy/messages?sessionId=${sessionId}`;
4512
+ const ua = c.req.header("user-agent") || "";
4513
+ const isClaude = /claude/i.test(ua) || /anthropic/i.test(ua);
4514
+ if (!isClaude) {
4515
+ controller.enqueue(encoder.encode(`event: endpoint
4516
+ `));
4517
+ controller.enqueue(
4518
+ encoder.encode(`data: ${JSON.stringify({ url: endpoint })}
4519
+
4520
+ `)
4521
+ );
4522
+ }
4523
+ controller.enqueue(encoder.encode(`event: endpoint
4524
+ `));
4525
+ controller.enqueue(encoder.encode(`data: ${endpoint}
4526
+
4527
+ `));
4528
+ const t = setInterval(() => {
4529
+ try {
4530
+ controller.enqueue(
4531
+ encoder.encode(`: keepalive ${Date.now()}
4532
+
4533
+ `)
4534
+ );
4535
+ } catch {
4536
+ }
4537
+ }, 15e3);
4538
+ controller._t = t;
4539
+ },
4540
+ cancel() {
4541
+ try {
4542
+ clearInterval(this._t);
4543
+ } catch {
4544
+ }
4545
+ }
4546
+ });
4547
+ {
4548
+ const headers = new Headers({
4549
+ "Content-Type": "text/event-stream",
4550
+ "Cache-Control": "no-cache",
4551
+ Connection: "keep-alive",
4552
+ "X-Accel-Buffering": "no"
4553
+ });
4554
+ headers.delete("content-length");
4555
+ headers.delete("Content-Length");
4556
+ headers.delete("transfer-encoding");
4557
+ headers.delete("Transfer-Encoding");
4558
+ return withCORS(new Response(stream, { headers }));
4559
+ }
4560
+ }
4561
+ } catch {
4562
+ }
4563
+ let upstreamUrl = new URL(entry.targetUrl);
4564
+ try {
4565
+ const originalUrl = new URL(req.url);
4566
+ const proxyBase = `/api/mcp/interceptor/${id}/proxy`;
4567
+ const rest = originalUrl.pathname.startsWith(proxyBase) ? originalUrl.pathname.slice(proxyBase.length) : "";
4568
+ const basePath = upstreamUrl.pathname.endsWith("/") ? upstreamUrl.pathname.slice(0, -1) : upstreamUrl.pathname;
4569
+ const trailing = rest ? rest.startsWith("/") ? rest : `/${rest}` : "";
4570
+ if (trailing.startsWith("/messages")) {
4571
+ const sessionId = new URL(req.url).searchParams.get("sessionId") || "";
4572
+ const mapped = sessionId ? interceptorStore.getSessionEndpoint(id, sessionId) : void 0;
4573
+ if (mapped) {
4574
+ upstreamUrl = new URL(mapped);
4575
+ } else {
4576
+ upstreamUrl.pathname = `${basePath}`;
4577
+ upstreamUrl.search = "";
4578
+ }
4579
+ } else {
4580
+ upstreamUrl.pathname = `${basePath}${trailing}`;
4581
+ const targetParams = new URLSearchParams(upstreamUrl.search);
4582
+ const requestParams = new URLSearchParams(originalUrl.search);
4583
+ requestParams.forEach((value, key) => {
4584
+ if (!targetParams.has(key)) {
4585
+ targetParams.set(key, value);
4586
+ }
4587
+ });
4588
+ upstreamUrl.search = targetParams.toString();
4589
+ }
4590
+ } catch {
4591
+ }
4592
+ const filtered = new Headers();
4593
+ req.headers.forEach((value, key) => {
4594
+ const k = key.toLowerCase();
4595
+ if ([
4596
+ "connection",
4597
+ "keep-alive",
4598
+ "transfer-encoding",
4599
+ "upgrade",
4600
+ "proxy-authenticate",
4601
+ "proxy-authorization",
4602
+ "te",
4603
+ "trailer"
4604
+ ].includes(k))
4605
+ return;
4606
+ if (k === "content-length") return;
4607
+ if (k === "host") return;
4608
+ filtered.set(key, value);
4609
+ });
4610
+ try {
4611
+ const acc = (filtered.get("accept") || "").toLowerCase();
4612
+ const hasJson = acc.includes("application/json");
4613
+ const hasSse = acc.includes("text/event-stream");
4614
+ if (!hasJson || !hasSse) {
4615
+ const parts = [];
4616
+ if (!hasJson) parts.push("application/json");
4617
+ if (!hasSse) parts.push("text/event-stream");
4618
+ const suffix = parts.join(", ");
4619
+ filtered.set("accept", acc ? `${acc}, ${suffix}` : suffix);
4620
+ }
4621
+ } catch {
4622
+ }
4623
+ if (entry.injectHeaders) {
4624
+ for (const [key, value] of Object.entries(entry.injectHeaders)) {
4625
+ const k = key.toLowerCase();
4626
+ if (k === "host" || k === "content-length") continue;
4627
+ if ([
4628
+ "connection",
4629
+ "keep-alive",
4630
+ "transfer-encoding",
4631
+ "upgrade",
4632
+ "proxy-authenticate",
4633
+ "proxy-authorization",
4634
+ "te",
4635
+ "trailer"
4636
+ ].includes(k))
4637
+ continue;
4638
+ if (k === "authorization" && filtered.has("authorization")) continue;
4639
+ filtered.set(key, value);
4640
+ }
4641
+ }
4642
+ const init = {
4643
+ method: req.method,
4644
+ headers: filtered
4645
+ };
4646
+ if (req.method !== "GET" && req.method !== "HEAD") {
4647
+ init.body = requestBody;
4648
+ }
4649
+ const targetReq = new Request(upstreamUrl.toString(), init);
4650
+ try {
4651
+ const res = await fetch(targetReq);
4652
+ const resClone = res.clone();
4653
+ const ct = (res.headers.get("content-type") || "").toLowerCase();
4654
+ const isStreaming = ct.includes("text/event-stream") || ct.includes("application/x-ndjson");
4655
+ let responseBody;
4656
+ try {
4657
+ if (ct.includes("text/event-stream") || ct.includes("application/x-ndjson")) {
4658
+ responseBody = "[stream]";
4659
+ } else {
4660
+ responseBody = await resClone.text();
4661
+ }
4662
+ } catch {
4663
+ responseBody = void 0;
4664
+ }
4665
+ if (!isStreaming) {
4666
+ interceptorStore.appendLog(id, {
4667
+ id: `${requestId}-res`,
4668
+ timestamp: Date.now(),
4669
+ direction: "response",
4670
+ status: res.status,
4671
+ statusText: res.statusText,
4672
+ headers: Object.fromEntries(res.headers.entries()),
4673
+ body: responseBody
4674
+ });
4675
+ }
4676
+ if (ct.includes("text/event-stream")) {
4677
+ const upstreamBody = res.body;
4678
+ if (!upstreamBody) {
4679
+ return withCORS(
4680
+ new Response(null, { status: res.status, headers: res.headers })
4681
+ );
4682
+ }
4683
+ const encoder = new TextEncoder();
4684
+ const decoder = new TextDecoder();
4685
+ let buffer = "";
4686
+ let lastEventType = null;
4687
+ let currentEventData = [];
4688
+ const proxyBasePath = `/api/mcp/interceptor/${id}/proxy`;
4689
+ const xfProto = c.req.header("x-forwarded-proto");
4690
+ const xfHost = c.req.header("x-forwarded-host");
4691
+ const reqHost = xfHost || c.req.header("host");
4692
+ let reqProto = xfProto;
4693
+ if (!reqProto) {
4694
+ const originHeader = c.req.header("origin");
4695
+ if (originHeader && /^https:/i.test(originHeader)) reqProto = "https";
4696
+ }
4697
+ if (!reqProto) reqProto = "http";
4698
+ const proxyOrigin = reqHost ? `${reqProto}://${reqHost}` : new URL(c.req.url).origin;
4699
+ let upstreamOrigin = (() => {
4700
+ try {
4701
+ const u = new URL(res.url || upstreamUrl.toString());
4702
+ return `${u.protocol}//${u.host}`;
4703
+ } catch {
4704
+ try {
4705
+ return new URL(entry.targetUrl).origin;
4706
+ } catch {
4707
+ return "";
4708
+ }
4709
+ }
4710
+ })();
4711
+ const rewriteStream = new ReadableStream({
4712
+ async start(controller) {
4713
+ const reader = upstreamBody.getReader();
4714
+ try {
4715
+ while (true) {
4716
+ const { done, value } = await reader.read();
4717
+ if (done) break;
4718
+ buffer += decoder.decode(value, { stream: true });
4719
+ let idx;
4720
+ while ((idx = buffer.indexOf("\n")) !== -1) {
4721
+ const line = buffer.slice(0, idx + 1);
4722
+ buffer = buffer.slice(idx + 1);
4723
+ if (line.startsWith("event:")) {
4724
+ lastEventType = line.slice(6).trim();
4725
+ controller.enqueue(encoder.encode(line));
4726
+ } else if (line.startsWith("data:")) {
4727
+ const rawLine = line.slice(5);
4728
+ const trimmed = rawLine.trim();
4729
+ currentEventData.push(trimmed);
4730
+ let endpointUrl = null;
4731
+ try {
4732
+ if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
4733
+ const obj = JSON.parse(trimmed);
4734
+ endpointUrl = obj?.url || obj?.endpoint?.url || obj?.type === "endpoint" && (obj?.data?.url || obj?.data);
4735
+ }
4736
+ } catch {
4737
+ }
4738
+ if (!endpointUrl) {
4739
+ const str = trimmed;
4740
+ if (/message\?/.test(str) || /messages\?/.test(str)) {
4741
+ endpointUrl = str;
4742
+ }
4743
+ }
4744
+ if (endpointUrl) {
4745
+ try {
4746
+ const u = new URL(
4747
+ endpointUrl,
4748
+ upstreamOrigin || void 0
4749
+ );
4750
+ const sessionId = u.searchParams.get("sessionId") || u.searchParams.get("sid") || "";
4751
+ if (sessionId) {
4752
+ interceptorStore.setSessionEndpoint(
4753
+ id,
4754
+ sessionId,
4755
+ u.toString()
4756
+ );
4757
+ try {
4758
+ console.log("[proxy] mapped session", {
4759
+ id,
4760
+ sessionId,
4761
+ upstream: u.toString()
4762
+ });
4763
+ } catch {
4764
+ }
4765
+ }
4766
+ const proxyEndpoint = `${proxyOrigin}${proxyBasePath}/messages${u.search}`;
4767
+ controller.enqueue(encoder.encode(`event: endpoint
4768
+ `));
4769
+ controller.enqueue(
4770
+ encoder.encode(`data: ${proxyEndpoint}
4771
+
4772
+ `)
4773
+ );
4774
+ currentEventData = [];
4775
+ continue;
4776
+ } catch {
4777
+ }
4778
+ }
4779
+ controller.enqueue(encoder.encode(line));
4780
+ } else if (line === "\n") {
4781
+ if (lastEventType === "message") {
4782
+ const dataText = currentEventData.join("\n");
4783
+ let bodyText = dataText;
4784
+ try {
4785
+ const parsed = JSON.parse(dataText);
4786
+ bodyText = JSON.stringify(parsed);
4787
+ } catch {
4788
+ }
4789
+ try {
4790
+ interceptorStore.appendLog(id, {
4791
+ id: `${requestId}-sse-${Date.now()}`,
4792
+ timestamp: Date.now(),
4793
+ direction: "response",
4794
+ status: 200,
4795
+ statusText: "SSE message",
4796
+ headers: { "content-type": "text/event-stream" },
4797
+ body: bodyText
4798
+ });
4799
+ } catch {
4800
+ }
4801
+ }
4802
+ currentEventData = [];
4803
+ lastEventType = null;
4804
+ controller.enqueue(encoder.encode(line));
4805
+ } else {
4806
+ controller.enqueue(encoder.encode(line));
4807
+ }
4808
+ }
4809
+ }
4810
+ if (buffer.length) controller.enqueue(encoder.encode(buffer));
4811
+ } finally {
4812
+ try {
4813
+ controller.close();
4814
+ } catch {
4815
+ }
4816
+ }
4817
+ }
4818
+ });
4819
+ const headers = new Headers(res.headers);
4820
+ headers.delete("content-length");
4821
+ headers.delete("Content-Length");
4822
+ headers.delete("transfer-encoding");
4823
+ headers.delete("Transfer-Encoding");
4824
+ headers.set("Cache-Control", "no-cache");
4825
+ headers.set("Connection", "keep-alive");
4826
+ return withCORS(
4827
+ new Response(rewriteStream, {
4828
+ status: res.status,
4829
+ statusText: res.statusText,
4830
+ headers
4831
+ })
4832
+ );
4833
+ }
4834
+ const nonSseHeaders = new Headers(res.headers);
4835
+ nonSseHeaders.delete("content-length");
4836
+ nonSseHeaders.delete("Content-Length");
4837
+ const passthrough = new Response(res.body, {
4838
+ status: res.status,
4839
+ statusText: res.statusText,
4840
+ headers: nonSseHeaders
4841
+ });
4842
+ return withCORS(passthrough);
4843
+ } catch (error) {
4844
+ const body = JSON.stringify({ error: String(error) });
4845
+ interceptorStore.appendLog(id, {
4846
+ id: `${requestId}-err`,
4847
+ timestamp: Date.now(),
4848
+ direction: "response",
4849
+ status: 500,
4850
+ statusText: "Proxy Error",
4851
+ headers: { "content-type": "application/json" },
4852
+ body
4853
+ });
4854
+ return withCORS(
4855
+ new Response(body, {
4856
+ status: 500,
4857
+ headers: { "Content-Type": "application/json" }
4858
+ })
4859
+ );
4860
+ }
4861
+ }
4862
+ interceptor.all("/:id/proxy", handleProxy);
4863
+ interceptor.all("/:id/proxy/*", handleProxy);
4864
+ var interceptor_default = interceptor;
4865
+
4866
+ // routes/mcp/http-adapters.ts
4867
+ import { Hono as Hono11 } from "hono";
4868
+
4869
+ // services/mcp-http-bridge.ts
4870
+ import { z as z11 } from "zod";
4871
+ import { zodToJsonSchema as zodToJsonSchema4 } from "zod-to-json-schema";
4872
+ function buildInitializeResult(serverId, mode) {
4873
+ if (mode === "adapter") {
4874
+ return {
4875
+ protocolVersion: "2025-06-18",
4876
+ capabilities: {
4877
+ tools: { listChanged: true },
4878
+ prompts: {},
4879
+ resources: { listChanged: true, subscribe: true },
4880
+ logging: {},
4881
+ roots: { listChanged: true }
4882
+ },
4883
+ serverInfo: { name: serverId, version: "stdio-adapter" }
4884
+ };
4885
+ }
4886
+ return {
4887
+ protocolVersion: "2025-06-18",
4888
+ capabilities: {
4889
+ tools: true,
4890
+ prompts: true,
4891
+ resources: true,
4892
+ logging: false,
4893
+ elicitation: {},
4894
+ roots: { listChanged: true }
4895
+ },
4896
+ serverInfo: { name: serverId, version: "mcpjam-proxy" }
4897
+ };
4898
+ }
4899
+ function toJsonSchemaMaybe(schema) {
4900
+ try {
4901
+ if (schema && typeof schema === "object") {
4902
+ if (schema instanceof z11.ZodType || "_def" in schema && "parse" in schema) {
4903
+ return zodToJsonSchema4(schema);
4904
+ }
4905
+ }
4906
+ } catch {
4907
+ }
4908
+ return schema;
4909
+ }
4910
+ async function handleJsonRpc(serverId, body, clientManager, mode) {
4911
+ const id = body?.id ?? null;
4912
+ const method = body?.method;
4913
+ const params = body?.params ?? {};
4914
+ if (!method || method.startsWith("notifications/")) {
4915
+ return null;
4916
+ }
4917
+ const respond = (payload) => ({ jsonrpc: "2.0", id, ...payload });
4918
+ try {
4919
+ switch (method) {
4920
+ case "ping":
4921
+ return respond({ result: {} });
4922
+ case "initialize": {
4923
+ const result = buildInitializeResult(serverId, mode);
4924
+ return respond({ result });
4925
+ }
4926
+ case "tools/list": {
4927
+ const toolsets = await clientManager.getToolsetsForServer(serverId);
4928
+ const tools2 = Object.keys(toolsets).map((name17) => ({
4929
+ name: name17,
4930
+ description: toolsets[name17].description,
4931
+ inputSchema: toJsonSchemaMaybe(toolsets[name17].inputSchema),
4932
+ outputSchema: toJsonSchemaMaybe(toolsets[name17].outputSchema)
4933
+ }));
4934
+ return respond({ result: { tools: tools2 } });
4935
+ }
4936
+ case "tools/call": {
4937
+ try {
4938
+ const exec = await clientManager.executeToolDirect(
4939
+ `${serverId}:${params?.name}`,
4940
+ params?.arguments || {}
4941
+ );
4942
+ if (mode === "manager") {
4943
+ const result = {
4944
+ content: [
4945
+ {
4946
+ type: "text",
4947
+ text: typeof exec.result === "string" ? exec.result : JSON.stringify(exec.result, null, 2)
4948
+ }
4949
+ ],
4950
+ isError: false
4951
+ };
4952
+ return respond({ result });
4953
+ }
4954
+ return respond({ result: exec.result });
4955
+ } catch (e) {
4956
+ if (mode === "manager") {
4957
+ const result = {
4958
+ content: [
4959
+ { type: "text", text: `Error: ${e?.message || String(e)}` }
4960
+ ],
4961
+ isError: true
4962
+ };
4963
+ return respond({ result });
4964
+ }
4965
+ return respond({
4966
+ error: { code: -32e3, message: e?.message || String(e) }
4967
+ });
4968
+ }
4969
+ }
4970
+ case "resources/list": {
4971
+ const resources2 = clientManager.getResourcesForServer(serverId).map((r) => ({
4972
+ uri: r.uri,
4973
+ name: r.name,
4974
+ description: r.description,
4975
+ mimeType: r.mimeType
4976
+ }));
4977
+ return respond({ result: { resources: resources2 } });
4978
+ }
4979
+ case "resources/read": {
4980
+ try {
4981
+ const content = await clientManager.getResource(
4982
+ params?.uri,
4983
+ serverId
4984
+ );
4985
+ if (mode === "manager") {
4986
+ const result = {
4987
+ contents: [
4988
+ {
4989
+ uri: params?.uri,
4990
+ mimeType: content?.mimeType || "text/plain",
4991
+ text: typeof content === "string" ? content : JSON.stringify(content, null, 2)
4992
+ }
4993
+ ]
4994
+ };
4995
+ return respond({ result });
4996
+ }
4997
+ return respond({ result: content });
4998
+ } catch (e) {
4999
+ return respond({
5000
+ error: { code: -32e3, message: e?.message || String(e) }
5001
+ });
5002
+ }
5003
+ }
5004
+ case "prompts/list": {
5005
+ const prompts2 = clientManager.getPromptsForServer(serverId).map((p) => ({
5006
+ name: p.name,
5007
+ description: p.description,
5008
+ arguments: p.arguments
5009
+ }));
5010
+ return respond({ result: { prompts: prompts2 } });
5011
+ }
5012
+ case "prompts/get": {
5013
+ try {
5014
+ const content = await clientManager.getPrompt(
5015
+ params?.name,
5016
+ serverId,
5017
+ params?.arguments || {}
5018
+ );
5019
+ if (mode === "manager") {
5020
+ const result = {
5021
+ description: content?.description || `Prompt: ${params?.name}`,
5022
+ messages: [
5023
+ {
5024
+ role: "user",
5025
+ content: {
5026
+ type: "text",
5027
+ text: typeof content === "string" ? content : JSON.stringify(content, null, 2)
5028
+ }
5029
+ }
5030
+ ]
5031
+ };
5032
+ return respond({ result });
5033
+ }
5034
+ return respond({ result: content });
5035
+ } catch (e) {
5036
+ return respond({
5037
+ error: { code: -32e3, message: e?.message || String(e) }
5038
+ });
5039
+ }
5040
+ }
5041
+ case "roots/list": {
5042
+ return respond({ result: { roots: [] } });
5043
+ }
5044
+ case "logging/setLevel": {
5045
+ return respond({ result: { success: true } });
5046
+ }
5047
+ default: {
5048
+ return respond({
5049
+ error: { code: -32601, message: `Method not implemented: ${method}` }
5050
+ });
5051
+ }
5052
+ }
5053
+ } catch (e) {
5054
+ return respond({
5055
+ error: { code: -32e3, message: e?.message || String(e) }
5056
+ });
5057
+ }
5058
+ }
5059
+
5060
+ // routes/mcp/http-adapters.ts
5061
+ var sessions = /* @__PURE__ */ new Map();
5062
+ var latestSessionByServer = /* @__PURE__ */ new Map();
5063
+ function createHttpHandler(mode, routePrefix) {
5064
+ const router = new Hono11();
5065
+ router.options(
5066
+ "/:serverId",
5067
+ (c) => c.body(null, 204, {
5068
+ "Access-Control-Allow-Origin": "*",
5069
+ "Access-Control-Allow-Methods": "GET,POST,HEAD,OPTIONS",
5070
+ "Access-Control-Allow-Headers": "*, Authorization, Content-Type, Accept, Accept-Language",
5071
+ "Access-Control-Expose-Headers": "*",
5072
+ "Access-Control-Max-Age": "86400"
5073
+ })
5074
+ );
5075
+ router.options(
5076
+ "/:serverId/*",
5077
+ (c) => c.body(null, 204, {
5078
+ "Access-Control-Allow-Origin": "*",
5079
+ "Access-Control-Allow-Methods": "GET,POST,HEAD,OPTIONS",
5080
+ "Access-Control-Allow-Headers": "*, Authorization, Content-Type, Accept, Accept-Language",
5081
+ "Access-Control-Expose-Headers": "*",
5082
+ "Access-Control-Max-Age": "86400"
5083
+ })
5084
+ );
5085
+ async function handleHttp(c) {
5086
+ const serverId = c.req.param("serverId");
5087
+ const method = c.req.method;
5088
+ if (method === "HEAD") {
5089
+ return c.body(null, 200, {
5090
+ "Content-Type": "text/event-stream",
5091
+ "Cache-Control": "no-cache",
5092
+ Connection: "keep-alive",
5093
+ "Access-Control-Allow-Origin": "*",
5094
+ "X-Accel-Buffering": "no"
5095
+ });
5096
+ }
5097
+ if (method === "GET") {
5098
+ const encoder = new TextEncoder();
5099
+ const incomingUrl = new URL(c.req.url);
5100
+ const overrideBase = c.req.header("x-mcpjam-endpoint-base");
5101
+ let endpointBase;
5102
+ if (overrideBase && overrideBase.trim() !== "") {
5103
+ endpointBase = overrideBase.trim();
5104
+ } else {
5105
+ const xfProto = c.req.header("x-forwarded-proto");
5106
+ const xfHost = c.req.header("x-forwarded-host");
5107
+ const host = xfHost || c.req.header("host");
5108
+ let proto = xfProto;
5109
+ if (!proto) {
5110
+ const originHeader = c.req.header("origin");
5111
+ if (originHeader && /^https:/i.test(originHeader)) proto = "https";
5112
+ }
5113
+ if (!proto) proto = "http";
5114
+ const origin = host ? `${proto}://${host}` : incomingUrl.origin;
5115
+ endpointBase = `${origin}/api/mcp/${routePrefix}/${serverId}/messages`;
5116
+ }
5117
+ const sessionId = crypto.randomUUID();
5118
+ let timer;
5119
+ const stream = new ReadableStream({
5120
+ start(controller) {
5121
+ console.log(`[${routePrefix}] SSE open`, { serverId, sessionId });
5122
+ const send = (event, data) => {
5123
+ controller.enqueue(encoder.encode(`event: ${event}
5124
+ `));
5125
+ controller.enqueue(encoder.encode(`data: ${data}
5126
+
5127
+ `));
5128
+ };
5129
+ const close = () => {
5130
+ try {
5131
+ controller.close();
5132
+ } catch {
5133
+ }
5134
+ };
5135
+ sessions.set(`${serverId}:${sessionId}`, { send, close });
5136
+ latestSessionByServer.set(serverId, sessionId);
5137
+ console.log(`[${routePrefix}] session registered`, {
5138
+ key: `${serverId}:${sessionId}`
5139
+ });
5140
+ send("ping", "");
5141
+ const sep = endpointBase.includes("?") ? "&" : "?";
5142
+ const url = `${endpointBase}${sep}sessionId=${sessionId}`;
5143
+ console.log(`[${routePrefix}] endpoint`, {
5144
+ serverId,
5145
+ sessionId,
5146
+ url
5147
+ });
5148
+ try {
5149
+ send("endpoint", JSON.stringify({ url, headers: {} }));
5150
+ } catch {
5151
+ }
5152
+ try {
5153
+ send("endpoint", url);
5154
+ } catch {
5155
+ }
5156
+ timer = setInterval(() => {
5157
+ try {
5158
+ controller.enqueue(
5159
+ encoder.encode(`: keepalive ${Date.now()}
5160
+
5161
+ `)
5162
+ );
5163
+ } catch {
5164
+ }
5165
+ }, 15e3);
5166
+ },
5167
+ cancel() {
5168
+ try {
5169
+ clearInterval(timer);
5170
+ } catch {
5171
+ }
5172
+ console.log(`[${routePrefix}] SSE close`, { serverId, sessionId });
5173
+ sessions.delete(`${serverId}:${sessionId}`);
5174
+ if (latestSessionByServer.get(serverId) === sessionId) {
5175
+ latestSessionByServer.delete(serverId);
5176
+ }
5177
+ }
5178
+ });
5179
+ return c.body(stream, 200, {
5180
+ "Content-Type": "text/event-stream",
5181
+ "Cache-Control": "no-cache",
5182
+ Connection: "keep-alive",
5183
+ "Access-Control-Allow-Origin": "*",
5184
+ "Access-Control-Expose-Headers": "*",
5185
+ "X-Accel-Buffering": "no",
5186
+ "Transfer-Encoding": "chunked"
5187
+ });
5188
+ }
5189
+ if (method !== "POST") {
5190
+ return c.json({ error: "Unsupported request" }, 400);
5191
+ }
5192
+ let body = void 0;
5193
+ try {
5194
+ body = await c.req.json();
5195
+ } catch {
5196
+ }
5197
+ const clientManager = c.mcpJamClientManager;
5198
+ const response = await handleJsonRpc(
5199
+ serverId,
5200
+ body,
5201
+ clientManager,
5202
+ mode
5203
+ );
5204
+ if (!response) {
5205
+ return c.body("Accepted", 202, { "Access-Control-Allow-Origin": "*" });
5206
+ }
5207
+ return c.body(JSON.stringify(response), 200, {
5208
+ "Content-Type": "application/json",
5209
+ "Access-Control-Allow-Origin": "*",
5210
+ "Access-Control-Expose-Headers": "*"
5211
+ });
5212
+ }
5213
+ router.post("/:serverId/messages", async (c) => {
5214
+ const serverId = c.req.param("serverId");
5215
+ const url = new URL(c.req.url);
5216
+ const sessionId = url.searchParams.get("sessionId") || "";
5217
+ const key = `${serverId}:${sessionId}`;
5218
+ let sess = sessions.get(key);
5219
+ if (!sess) {
5220
+ const fallbackId = latestSessionByServer.get(serverId);
5221
+ if (fallbackId) {
5222
+ sess = sessions.get(`${serverId}:${fallbackId}`);
5223
+ }
5224
+ }
5225
+ console.log(`[${routePrefix}] POST messages`, {
5226
+ key,
5227
+ resolved: !!sess,
5228
+ contentType: c.req.header("content-type")
5229
+ });
5230
+ if (!sess) {
5231
+ return c.json({ error: "Invalid session" }, 400);
5232
+ }
5233
+ let body;
5234
+ try {
5235
+ body = await c.req.json();
5236
+ } catch {
5237
+ try {
5238
+ const txt = await c.req.text();
5239
+ body = txt ? JSON.parse(txt) : void 0;
5240
+ } catch {
5241
+ body = void 0;
5242
+ }
5243
+ }
5244
+ const id = body?.id ?? null;
5245
+ const method = body?.method;
5246
+ const params = body?.params ?? {};
5247
+ try {
5248
+ const responseMessage = await handleJsonRpc(
5249
+ serverId,
5250
+ { id, method, params },
5251
+ c.mcpJamClientManager,
5252
+ mode
5253
+ );
5254
+ if (responseMessage) {
5255
+ try {
5256
+ console.log(`[${routePrefix}] emit message`, {
5257
+ key,
5258
+ id: responseMessage.id,
5259
+ method
5260
+ });
5261
+ sess.send("message", JSON.stringify(responseMessage));
5262
+ } catch {
5263
+ }
5264
+ }
5265
+ return c.body("Accepted", 202, {
5266
+ "Access-Control-Allow-Origin": "*",
5267
+ "Access-Control-Expose-Headers": "*"
5268
+ });
5269
+ } catch (e) {
5270
+ return c.body("Error", 400, {
5271
+ "Access-Control-Allow-Origin": "*",
5272
+ "Access-Control-Expose-Headers": "*"
5273
+ });
5274
+ }
5275
+ });
5276
+ router.all("/:serverId", handleHttp);
5277
+ router.all("/:serverId/*", handleHttp);
5278
+ return router;
5279
+ }
5280
+ var adapterHttp = createHttpHandler("adapter", "adapter-http");
5281
+ var managerHttp = createHttpHandler("manager", "manager-http");
5282
+
3984
5283
  // routes/mcp/index.ts
3985
- var mcp = new Hono10();
5284
+ var mcp = new Hono12();
3986
5285
  mcp.get("/health", (c) => {
3987
5286
  return c.json({
3988
5287
  service: "MCP API",
@@ -3999,6 +5298,9 @@ mcp.route("/resources", resources_default);
3999
5298
  mcp.route("/prompts", prompts_default);
4000
5299
  mcp.route("/oauth", oauth_default);
4001
5300
  mcp.route("/export", export_default);
5301
+ mcp.route("/interceptor", interceptor_default);
5302
+ mcp.route("/adapter-http", adapterHttp);
5303
+ mcp.route("/manager-http", managerHttp);
4002
5304
  var mcp_default = mcp;
4003
5305
 
4004
5306
  // services/mcpjam-client-manager.ts
@@ -4284,6 +5586,7 @@ var MCPJamClientManager = class {
4284
5586
  let lastError = void 0;
4285
5587
  for (const args of attempts) {
4286
5588
  try {
5589
+ console.log("args", args);
4287
5590
  const result = await tool.execute(args);
4288
5591
  if (result && result.isError) {
4289
5592
  const errorText = result.content && result.content[0] && result.content[0].text ? result.content[0].text : "Unknown error";
@@ -4452,7 +5755,7 @@ try {
4452
5755
  fixPath();
4453
5756
  } catch {
4454
5757
  }
4455
- var app = new Hono11();
5758
+ var app = new Hono13();
4456
5759
  var mcpJamClientManager = new MCPJamClientManager();
4457
5760
  app.use("*", async (c, next) => {
4458
5761
  c.mcpJamClientManager = mcpJamClientManager;
@@ -4473,6 +5776,95 @@ app.use(
4473
5776
  })
4474
5777
  );
4475
5778
  app.route("/api/mcp", mcp_default);
5779
+ app.options("/sse/message", (c) => {
5780
+ return c.body(null, 204, {
5781
+ "Access-Control-Allow-Origin": "*",
5782
+ "Access-Control-Allow-Methods": "POST,OPTIONS",
5783
+ "Access-Control-Allow-Headers": "Authorization, Content-Type, Accept, Accept-Language",
5784
+ "Access-Control-Max-Age": "86400",
5785
+ Vary: "Origin, Access-Control-Request-Headers"
5786
+ });
5787
+ });
5788
+ app.post("/sse/message", async (c) => {
5789
+ try {
5790
+ const url = new URL(c.req.url);
5791
+ const sessionId = url.searchParams.get("sessionId") || url.searchParams.get("sid") || "";
5792
+ if (!sessionId) {
5793
+ return c.json({ error: "Missing sessionId" }, 400);
5794
+ }
5795
+ const mapping = interceptorStore.getSessionMapping(sessionId);
5796
+ if (!mapping) {
5797
+ return c.json({ error: "Unknown sessionId" }, 404);
5798
+ }
5799
+ const entry = interceptorStore.get(mapping.interceptorId);
5800
+ if (!entry) {
5801
+ return c.json({ error: "Interceptor not found" }, 404);
5802
+ }
5803
+ let bodyText = "";
5804
+ try {
5805
+ bodyText = await c.req.text();
5806
+ } catch {
5807
+ }
5808
+ const headers = new Headers();
5809
+ c.req.raw.headers.forEach((v, k) => {
5810
+ const key = k.toLowerCase();
5811
+ if ([
5812
+ "connection",
5813
+ "keep-alive",
5814
+ "transfer-encoding",
5815
+ "upgrade",
5816
+ "proxy-authenticate",
5817
+ "proxy-authorization",
5818
+ "te",
5819
+ "trailer",
5820
+ "host",
5821
+ "content-length"
5822
+ ].includes(key))
5823
+ return;
5824
+ headers.set(k, v);
5825
+ });
5826
+ if (entry.injectHeaders) {
5827
+ for (const [k, v] of Object.entries(entry.injectHeaders)) {
5828
+ const key = k.toLowerCase();
5829
+ if ([
5830
+ "connection",
5831
+ "keep-alive",
5832
+ "transfer-encoding",
5833
+ "upgrade",
5834
+ "proxy-authenticate",
5835
+ "proxy-authorization",
5836
+ "te",
5837
+ "trailer",
5838
+ "host",
5839
+ "content-length"
5840
+ ].includes(key))
5841
+ continue;
5842
+ if (key === "authorization" && headers.has("authorization")) continue;
5843
+ headers.set(k, v);
5844
+ }
5845
+ }
5846
+ try {
5847
+ await fetch(
5848
+ new Request(mapping.url, { method: "POST", headers, body: bodyText })
5849
+ );
5850
+ } catch {
5851
+ }
5852
+ return c.body("Accepted", 202, {
5853
+ "Access-Control-Allow-Origin": "*",
5854
+ "Access-Control-Expose-Headers": "*"
5855
+ });
5856
+ } catch (e) {
5857
+ return c.body(
5858
+ JSON.stringify({ error: e?.message || "Forward error" }),
5859
+ 400,
5860
+ {
5861
+ "Content-Type": "application/json",
5862
+ "Access-Control-Allow-Origin": "*",
5863
+ "Access-Control-Expose-Headers": "*"
5864
+ }
5865
+ );
5866
+ }
5867
+ });
4476
5868
  app.get("/health", (c) => {
4477
5869
  return c.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
4478
5870
  });
@@ -4481,6 +5873,11 @@ app.get("/api/mcp-cli-config", (c) => {
4481
5873
  return c.json({ config: mcpConfig });
4482
5874
  });
4483
5875
  if (process.env.NODE_ENV === "production") {
5876
+ app.use("/*.png", serveStatic({ root: "./public" }));
5877
+ app.use("/*.svg", serveStatic({ root: "./public" }));
5878
+ app.use("/*.jpg", serveStatic({ root: "./public" }));
5879
+ app.use("/*.jpeg", serveStatic({ root: "./public" }));
5880
+ app.use("/*.ico", serveStatic({ root: "./public" }));
4484
5881
  app.use("/*", serveStatic({ root: "./dist/client" }));
4485
5882
  app.get("*", async (c) => {
4486
5883
  const path = c.req.path;