@mcp-rating/gateway 0.2.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.
Files changed (168) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +130 -0
  3. package/dist/ads/ad-tracker.d.ts +63 -0
  4. package/dist/ads/ad-tracker.js +144 -0
  5. package/dist/ads/ad-tracker.js.map +1 -0
  6. package/dist/analytics/usage-tracker.d.ts +67 -0
  7. package/dist/analytics/usage-tracker.js +148 -0
  8. package/dist/analytics/usage-tracker.js.map +1 -0
  9. package/dist/audit/audit-log.d.ts +71 -0
  10. package/dist/audit/audit-log.js +72 -0
  11. package/dist/audit/audit-log.js.map +1 -0
  12. package/dist/config/gateway-config.d.ts +6 -0
  13. package/dist/config/gateway-config.js +135 -0
  14. package/dist/config/gateway-config.js.map +1 -0
  15. package/dist/config/groups.d.ts +29 -0
  16. package/dist/config/groups.js +102 -0
  17. package/dist/config/groups.js.map +1 -0
  18. package/dist/config/profiles.d.ts +42 -0
  19. package/dist/config/profiles.js +167 -0
  20. package/dist/config/profiles.js.map +1 -0
  21. package/dist/config/types.d.ts +76 -0
  22. package/dist/config/types.js +2 -0
  23. package/dist/config/types.js.map +1 -0
  24. package/dist/connection/auto-installer.d.ts +13 -0
  25. package/dist/connection/auto-installer.js +61 -0
  26. package/dist/connection/auto-installer.js.map +1 -0
  27. package/dist/connection/connection-manager.d.ts +106 -0
  28. package/dist/connection/connection-manager.js +845 -0
  29. package/dist/connection/connection-manager.js.map +1 -0
  30. package/dist/connection/downstream-client.d.ts +50 -0
  31. package/dist/connection/downstream-client.js +170 -0
  32. package/dist/connection/downstream-client.js.map +1 -0
  33. package/dist/connection/http-client.d.ts +12 -0
  34. package/dist/connection/http-client.js +26 -0
  35. package/dist/connection/http-client.js.map +1 -0
  36. package/dist/connection/sse-client.d.ts +12 -0
  37. package/dist/connection/sse-client.js +26 -0
  38. package/dist/connection/sse-client.js.map +1 -0
  39. package/dist/connection/transport-factory.d.ts +31 -0
  40. package/dist/connection/transport-factory.js +67 -0
  41. package/dist/connection/transport-factory.js.map +1 -0
  42. package/dist/connection/types.d.ts +134 -0
  43. package/dist/connection/types.js +5 -0
  44. package/dist/connection/types.js.map +1 -0
  45. package/dist/connection/ws-client.d.ts +12 -0
  46. package/dist/connection/ws-client.js +26 -0
  47. package/dist/connection/ws-client.js.map +1 -0
  48. package/dist/discovery/auto-recommender.d.ts +51 -0
  49. package/dist/discovery/auto-recommender.js +176 -0
  50. package/dist/discovery/auto-recommender.js.map +1 -0
  51. package/dist/gateway-server.d.ts +42 -0
  52. package/dist/gateway-server.js +135 -0
  53. package/dist/gateway-server.js.map +1 -0
  54. package/dist/http-daemon.d.ts +26 -0
  55. package/dist/http-daemon.js +302 -0
  56. package/dist/http-daemon.js.map +1 -0
  57. package/dist/index.d.ts +2 -0
  58. package/dist/index.js +83 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/meta-tools/ad-status.d.ts +8 -0
  61. package/dist/meta-tools/ad-status.js +41 -0
  62. package/dist/meta-tools/ad-status.js.map +1 -0
  63. package/dist/meta-tools/audit.d.ts +12 -0
  64. package/dist/meta-tools/audit.js +98 -0
  65. package/dist/meta-tools/audit.js.map +1 -0
  66. package/dist/meta-tools/call-tool.d.ts +11 -0
  67. package/dist/meta-tools/call-tool.js +65 -0
  68. package/dist/meta-tools/call-tool.js.map +1 -0
  69. package/dist/meta-tools/connect.d.ts +3 -0
  70. package/dist/meta-tools/connect.js +105 -0
  71. package/dist/meta-tools/connect.js.map +1 -0
  72. package/dist/meta-tools/disconnect.d.ts +3 -0
  73. package/dist/meta-tools/disconnect.js +30 -0
  74. package/dist/meta-tools/disconnect.js.map +1 -0
  75. package/dist/meta-tools/discover.d.ts +3 -0
  76. package/dist/meta-tools/discover.js +97 -0
  77. package/dist/meta-tools/discover.js.map +1 -0
  78. package/dist/meta-tools/groups.d.ts +7 -0
  79. package/dist/meta-tools/groups.js +237 -0
  80. package/dist/meta-tools/groups.js.map +1 -0
  81. package/dist/meta-tools/health.d.ts +4 -0
  82. package/dist/meta-tools/health.js +76 -0
  83. package/dist/meta-tools/health.js.map +1 -0
  84. package/dist/meta-tools/index.d.ts +28 -0
  85. package/dist/meta-tools/index.js +56 -0
  86. package/dist/meta-tools/index.js.map +1 -0
  87. package/dist/meta-tools/list-active.d.ts +3 -0
  88. package/dist/meta-tools/list-active.js +50 -0
  89. package/dist/meta-tools/list-active.js.map +1 -0
  90. package/dist/meta-tools/profiles.d.ts +6 -0
  91. package/dist/meta-tools/profiles.js +197 -0
  92. package/dist/meta-tools/profiles.js.map +1 -0
  93. package/dist/meta-tools/recommend.d.ts +14 -0
  94. package/dist/meta-tools/recommend.js +74 -0
  95. package/dist/meta-tools/recommend.js.map +1 -0
  96. package/dist/meta-tools/sandbox.d.ts +12 -0
  97. package/dist/meta-tools/sandbox.js +125 -0
  98. package/dist/meta-tools/sandbox.js.map +1 -0
  99. package/dist/meta-tools/server-info.d.ts +4 -0
  100. package/dist/meta-tools/server-info.js +156 -0
  101. package/dist/meta-tools/server-info.js.map +1 -0
  102. package/dist/meta-tools/usage.d.ts +6 -0
  103. package/dist/meta-tools/usage.js +149 -0
  104. package/dist/meta-tools/usage.js.map +1 -0
  105. package/dist/permissions/permission-manager.d.ts +26 -0
  106. package/dist/permissions/permission-manager.js +73 -0
  107. package/dist/permissions/permission-manager.js.map +1 -0
  108. package/dist/permissions/trust-tiers.d.ts +18 -0
  109. package/dist/permissions/trust-tiers.js +42 -0
  110. package/dist/permissions/trust-tiers.js.map +1 -0
  111. package/dist/plugins/builtin/logging-plugin.d.ts +6 -0
  112. package/dist/plugins/builtin/logging-plugin.js +41 -0
  113. package/dist/plugins/builtin/logging-plugin.js.map +1 -0
  114. package/dist/plugins/plugin-manager.d.ts +65 -0
  115. package/dist/plugins/plugin-manager.js +137 -0
  116. package/dist/plugins/plugin-manager.js.map +1 -0
  117. package/dist/proxy/prime-handlers.d.ts +27 -0
  118. package/dist/proxy/prime-handlers.js +47 -0
  119. package/dist/proxy/prime-handlers.js.map +1 -0
  120. package/dist/proxy/progress-forwarder.d.ts +44 -0
  121. package/dist/proxy/progress-forwarder.js +40 -0
  122. package/dist/proxy/progress-forwarder.js.map +1 -0
  123. package/dist/proxy/prompt-router.d.ts +25 -0
  124. package/dist/proxy/prompt-router.js +110 -0
  125. package/dist/proxy/prompt-router.js.map +1 -0
  126. package/dist/proxy/resource-router.d.ts +25 -0
  127. package/dist/proxy/resource-router.js +91 -0
  128. package/dist/proxy/resource-router.js.map +1 -0
  129. package/dist/proxy/schema-adapter.d.ts +11 -0
  130. package/dist/proxy/schema-adapter.js +36 -0
  131. package/dist/proxy/schema-adapter.js.map +1 -0
  132. package/dist/proxy/task-proxy.d.ts +28 -0
  133. package/dist/proxy/task-proxy.js +186 -0
  134. package/dist/proxy/task-proxy.js.map +1 -0
  135. package/dist/proxy/tool-router.d.ts +38 -0
  136. package/dist/proxy/tool-router.js +229 -0
  137. package/dist/proxy/tool-router.js.map +1 -0
  138. package/dist/proxy/ui-detect.d.ts +22 -0
  139. package/dist/proxy/ui-detect.js +30 -0
  140. package/dist/proxy/ui-detect.js.map +1 -0
  141. package/dist/registry/registry-client.d.ts +55 -0
  142. package/dist/registry/registry-client.js +211 -0
  143. package/dist/registry/registry-client.js.map +1 -0
  144. package/dist/registry/types.d.ts +69 -0
  145. package/dist/registry/types.js +2 -0
  146. package/dist/registry/types.js.map +1 -0
  147. package/dist/sandbox/container-runtime.d.ts +91 -0
  148. package/dist/sandbox/container-runtime.js +178 -0
  149. package/dist/sandbox/container-runtime.js.map +1 -0
  150. package/dist/sandbox/egress-proxy.d.ts +51 -0
  151. package/dist/sandbox/egress-proxy.js +146 -0
  152. package/dist/sandbox/egress-proxy.js.map +1 -0
  153. package/dist/sandbox/env-scoper.d.ts +29 -0
  154. package/dist/sandbox/env-scoper.js +60 -0
  155. package/dist/sandbox/env-scoper.js.map +1 -0
  156. package/dist/sandbox/manifest-resolver.d.ts +40 -0
  157. package/dist/sandbox/manifest-resolver.js +185 -0
  158. package/dist/sandbox/manifest-resolver.js.map +1 -0
  159. package/dist/sandbox/types.d.ts +88 -0
  160. package/dist/sandbox/types.js +21 -0
  161. package/dist/sandbox/types.js.map +1 -0
  162. package/dist/utils/errors.d.ts +39 -0
  163. package/dist/utils/errors.js +67 -0
  164. package/dist/utils/errors.js.map +1 -0
  165. package/dist/utils/logger.d.ts +16 -0
  166. package/dist/utils/logger.js +51 -0
  167. package/dist/utils/logger.js.map +1 -0
  168. package/package.json +72 -0
@@ -0,0 +1,91 @@
1
+ import { TRUST_LABELS } from "../permissions/trust-tiers.js";
2
+ import { log } from "../utils/logger.js";
3
+ /**
4
+ * Register proxied resources on the gateway McpServer for a downstream connection.
5
+ *
6
+ * Each downstream resource is registered with:
7
+ * - Namespaced URI: `slug://original-uri`
8
+ * - Description prefixed with trust tier and server name
9
+ * - Read handler that forwards to downstream `client.readResource()`
10
+ *
11
+ * Returns a map of namespaced URI → { remove } handle for cleanup.
12
+ */
13
+ export function registerProxiedResources(mcpServer, connection, downstreamResources) {
14
+ const registered = new Map();
15
+ const trustLabel = TRUST_LABELS[connection.trustTier];
16
+ for (const resource of downstreamResources) {
17
+ const nsUri = namespaceResourceUri(connection.slug, resource.uri);
18
+ const description = `[${trustLabel}] [${connection.displayName}] ${resource.description || resource.name}`;
19
+ log.debug("Registering proxied resource", {
20
+ nsUri,
21
+ downstream: resource.uri,
22
+ server: connection.slug,
23
+ });
24
+ try {
25
+ const handle = mcpServer.resource(resource.name, nsUri, {
26
+ description,
27
+ mimeType: resource.mimeType,
28
+ }, async () => {
29
+ log.debug("Proxying resource read", {
30
+ nsUri,
31
+ downstream: resource.uri,
32
+ server: connection.slug,
33
+ });
34
+ try {
35
+ const result = await connection.client.readResource({
36
+ uri: resource.uri,
37
+ });
38
+ return result;
39
+ }
40
+ catch (err) {
41
+ const msg = err instanceof Error ? err.message : String(err);
42
+ log.error("Proxied resource read failed", {
43
+ nsUri,
44
+ error: msg,
45
+ });
46
+ return {
47
+ contents: [
48
+ {
49
+ uri: nsUri,
50
+ text: `Error reading resource from ${connection.displayName}: ${msg}`,
51
+ },
52
+ ],
53
+ };
54
+ }
55
+ });
56
+ registered.set(nsUri, handle);
57
+ }
58
+ catch (err) {
59
+ log.warn("Failed to register proxied resource", {
60
+ nsUri,
61
+ error: String(err),
62
+ });
63
+ }
64
+ }
65
+ return registered;
66
+ }
67
+ /**
68
+ * Remove all proxied resources for a connection.
69
+ */
70
+ export function removeProxiedResources(proxies) {
71
+ for (const [uri, handle] of proxies) {
72
+ log.debug("Removing proxied resource", { uri });
73
+ handle.remove();
74
+ }
75
+ proxies.clear();
76
+ }
77
+ /**
78
+ * Namespace a resource URI: `slug://original-uri-path`
79
+ */
80
+ export function namespaceResourceUri(slug, originalUri) {
81
+ // If the original URI has a scheme, replace it with slug://
82
+ try {
83
+ const parsed = new URL(originalUri);
84
+ return `${slug}://${parsed.host}${parsed.pathname}${parsed.search}${parsed.hash}`;
85
+ }
86
+ catch {
87
+ // Not a valid URL — just prepend the slug scheme
88
+ return `${slug}://${originalUri}`;
89
+ }
90
+ }
91
+ //# sourceMappingURL=resource-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-router.js","sourceRoot":"","sources":["../../src/proxy/resource-router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Build a permissive Zod object schema from a downstream tool's JSON Schema.
4
+ *
5
+ * The gateway does NOT enforce types — it preserves property names so the
6
+ * host client can display them, but accepts any value for each property.
7
+ * The downstream server handles its own validation.
8
+ *
9
+ * If the input schema has no properties, returns an empty passthrough schema.
10
+ */
11
+ export declare function buildPassthroughSchema(jsonSchema: Record<string, unknown>): Record<string, z.ZodTypeAny>;
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Build a permissive Zod object schema from a downstream tool's JSON Schema.
4
+ *
5
+ * The gateway does NOT enforce types — it preserves property names so the
6
+ * host client can display them, but accepts any value for each property.
7
+ * The downstream server handles its own validation.
8
+ *
9
+ * If the input schema has no properties, returns an empty passthrough schema.
10
+ */
11
+ export function buildPassthroughSchema(jsonSchema) {
12
+ const properties = jsonSchema?.properties;
13
+ if (!properties || typeof properties !== "object") {
14
+ // No known properties — accept arbitrary args
15
+ return {};
16
+ }
17
+ const shape = {};
18
+ const required = new Set(Array.isArray(jsonSchema.required) ? jsonSchema.required : []);
19
+ for (const [key, propSchema] of Object.entries(properties)) {
20
+ // Build a Zod type that accepts any value but carries the description
21
+ const desc = typeof propSchema.description === "string"
22
+ ? propSchema.description
23
+ : undefined;
24
+ let field = z.any();
25
+ if (desc) {
26
+ field = field.describe(desc);
27
+ }
28
+ // Mark optional if not in required array
29
+ if (!required.has(key)) {
30
+ field = field.optional();
31
+ }
32
+ shape[key] = field;
33
+ }
34
+ return shape;
35
+ }
36
+ //# sourceMappingURL=schema-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-adapter.js","sourceRoot":"","sources":["../../src/proxy/schema-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CACpC,UAAmC;IAEnC,MAAM,UAAU,GAAG,UAAU,EAAE,UAElB,CAAC;IAEd,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QAClD,8CAA8C;QAC9C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAAiC,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAG,IAAI,GAAG,CACtB,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAC9D,CAAC;IAEF,KAAK,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3D,sEAAsE;QACtE,MAAM,IAAI,GACR,OAAO,UAAU,CAAC,WAAW,KAAK,QAAQ;YACxC,CAAC,CAAC,UAAU,CAAC,WAAW;YACxB,CAAC,CAAC,SAAS,CAAC;QAEhB,IAAI,KAAK,GAAiB,CAAC,CAAC,GAAG,EAAE,CAAC;QAClC,IAAI,IAAI,EAAE,CAAC;YACT,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;QAED,yCAAyC;QACzC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC3B,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { DownstreamConnection, DownstreamTool } from "../connection/types.js";
3
+ import type { UsageTracker } from "../analytics/usage-tracker.js";
4
+ import type { AdTracker } from "../ads/ad-tracker.js";
5
+ /**
6
+ * Returns true if the downstream tool runs as an experimental MCP task
7
+ * (taskSupport "optional" or "required"), meaning it must be proxied
8
+ * through the task lifecycle rather than a plain `tools/call`.
9
+ */
10
+ export declare function isTaskTool(tool: DownstreamTool): boolean;
11
+ /**
12
+ * Register a task-capable downstream tool on the gateway as an upstream
13
+ * task-tool.
14
+ *
15
+ * Bridging model:
16
+ * - `createTask`: starts the downstream tool via `callToolStream`, creates a
17
+ * matching task in the gateway's upstream task store, and drives the
18
+ * downstream stream in the background — mirroring status updates and the
19
+ * final result/error into the upstream store.
20
+ * - `getTask` / `getTaskResult`: read straight from the upstream store, which
21
+ * the background loop keeps current. No manual task-ID mapping is needed
22
+ * because the upstream store owns the upstream task identity.
23
+ *
24
+ * Returns a `{ remove }` handle for cleanup, matching the plain-tool path.
25
+ */
26
+ export declare function registerTaskProxyTool(mcpServer: McpServer, connection: DownstreamConnection, tool: DownstreamTool, proxyTimeoutMs: number, usageTracker?: UsageTracker, adTracker?: AdTracker | null): {
27
+ remove: () => void;
28
+ };
@@ -0,0 +1,186 @@
1
+ import { buildPassthroughSchema } from "./schema-adapter.js";
2
+ import { TRUST_LABELS } from "../permissions/trust-tiers.js";
3
+ import { log } from "../utils/logger.js";
4
+ const SEPARATOR = "__";
5
+ /**
6
+ * Returns true if the downstream tool runs as an experimental MCP task
7
+ * (taskSupport "optional" or "required"), meaning it must be proxied
8
+ * through the task lifecycle rather than a plain `tools/call`.
9
+ */
10
+ export function isTaskTool(tool) {
11
+ const support = tool.execution?.taskSupport;
12
+ return support === "optional" || support === "required";
13
+ }
14
+ /**
15
+ * Register a task-capable downstream tool on the gateway as an upstream
16
+ * task-tool.
17
+ *
18
+ * Bridging model:
19
+ * - `createTask`: starts the downstream tool via `callToolStream`, creates a
20
+ * matching task in the gateway's upstream task store, and drives the
21
+ * downstream stream in the background — mirroring status updates and the
22
+ * final result/error into the upstream store.
23
+ * - `getTask` / `getTaskResult`: read straight from the upstream store, which
24
+ * the background loop keeps current. No manual task-ID mapping is needed
25
+ * because the upstream store owns the upstream task identity.
26
+ *
27
+ * Returns a `{ remove }` handle for cleanup, matching the plain-tool path.
28
+ */
29
+ export function registerTaskProxyTool(mcpServer, connection, tool, proxyTimeoutMs, usageTracker, adTracker) {
30
+ const nsName = `${connection.slug}${SEPARATOR}${tool.name}`;
31
+ const trustLabel = TRUST_LABELS[connection.trustTier];
32
+ const description = `[${trustLabel}] [${connection.displayName}] [task] ${tool.description || tool.name}`;
33
+ const inputSchema = buildPassthroughSchema(tool.inputSchema);
34
+ log.debug("Registering task-proxy tool", {
35
+ nsName,
36
+ downstream: tool.name,
37
+ server: connection.slug,
38
+ taskSupport: tool.execution?.taskSupport,
39
+ });
40
+ const registered = mcpServer.experimental.tasks.registerToolTask(nsName, {
41
+ description,
42
+ inputSchema,
43
+ // Mirror the downstream requirement: if the downstream demands a task,
44
+ // require it upstream too; otherwise allow either.
45
+ execution: {
46
+ taskSupport: tool.execution?.taskSupport === "required" ? "required" : "optional",
47
+ },
48
+ }, {
49
+ // ── Create: kick off downstream work, mirror into upstream store ──
50
+ createTask: async (args, extra) => {
51
+ const startedAt = Date.now();
52
+ const task = await extra.taskStore.createTask({
53
+ ttl: extra.taskRequestedTtl ?? 300_000,
54
+ });
55
+ log.debug("Task created, starting downstream stream", {
56
+ nsName,
57
+ taskId: task.taskId,
58
+ });
59
+ // Drive the downstream task in the background. We intentionally do
60
+ // not await — createTask must return promptly with the task handle.
61
+ void driveDownstreamTask({
62
+ connection,
63
+ tool,
64
+ args,
65
+ upstreamTaskId: task.taskId,
66
+ taskStore: extra.taskStore,
67
+ proxyTimeoutMs,
68
+ startedAt,
69
+ usageTracker,
70
+ adTracker,
71
+ });
72
+ return { task };
73
+ },
74
+ // ── Get task status: read from upstream store ──
75
+ getTask: async (_args, extra) => {
76
+ return extra.taskStore.getTask(extra.taskId);
77
+ },
78
+ // ── Get task result: read from upstream store ──
79
+ getTaskResult: async (_args, extra) => {
80
+ const result = await extra.taskStore.getTaskResult(extra.taskId);
81
+ return result;
82
+ },
83
+ });
84
+ return { remove: () => registered.remove() };
85
+ }
86
+ /**
87
+ * Background driver: consumes the downstream task stream and mirrors its
88
+ * lifecycle into the gateway's upstream task store. Never throws — all
89
+ * failures are converted into a stored "failed" result so upstream getters
90
+ * always resolve.
91
+ */
92
+ async function driveDownstreamTask(opts) {
93
+ const { connection, tool, args, upstreamTaskId, taskStore, startedAt, usageTracker, adTracker, } = opts;
94
+ try {
95
+ const stream = connection.client.experimental.tasks.callToolStream({
96
+ name: tool.name,
97
+ arguments: args,
98
+ });
99
+ for await (const message of stream) {
100
+ switch (message.type) {
101
+ case "taskStatus":
102
+ // Mirror non-terminal status (working / input_required) upstream.
103
+ if (message.task.status === "working" ||
104
+ message.task.status === "input_required") {
105
+ await taskStore
106
+ .updateTaskStatus(upstreamTaskId, message.task.status, message.task.statusMessage)
107
+ .catch(() => { });
108
+ }
109
+ break;
110
+ case "result": {
111
+ await taskStore.storeTaskResult(upstreamTaskId, "completed", message.result);
112
+ recordUsage(usageTracker, connection, tool, startedAt, true);
113
+ if (adTracker) {
114
+ adTracker.trackToolUsage(connection.slug, tool.name).catch(() => { });
115
+ }
116
+ return;
117
+ }
118
+ case "error": {
119
+ const errMsg = message.error?.message ?? "Downstream task failed";
120
+ await taskStore.storeTaskResult(upstreamTaskId, "failed", {
121
+ content: [
122
+ {
123
+ type: "text",
124
+ text: `Error from ${connection.displayName}: ${errMsg}`,
125
+ },
126
+ ],
127
+ isError: true,
128
+ });
129
+ recordUsage(usageTracker, connection, tool, startedAt, false, errMsg);
130
+ return;
131
+ }
132
+ // "taskCreated" — downstream task started; nothing to mirror since the
133
+ // upstream task already exists. Continue consuming the stream.
134
+ default:
135
+ break;
136
+ }
137
+ }
138
+ // Stream ended without an explicit result/error message — treat as failure
139
+ // so upstream getters never hang on a missing result.
140
+ await taskStore
141
+ .storeTaskResult(upstreamTaskId, "failed", {
142
+ content: [
143
+ {
144
+ type: "text",
145
+ text: `Downstream task for ${tool.name} ended without a result.`,
146
+ },
147
+ ],
148
+ isError: true,
149
+ })
150
+ .catch(() => { });
151
+ recordUsage(usageTracker, connection, tool, startedAt, false, "no result");
152
+ }
153
+ catch (err) {
154
+ const msg = err instanceof Error ? err.message : String(err);
155
+ log.error("Downstream task stream failed", {
156
+ slug: connection.slug,
157
+ tool: tool.name,
158
+ error: msg,
159
+ });
160
+ await taskStore
161
+ .storeTaskResult(upstreamTaskId, "failed", {
162
+ content: [
163
+ {
164
+ type: "text",
165
+ text: `Error from ${connection.displayName}: ${msg}`,
166
+ },
167
+ ],
168
+ isError: true,
169
+ })
170
+ .catch(() => { });
171
+ recordUsage(usageTracker, connection, tool, startedAt, false, msg);
172
+ }
173
+ }
174
+ function recordUsage(usageTracker, connection, tool, startedAt, success, error) {
175
+ if (!usageTracker)
176
+ return;
177
+ usageTracker.recordCall({
178
+ slug: connection.slug,
179
+ toolName: tool.name,
180
+ startedAt,
181
+ durationMs: Date.now() - startedAt,
182
+ success,
183
+ ...(error ? { error } : {}),
184
+ });
185
+ }
186
+ //# sourceMappingURL=task-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-proxy.js","sourceRoot":"","sources":["../../src/proxy/task-proxy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { PermissionManager } from "../permissions/permission-manager.js";
3
+ import type { DownstreamConnection, DownstreamTool } from "../connection/types.js";
4
+ import type { AdTracker } from "../ads/ad-tracker.js";
5
+ import type { UsageTracker } from "../analytics/usage-tracker.js";
6
+ import type { PluginManager } from "../plugins/plugin-manager.js";
7
+ import type { AuditLog } from "../audit/audit-log.js";
8
+ /**
9
+ * Namespace a tool name: `{slug}__{toolname}`
10
+ */
11
+ export declare function namespaceTool(slug: string, toolName: string): string;
12
+ /**
13
+ * Parse a namespaced tool name back into slug + original tool name.
14
+ */
15
+ export declare function parseNamespacedTool(namespacedName: string): {
16
+ slug: string;
17
+ toolName: string;
18
+ } | null;
19
+ /**
20
+ * Register proxied tools on the gateway McpServer for a downstream connection.
21
+ *
22
+ * Each downstream tool is registered with:
23
+ * - Namespaced name: `slug__toolname`
24
+ * - Description prefixed with trust tier and server name
25
+ * - Passthrough Zod schema (preserves property names, accepts any values)
26
+ * - Callback that forwards to `client.callTool()`
27
+ *
28
+ * Returns a map of namespaced name → { remove } handle for cleanup.
29
+ */
30
+ export declare function registerProxiedTools(mcpServer: McpServer, connection: DownstreamConnection, downstreamTools: DownstreamTool[], proxyTimeoutMs: number, permissionManager?: PermissionManager, adTracker?: AdTracker | null, usageTracker?: UsageTracker, pluginManager?: PluginManager, auditLog?: AuditLog): Map<string, {
31
+ remove: () => void;
32
+ }>;
33
+ /**
34
+ * Remove all proxied tools for a connection.
35
+ */
36
+ export declare function removeProxiedTools(proxies: Map<string, {
37
+ remove: () => void;
38
+ }>): void;
@@ -0,0 +1,229 @@
1
+ import { buildPassthroughSchema } from "./schema-adapter.js";
2
+ import { TRUST_LABELS } from "../permissions/trust-tiers.js";
3
+ import { log } from "../utils/logger.js";
4
+ import { buildProgressForwarder } from "./progress-forwarder.js";
5
+ import { isTaskTool, registerTaskProxyTool } from "./task-proxy.js";
6
+ const SEPARATOR = "__";
7
+ /**
8
+ * Namespace a tool name: `{slug}__{toolname}`
9
+ */
10
+ export function namespaceTool(slug, toolName) {
11
+ return `${slug}${SEPARATOR}${toolName}`;
12
+ }
13
+ /**
14
+ * Parse a namespaced tool name back into slug + original tool name.
15
+ */
16
+ export function parseNamespacedTool(namespacedName) {
17
+ const idx = namespacedName.indexOf(SEPARATOR);
18
+ if (idx === -1)
19
+ return null;
20
+ return {
21
+ slug: namespacedName.substring(0, idx),
22
+ toolName: namespacedName.substring(idx + SEPARATOR.length),
23
+ };
24
+ }
25
+ /**
26
+ * Register proxied tools on the gateway McpServer for a downstream connection.
27
+ *
28
+ * Each downstream tool is registered with:
29
+ * - Namespaced name: `slug__toolname`
30
+ * - Description prefixed with trust tier and server name
31
+ * - Passthrough Zod schema (preserves property names, accepts any values)
32
+ * - Callback that forwards to `client.callTool()`
33
+ *
34
+ * Returns a map of namespaced name → { remove } handle for cleanup.
35
+ */
36
+ export function registerProxiedTools(mcpServer, connection, downstreamTools, proxyTimeoutMs, permissionManager, adTracker, usageTracker, pluginManager, auditLog) {
37
+ const registered = new Map();
38
+ const trustLabel = TRUST_LABELS[connection.trustTier];
39
+ for (const tool of downstreamTools) {
40
+ // Check tool-level permissions if a permission manager is provided
41
+ if (permissionManager && !permissionManager.isToolAllowed(connection.slug, tool.name)) {
42
+ log.info("Tool blocked by permission policy", {
43
+ tool: tool.name,
44
+ server: connection.slug,
45
+ });
46
+ continue;
47
+ }
48
+ const nsName = namespaceTool(connection.slug, tool.name);
49
+ // Task-based tools (experimental) need the task lifecycle, not a plain
50
+ // call. Route them to the dedicated task proxy.
51
+ if (isTaskTool(tool)) {
52
+ try {
53
+ const taskHandle = registerTaskProxyTool(mcpServer, connection, tool, proxyTimeoutMs, usageTracker, adTracker);
54
+ registered.set(nsName, taskHandle);
55
+ }
56
+ catch (err) {
57
+ log.warn("Failed to register task-proxy tool; skipping", {
58
+ nsName,
59
+ error: err instanceof Error ? err.message : String(err),
60
+ });
61
+ }
62
+ continue;
63
+ }
64
+ // Build description with trust label and optional annotation hints
65
+ const annotationHints = buildAnnotationHints(tool.annotations);
66
+ const description = `[${trustLabel}] [${connection.displayName}]${annotationHints} ${tool.description || tool.name}`;
67
+ const schema = buildPassthroughSchema(tool.inputSchema);
68
+ log.debug("Registering proxied tool", {
69
+ nsName,
70
+ downstream: tool.name,
71
+ server: connection.slug,
72
+ });
73
+ const handle = mcpServer.tool(nsName, description, schema, async (args,
74
+ // RequestHandlerExtra from the upstream caller (e.g. Cursor). We type
75
+ // loosely (`unknown`) because the SDK's generic shape is awkward to
76
+ // import here and we only need two fields — captured by the
77
+ // UpstreamProgressContext interface.
78
+ extra) => {
79
+ const startedAt = Date.now();
80
+ log.debug("Proxying tool call", {
81
+ nsName,
82
+ downstream: tool.name,
83
+ server: connection.slug,
84
+ });
85
+ // Run plugin beforeToolCall hooks
86
+ if (pluginManager) {
87
+ const ctx = {
88
+ slug: connection.slug,
89
+ toolName: tool.name,
90
+ args,
91
+ trustTier: connection.trustTier,
92
+ };
93
+ const modifiedCtx = await pluginManager.runBeforeToolCall(ctx);
94
+ if (modifiedCtx === null) {
95
+ // Plugin blocked the call
96
+ auditLog?.record({
97
+ type: "tool_blocked",
98
+ slug: connection.slug,
99
+ tool: tool.name,
100
+ enforcement: connection.manifest?.enforcement,
101
+ reason: "blocked by gateway plugin",
102
+ });
103
+ return {
104
+ content: [
105
+ {
106
+ type: "text",
107
+ text: `Tool call blocked by gateway plugin.`,
108
+ },
109
+ ],
110
+ isError: true,
111
+ };
112
+ }
113
+ // Use potentially modified args
114
+ args = modifiedCtx.args;
115
+ }
116
+ // Forward progress notifications back to the upstream client
117
+ // using the upstream-supplied progress token (Bug A fix).
118
+ const onprogress = buildProgressForwarder(extra, { slug: connection.slug, toolName: tool.name });
119
+ try {
120
+ const result = await connection.client.callTool({ name: tool.name, arguments: args }, undefined, {
121
+ signal: AbortSignal.timeout(proxyTimeoutMs),
122
+ ...(onprogress ? { onprogress } : {}),
123
+ });
124
+ const durationMs = Date.now() - startedAt;
125
+ // Track tool usage for ad attribution (fire-and-forget)
126
+ if (adTracker) {
127
+ adTracker.trackToolUsage(connection.slug, tool.name).catch(() => { });
128
+ }
129
+ // Track usage analytics
130
+ if (usageTracker) {
131
+ usageTracker.recordCall({
132
+ slug: connection.slug,
133
+ toolName: tool.name,
134
+ startedAt,
135
+ durationMs,
136
+ success: true,
137
+ });
138
+ }
139
+ // Safety audit: the tool ran under its sandbox enforcement level.
140
+ auditLog?.record({
141
+ type: "tool_call",
142
+ slug: connection.slug,
143
+ tool: tool.name,
144
+ enforcement: connection.manifest?.enforcement,
145
+ ok: true,
146
+ });
147
+ // Run plugin afterToolCall hooks
148
+ let finalResult = result;
149
+ if (pluginManager) {
150
+ finalResult = await pluginManager.runAfterToolCall({
151
+ slug: connection.slug,
152
+ toolName: tool.name,
153
+ args,
154
+ trustTier: connection.trustTier,
155
+ }, finalResult);
156
+ }
157
+ return finalResult;
158
+ }
159
+ catch (err) {
160
+ const durationMs = Date.now() - startedAt;
161
+ const msg = err instanceof Error ? err.message : String(err);
162
+ log.error("Proxied tool call failed", {
163
+ nsName,
164
+ error: msg,
165
+ });
166
+ // Track failed call analytics
167
+ if (usageTracker) {
168
+ usageTracker.recordCall({
169
+ slug: connection.slug,
170
+ toolName: tool.name,
171
+ startedAt,
172
+ durationMs,
173
+ success: false,
174
+ error: msg,
175
+ });
176
+ }
177
+ // Safety audit: the tool call errored (forensics — what failed).
178
+ auditLog?.record({
179
+ type: "tool_call",
180
+ slug: connection.slug,
181
+ tool: tool.name,
182
+ enforcement: connection.manifest?.enforcement,
183
+ ok: false,
184
+ reason: msg.slice(0, 200),
185
+ });
186
+ return {
187
+ content: [
188
+ {
189
+ type: "text",
190
+ text: `Error from ${connection.displayName}: ${msg}`,
191
+ },
192
+ ],
193
+ isError: true,
194
+ };
195
+ }
196
+ });
197
+ registered.set(nsName, handle);
198
+ }
199
+ return registered;
200
+ }
201
+ /**
202
+ * Remove all proxied tools for a connection.
203
+ */
204
+ export function removeProxiedTools(proxies) {
205
+ for (const [name, handle] of proxies) {
206
+ log.debug("Removing proxied tool", { name });
207
+ handle.remove();
208
+ }
209
+ proxies.clear();
210
+ }
211
+ /**
212
+ * Build a compact annotation hint string from tool annotations.
213
+ * E.g. " [read-only]" or " [destructive]" or "" if no relevant annotations.
214
+ */
215
+ function buildAnnotationHints(annotations) {
216
+ if (!annotations)
217
+ return "";
218
+ const hints = [];
219
+ if (annotations.readOnlyHint === true)
220
+ hints.push("read-only");
221
+ if (annotations.destructiveHint === true)
222
+ hints.push("destructive");
223
+ if (annotations.idempotentHint === true)
224
+ hints.push("idempotent");
225
+ if (annotations.openWorldHint === true)
226
+ hints.push("open-world");
227
+ return hints.length > 0 ? ` [${hints.join(", ")}]` : "";
228
+ }
229
+ //# sourceMappingURL=tool-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-router.js","sourceRoot":"","sources":["../../src/proxy/tool-router.ts"],"names":[],"mappings":"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"}