@superblocksteam/gateway 2.0.155-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +87 -0
- package/README.md +170 -0
- package/dist/agents/resolve-orchestrator-url.d.ts +22 -0
- package/dist/agents/resolve-orchestrator-url.js +38 -0
- package/dist/agents/resolve-orchestrator-url.js.map +1 -0
- package/dist/capabilities/import-prompt.d.ts +11 -0
- package/dist/capabilities/import-prompt.js +96 -0
- package/dist/capabilities/import-prompt.js.map +1 -0
- package/dist/capabilities/lifecycle.d.ts +73 -0
- package/dist/capabilities/lifecycle.js +2531 -0
- package/dist/capabilities/lifecycle.js.map +1 -0
- package/dist/capabilities/persisted-progress.d.ts +46 -0
- package/dist/capabilities/persisted-progress.js +246 -0
- package/dist/capabilities/persisted-progress.js.map +1 -0
- package/dist/capabilities/types.d.ts +446 -0
- package/dist/capabilities/types.js +47 -0
- package/dist/capabilities/types.js.map +1 -0
- package/dist/capture/browser-contract.d.ts +36 -0
- package/dist/capture/browser-contract.js +24 -0
- package/dist/capture/browser-contract.js.map +1 -0
- package/dist/capture/browser-instructions.d.ts +9 -0
- package/dist/capture/browser-instructions.js +17 -0
- package/dist/capture/browser-instructions.js.map +1 -0
- package/dist/capture/capture-library-probe.d.ts +55 -0
- package/dist/capture/capture-library-probe.js +177 -0
- package/dist/capture/capture-library-probe.js.map +1 -0
- package/dist/capture/capture-library.d.ts +52 -0
- package/dist/capture/capture-library.js +249 -0
- package/dist/capture/capture-library.js.map +1 -0
- package/dist/capture/classify-library-paint.d.ts +21 -0
- package/dist/capture/classify-library-paint.js +13 -0
- package/dist/capture/classify-library-paint.js.map +1 -0
- package/dist/capture/host-mcp-scan.d.ts +16 -0
- package/dist/capture/host-mcp-scan.js +64 -0
- package/dist/capture/host-mcp-scan.js.map +1 -0
- package/dist/capture/library-parent-script.d.ts +34 -0
- package/dist/capture/library-parent-script.js +82 -0
- package/dist/capture/library-parent-script.js.map +1 -0
- package/dist/capture/mode.d.ts +22 -0
- package/dist/capture/mode.js +45 -0
- package/dist/capture/mode.js.map +1 -0
- package/dist/config.d.ts +141 -0
- package/dist/config.js +109 -0
- package/dist/config.js.map +1 -0
- package/dist/debug.d.ts +2 -0
- package/dist/debug.js +9 -0
- package/dist/debug.js.map +1 -0
- package/dist/deps.d.ts +36 -0
- package/dist/deps.js +2 -0
- package/dist/deps.js.map +1 -0
- package/dist/events/cursor.d.ts +43 -0
- package/dist/events/cursor.js +78 -0
- package/dist/events/cursor.js.map +1 -0
- package/dist/events/memory-event-store.d.ts +34 -0
- package/dist/events/memory-event-store.js +110 -0
- package/dist/events/memory-event-store.js.map +1 -0
- package/dist/events/merge.d.ts +23 -0
- package/dist/events/merge.js +97 -0
- package/dist/events/merge.js.map +1 -0
- package/dist/events/normalized-collector.d.ts +62 -0
- package/dist/events/normalized-collector.js +156 -0
- package/dist/events/normalized-collector.js.map +1 -0
- package/dist/events/schema.d.ts +9 -0
- package/dist/events/schema.js +93 -0
- package/dist/events/schema.js.map +1 -0
- package/dist/events/snapshot.d.ts +32 -0
- package/dist/events/snapshot.js +57 -0
- package/dist/events/snapshot.js.map +1 -0
- package/dist/events/stream-key.d.ts +2 -0
- package/dist/events/stream-key.js +31 -0
- package/dist/events/stream-key.js.map +1 -0
- package/dist/events/types.d.ts +179 -0
- package/dist/events/types.js +66 -0
- package/dist/events/types.js.map +1 -0
- package/dist/health/readiness.d.ts +99 -0
- package/dist/health/readiness.js +202 -0
- package/dist/health/readiness.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/filters.d.ts +8 -0
- package/dist/integrations/filters.js +40 -0
- package/dist/integrations/filters.js.map +1 -0
- package/dist/integrations/map.d.ts +26 -0
- package/dist/integrations/map.js +43 -0
- package/dist/integrations/map.js.map +1 -0
- package/dist/integrations/profile-key.d.ts +19 -0
- package/dist/integrations/profile-key.js +35 -0
- package/dist/integrations/profile-key.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +5 -0
- package/dist/main.js.map +1 -0
- package/dist/orchestrator/client.d.ts +62 -0
- package/dist/orchestrator/client.js +160 -0
- package/dist/orchestrator/client.js.map +1 -0
- package/dist/playwright/ensure-chromium.d.ts +26 -0
- package/dist/playwright/ensure-chromium.js +60 -0
- package/dist/playwright/ensure-chromium.js.map +1 -0
- package/dist/preview/capture-screenshot.d.ts +52 -0
- package/dist/preview/capture-screenshot.js +158 -0
- package/dist/preview/capture-screenshot.js.map +1 -0
- package/dist/preview/viewer-url.d.ts +82 -0
- package/dist/preview/viewer-url.js +128 -0
- package/dist/preview/viewer-url.js.map +1 -0
- package/dist/process/fault-barrier.d.ts +55 -0
- package/dist/process/fault-barrier.js +113 -0
- package/dist/process/fault-barrier.js.map +1 -0
- package/dist/resume/memory-progress-store.d.ts +39 -0
- package/dist/resume/memory-progress-store.js +82 -0
- package/dist/resume/memory-progress-store.js.map +1 -0
- package/dist/resume/memory-recent-app-store.d.ts +14 -0
- package/dist/resume/memory-recent-app-store.js +27 -0
- package/dist/resume/memory-recent-app-store.js.map +1 -0
- package/dist/resume/memory-turn-store.d.ts +18 -0
- package/dist/resume/memory-turn-store.js +73 -0
- package/dist/resume/memory-turn-store.js.map +1 -0
- package/dist/resume/progress-key.d.ts +21 -0
- package/dist/resume/progress-key.js +58 -0
- package/dist/resume/progress-key.js.map +1 -0
- package/dist/resume/stores.d.ts +14 -0
- package/dist/resume/stores.js +18 -0
- package/dist/resume/stores.js.map +1 -0
- package/dist/resume/types.d.ts +124 -0
- package/dist/resume/types.js +13 -0
- package/dist/resume/types.js.map +1 -0
- package/dist/sabs/agent-facing-text.d.ts +33 -0
- package/dist/sabs/agent-facing-text.js +125 -0
- package/dist/sabs/agent-facing-text.js.map +1 -0
- package/dist/sabs/browser-context.d.ts +53 -0
- package/dist/sabs/browser-context.js +91 -0
- package/dist/sabs/browser-context.js.map +1 -0
- package/dist/sabs/editor-client-methods.d.ts +147 -0
- package/dist/sabs/editor-client-methods.js +715 -0
- package/dist/sabs/editor-client-methods.js.map +1 -0
- package/dist/sabs/recent-tool-runs.d.ts +14 -0
- package/dist/sabs/recent-tool-runs.js +61 -0
- package/dist/sabs/recent-tool-runs.js.map +1 -0
- package/dist/sabs/session-peer.d.ts +351 -0
- package/dist/sabs/session-peer.js +2 -0
- package/dist/sabs/session-peer.js.map +1 -0
- package/dist/sabs/turn-collector.d.ts +152 -0
- package/dist/sabs/turn-collector.js +573 -0
- package/dist/sabs/turn-collector.js.map +1 -0
- package/dist/sabs/websocket-session-peer.d.ts +514 -0
- package/dist/sabs/websocket-session-peer.js +2487 -0
- package/dist/sabs/websocket-session-peer.js.map +1 -0
- package/dist/security/constant-time.d.ts +9 -0
- package/dist/security/constant-time.js +13 -0
- package/dist/security/constant-time.js.map +1 -0
- package/dist/server/client.d.ts +371 -0
- package/dist/server/client.js +572 -0
- package/dist/server/client.js.map +1 -0
- package/dist/server/identity-reads.d.ts +76 -0
- package/dist/server/identity-reads.js +209 -0
- package/dist/server/identity-reads.js.map +1 -0
- package/dist/start.d.ts +14 -0
- package/dist/start.js +75 -0
- package/dist/start.js.map +1 -0
- package/dist/telemetry/metrics.d.ts +32 -0
- package/dist/telemetry/metrics.js +57 -0
- package/dist/telemetry/metrics.js.map +1 -0
- package/dist/transports/mcp/admin-tools.d.ts +47 -0
- package/dist/transports/mcp/admin-tools.js +164 -0
- package/dist/transports/mcp/admin-tools.js.map +1 -0
- package/dist/transports/mcp/app-status-html.d.ts +15 -0
- package/dist/transports/mcp/app-status-html.js +563 -0
- package/dist/transports/mcp/app-status-html.js.map +1 -0
- package/dist/transports/mcp/decision-elicitation.d.ts +74 -0
- package/dist/transports/mcp/decision-elicitation.js +233 -0
- package/dist/transports/mcp/decision-elicitation.js.map +1 -0
- package/dist/transports/mcp/dispatch-probe.d.ts +24 -0
- package/dist/transports/mcp/dispatch-probe.js +48 -0
- package/dist/transports/mcp/dispatch-probe.js.map +1 -0
- package/dist/transports/mcp/format-tool-content.d.ts +25 -0
- package/dist/transports/mcp/format-tool-content.js +48 -0
- package/dist/transports/mcp/format-tool-content.js.map +1 -0
- package/dist/transports/mcp/mount.d.ts +9 -0
- package/dist/transports/mcp/mount.js +469 -0
- package/dist/transports/mcp/mount.js.map +1 -0
- package/dist/transports/mcp/progress-notifier.d.ts +39 -0
- package/dist/transports/mcp/progress-notifier.js +52 -0
- package/dist/transports/mcp/progress-notifier.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* The identity lookup did not produce an answer this caller can act on.
|
|
4
|
+
*
|
|
5
|
+
* Distinct from "the credential was refused": nothing is known about the
|
|
6
|
+
* caller's account, so the right response is to try again rather than to
|
|
7
|
+
* re-link. Capabilities map this to `downstream_unavailable`.
|
|
8
|
+
*/
|
|
9
|
+
export class IdentityUnavailableError extends Error {
|
|
10
|
+
reason;
|
|
11
|
+
constructor(message, options = {}) {
|
|
12
|
+
super(message, { cause: options.cause });
|
|
13
|
+
this.name = "IdentityUnavailableError";
|
|
14
|
+
this.reason = options.reason ?? "read_failed";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Short enough that losing access or switching org lands within a minute,
|
|
19
|
+
* long enough that a poll loop stops paying for a round trip per call.
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_CACHE_TTL_MS = 60_000;
|
|
22
|
+
/**
|
|
23
|
+
* The tool contract promises callers an answer in about ten seconds, so that
|
|
24
|
+
* is the longest a tool call waits on identity. The read itself is not
|
|
25
|
+
* cancelled at this point — see {@link withGuardedIdentityReads}.
|
|
26
|
+
*/
|
|
27
|
+
const DEFAULT_CALLER_WAIT_MS = 10_000;
|
|
28
|
+
/**
|
|
29
|
+
* How long a wedged endpoint is taken at its word. Long enough that a burst of
|
|
30
|
+
* polls costs one read rather than one per call, short enough that recovery is
|
|
31
|
+
* noticed without anyone restarting a client.
|
|
32
|
+
*/
|
|
33
|
+
const DEFAULT_COOLDOWN_MS = 30_000;
|
|
34
|
+
/** Cached entries are swept once the map is big enough for it to be worth it. */
|
|
35
|
+
const PRUNE_AT_ENTRIES = 512;
|
|
36
|
+
const OUTAGE_MESSAGE = "The Superblocks API could not confirm who this caller is in time. This is usually transient — try again in a moment.";
|
|
37
|
+
/**
|
|
38
|
+
* A deadline a caller can stop waiting at without cancelling what it waits on.
|
|
39
|
+
*/
|
|
40
|
+
function callerDeadline(ms) {
|
|
41
|
+
let timer;
|
|
42
|
+
const expired = new Promise((_resolve, reject) => {
|
|
43
|
+
timer = setTimeout(() => reject(new IdentityUnavailableError(OUTAGE_MESSAGE, {
|
|
44
|
+
reason: "caller_timeout",
|
|
45
|
+
})), ms);
|
|
46
|
+
});
|
|
47
|
+
return { cancel: () => clearTimeout(timer), expired };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Makes the identity lookup that every tool call gates on cheap and bounded.
|
|
51
|
+
*
|
|
52
|
+
* `GET /api/v1/users/me` is the first thing every capability does and the
|
|
53
|
+
* slowest endpoint the Superblocks API exposes. Read straight through, one
|
|
54
|
+
* wedged upstream took down the whole tool surface: each call waited out the
|
|
55
|
+
* full client timeout, a burst of callers multiplied the load on the endpoint
|
|
56
|
+
* that was already stuck, and nothing remembered that the last twenty calls
|
|
57
|
+
* had just failed the same way.
|
|
58
|
+
*
|
|
59
|
+
* Four things fix that, and they have to work together:
|
|
60
|
+
*
|
|
61
|
+
* - **Cache.** Identity does not change between polls, so a poll loop spends
|
|
62
|
+
* one read per TTL instead of one per call.
|
|
63
|
+
* - **Collapse.** Concurrent callers on a cold cache join one read rather than
|
|
64
|
+
* each starting their own.
|
|
65
|
+
* - **Caller deadline.** A tool call stops waiting at the window its own
|
|
66
|
+
* contract advertises. The read keeps running, so an endpoint that answers
|
|
67
|
+
* slowly still fills the cache and the next call is served from it — the
|
|
68
|
+
* reason this is a deadline on the wait and not a shorter timeout on the
|
|
69
|
+
* read.
|
|
70
|
+
* - **Breaker.** Once a read fails, later calls fail fast for a cooldown
|
|
71
|
+
* instead of each discovering the same outage the slow way.
|
|
72
|
+
*/
|
|
73
|
+
export function withGuardedIdentityReads(read, options = {}) {
|
|
74
|
+
const now = options.now ?? Date.now;
|
|
75
|
+
const cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
76
|
+
const callerWaitMs = options.callerWaitMs ?? DEFAULT_CALLER_WAIT_MS;
|
|
77
|
+
const cooldownMs = options.cooldownMs ?? DEFAULT_COOLDOWN_MS;
|
|
78
|
+
const onOutcome = options.onOutcome ?? (() => undefined);
|
|
79
|
+
const opensBreaker = options.opensBreaker ?? (() => true);
|
|
80
|
+
// Hashed so raw credentials are never held as map keys.
|
|
81
|
+
const cache = new Map();
|
|
82
|
+
const inflight = new Map();
|
|
83
|
+
let failFastUntil = 0;
|
|
84
|
+
// The breaker has tripped and nothing has succeeded since. While that holds,
|
|
85
|
+
// the cooldown expiring admits exactly one probe rather than one per
|
|
86
|
+
// credential. Collapsing is keyed per credential, so without this a burst of
|
|
87
|
+
// distinct callers each opened its own read against the wedged route the
|
|
88
|
+
// moment the cooldown lapsed -- eight agents, eight probes, every 30s.
|
|
89
|
+
let recovering = false;
|
|
90
|
+
let probeInFlight = false;
|
|
91
|
+
// Errors `opensBreaker` judged to be about one credential. They propagate
|
|
92
|
+
// raw, so the caller still sees its own 401 rather than a wrapped one, and
|
|
93
|
+
// this is what lets the outcome be labelled without re-inspecting them here.
|
|
94
|
+
// Weak so a rejected read holds nothing once its error is collected.
|
|
95
|
+
const credentialErrors = new WeakSet();
|
|
96
|
+
function prune() {
|
|
97
|
+
if (cache.size < PRUNE_AT_ENTRIES) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const timestamp = now();
|
|
101
|
+
for (const [key, entry] of cache) {
|
|
102
|
+
if (entry.expiresAt <= timestamp) {
|
|
103
|
+
cache.delete(key);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function startRead(key, apiKey) {
|
|
108
|
+
const pending = (async () => {
|
|
109
|
+
try {
|
|
110
|
+
const value = await read(apiKey);
|
|
111
|
+
failFastUntil = 0;
|
|
112
|
+
recovering = false;
|
|
113
|
+
// `undefined` means the API refused this credential. Caching that
|
|
114
|
+
// would keep refusing a caller who has since re-linked their account.
|
|
115
|
+
if (value !== undefined) {
|
|
116
|
+
prune();
|
|
117
|
+
cache.set(key, { expiresAt: now() + cacheTtlMs, value });
|
|
118
|
+
}
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
if (!opensBreaker(error)) {
|
|
123
|
+
// The endpoint answered — it just refused this caller. That is proof
|
|
124
|
+
// of life, so it closes the breaker rather than leaving it half-open:
|
|
125
|
+
// holding it open on the strength of a refusal would serialize every
|
|
126
|
+
// later read behind the one-probe gate for as long as refusals kept
|
|
127
|
+
// arriving, answering `breaker_open` to healthy callers against a
|
|
128
|
+
// healthy endpoint.
|
|
129
|
+
failFastUntil = 0;
|
|
130
|
+
recovering = false;
|
|
131
|
+
if (typeof error === "object" && error !== null) {
|
|
132
|
+
credentialErrors.add(error);
|
|
133
|
+
}
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
failFastUntil = now() + cooldownMs;
|
|
137
|
+
recovering = true;
|
|
138
|
+
// The raw failure names a timeout on an endpoint the caller never
|
|
139
|
+
// asked for, which reads as "the gateway is broken". This says what
|
|
140
|
+
// the caller can actually do about it.
|
|
141
|
+
throw new IdentityUnavailableError(OUTAGE_MESSAGE, {
|
|
142
|
+
cause: error,
|
|
143
|
+
reason: "read_failed",
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
inflight.delete(key);
|
|
148
|
+
probeInFlight = false;
|
|
149
|
+
}
|
|
150
|
+
})();
|
|
151
|
+
inflight.set(key, pending);
|
|
152
|
+
return pending;
|
|
153
|
+
}
|
|
154
|
+
function classifyFailure(error) {
|
|
155
|
+
if (error instanceof IdentityUnavailableError) {
|
|
156
|
+
return error.reason === "caller_timeout" ? "caller_timeout" : "outage";
|
|
157
|
+
}
|
|
158
|
+
if (typeof error === "object" && error !== null) {
|
|
159
|
+
// Raw and not wrapped: `startRead` decided this one is the credential's
|
|
160
|
+
// fault, not the endpoint's. Calling it an outage would put a healthy
|
|
161
|
+
// endpoint in the outage bucket every time a stale key retries.
|
|
162
|
+
return credentialErrors.has(error) ? "credential_error" : "outage";
|
|
163
|
+
}
|
|
164
|
+
return "outage";
|
|
165
|
+
}
|
|
166
|
+
return async function guardedRead(apiKey) {
|
|
167
|
+
const key = createHash("sha256").update(apiKey).digest("hex");
|
|
168
|
+
const cached = cache.get(key);
|
|
169
|
+
if (cached) {
|
|
170
|
+
if (cached.expiresAt > now()) {
|
|
171
|
+
onOutcome("cached");
|
|
172
|
+
return cached.value;
|
|
173
|
+
}
|
|
174
|
+
cache.delete(key);
|
|
175
|
+
}
|
|
176
|
+
// A read already running is the one to wait on even while the breaker is
|
|
177
|
+
// open: it is the probe that will close it.
|
|
178
|
+
let pending = inflight.get(key);
|
|
179
|
+
if (!pending) {
|
|
180
|
+
// Open, or half-open with the one probe already out.
|
|
181
|
+
if (failFastUntil > now() || (recovering && probeInFlight)) {
|
|
182
|
+
onOutcome("breaker_open");
|
|
183
|
+
throw new IdentityUnavailableError(OUTAGE_MESSAGE, {
|
|
184
|
+
reason: "breaker_open",
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (recovering) {
|
|
188
|
+
probeInFlight = true;
|
|
189
|
+
}
|
|
190
|
+
pending = startRead(key, apiKey);
|
|
191
|
+
}
|
|
192
|
+
// Promise.race keeps a rejection handler on `pending` for its whole life,
|
|
193
|
+
// so a read that fails after this caller gave up stays handled.
|
|
194
|
+
const deadline = callerDeadline(callerWaitMs);
|
|
195
|
+
try {
|
|
196
|
+
const value = await Promise.race([pending, deadline.expired]);
|
|
197
|
+
onOutcome(value === undefined ? "refused" : "resolved");
|
|
198
|
+
return value;
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
onOutcome(classifyFailure(error));
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
deadline.cancel();
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=identity-reads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity-reads.js","sourceRoot":"","sources":["../../src/server/identity-reads.ts"],"names":[],"mappings":"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"}
|
package/dist/start.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type GatewayConfig } from "./config.js";
|
|
2
|
+
export type StartGatewayOptions = {
|
|
3
|
+
/** Personal API key of the already-logged-in Superblocks CLI user. */
|
|
4
|
+
apiKey: string;
|
|
5
|
+
};
|
|
6
|
+
export type StartedGateway = {
|
|
7
|
+
close: () => Promise<void>;
|
|
8
|
+
config: GatewayConfig;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Boots Gateway MCP over stdio as the CLI user. Shared by
|
|
12
|
+
* `superblocks gateway serve` (the IR path) and `node dist/main.js`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function startStdioGateway(options: StartGatewayOptions): Promise<StartedGateway>;
|
package/dist/start.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
|
+
import { loadConfig } from "./config.js";
|
|
3
|
+
import { installFaultBarrier } from "./process/fault-barrier.js";
|
|
4
|
+
import { createStores } from "./resume/stores.js";
|
|
5
|
+
import { WebSocketSessionPeer } from "./sabs/websocket-session-peer.js";
|
|
6
|
+
import { HttpSuperblocksServerClient } from "./server/client.js";
|
|
7
|
+
import { createMcpServer } from "./transports/mcp/mount.js";
|
|
8
|
+
/**
|
|
9
|
+
* Chromium never blocks Gateway start. Preview screenshots fail closed when
|
|
10
|
+
* Playwright is missing (ENG-5796).
|
|
11
|
+
*/
|
|
12
|
+
async function createRuntime(apiKey) {
|
|
13
|
+
const config = loadConfig();
|
|
14
|
+
const stores = await createStores();
|
|
15
|
+
const serverClient = new HttpSuperblocksServerClient(config.serverUrl);
|
|
16
|
+
const sessionPeer = new WebSocketSessionPeer({
|
|
17
|
+
beatLostGraceMs: config.beatLostGraceMs,
|
|
18
|
+
idleMs: config.heldSessionIdleMs,
|
|
19
|
+
profileKey: config.profileKey,
|
|
20
|
+
readyWaitMs: config.clarkReadyWaitMs,
|
|
21
|
+
releaseAfterTurnMs: config.heldSessionReleaseAfterTurnMs,
|
|
22
|
+
server: serverClient,
|
|
23
|
+
settleMs: config.clarkSettleMs,
|
|
24
|
+
testingEnabled: config.clarkTestingEnabled,
|
|
25
|
+
turnTimeoutMs: config.clarkTurnTimeoutMs,
|
|
26
|
+
uiBaseUrl: config.uiBaseUrl,
|
|
27
|
+
});
|
|
28
|
+
const deps = {
|
|
29
|
+
cliApiKey: apiKey,
|
|
30
|
+
config,
|
|
31
|
+
events: stores.events,
|
|
32
|
+
progressCursors: stores.progressCursors,
|
|
33
|
+
recentApps: stores.recentApps,
|
|
34
|
+
server: serverClient,
|
|
35
|
+
sessionPeer,
|
|
36
|
+
turns: stores.turns,
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
config,
|
|
40
|
+
deps,
|
|
41
|
+
close: async () => {
|
|
42
|
+
sessionPeer.closeAll();
|
|
43
|
+
await stores.close();
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Boots Gateway MCP over stdio as the CLI user. Shared by
|
|
49
|
+
* `superblocks gateway serve` (the IR path) and `node dist/main.js`.
|
|
50
|
+
*/
|
|
51
|
+
export async function startStdioGateway(options) {
|
|
52
|
+
const apiKey = options.apiKey.trim();
|
|
53
|
+
if (!apiKey) {
|
|
54
|
+
throw new Error("Not logged in to Superblocks. Run: superblocks login");
|
|
55
|
+
}
|
|
56
|
+
const runtime = await createRuntime(apiKey);
|
|
57
|
+
const disposeFaultBarrier = installFaultBarrier({
|
|
58
|
+
onFault: () => undefined,
|
|
59
|
+
shutdown: () => runtime.close(),
|
|
60
|
+
});
|
|
61
|
+
const server = createMcpServer(runtime.deps);
|
|
62
|
+
const transport = new StdioServerTransport();
|
|
63
|
+
await server.connect(transport);
|
|
64
|
+
console.error("gateway MCP listening on stdio as the Superblocks CLI user");
|
|
65
|
+
console.error("gateway live canvas capture uses libraryBrowserContext (testingEnabled does not gate it)", { testingEnabled: runtime.config.clarkTestingEnabled });
|
|
66
|
+
return {
|
|
67
|
+
config: runtime.config,
|
|
68
|
+
close: async () => {
|
|
69
|
+
disposeFaultBarrier();
|
|
70
|
+
await server.close().catch(() => undefined);
|
|
71
|
+
await runtime.close();
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=start.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start.js","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { IdentityReadOutcome } from "../server/identity-reads.js";
|
|
2
|
+
export declare const identityReadOutcomes: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
3
|
+
/**
|
|
4
|
+
* How the identity lookup behind a capability is behaving.
|
|
5
|
+
*
|
|
6
|
+
* No credential, user, or organization is ever a label — the outcome is a
|
|
7
|
+
* closed set, so the series count grows with code and not with traffic.
|
|
8
|
+
*/
|
|
9
|
+
export declare function recordIdentityReadOutcome(outcome: IdentityReadOutcome): void;
|
|
10
|
+
export declare const processFaults: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
11
|
+
/**
|
|
12
|
+
* How often the gateway is absorbing faults it used to die from (ENG-5809).
|
|
13
|
+
*
|
|
14
|
+
* Neither the message nor a stack is a label; both go to the log line instead.
|
|
15
|
+
*/
|
|
16
|
+
export declare function recordGatewayFault(fault: {
|
|
17
|
+
fatal: boolean;
|
|
18
|
+
kind: string;
|
|
19
|
+
}): void;
|
|
20
|
+
export declare const readinessVerdicts: import("@opentelemetry/api").Counter<import("@opentelemetry/api").Attributes>;
|
|
21
|
+
/**
|
|
22
|
+
* Separates "this gateway is broken" from "the platform it fronts is down".
|
|
23
|
+
*
|
|
24
|
+
* Kept so in-process dispatch probes can still record a verdict. Stdio Gateway
|
|
25
|
+
* does not bind `/health`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function recordReadinessVerdict(input: {
|
|
28
|
+
dispatch: string;
|
|
29
|
+
probe: "liveness" | "readiness";
|
|
30
|
+
ready: boolean;
|
|
31
|
+
upstream: string;
|
|
32
|
+
}): void;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { metrics } from "@opentelemetry/api";
|
|
2
|
+
const meter = metrics.getMeter("gateway");
|
|
3
|
+
function resourceAttributes() {
|
|
4
|
+
return {
|
|
5
|
+
"deployment.environment.name": process.env.DEPLOYMENT_ENVIRONMENT_NAME ?? "local",
|
|
6
|
+
"service.name": "gateway",
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export const identityReadOutcomes = meter.createCounter("gateway.identity.read.outcomes", {
|
|
10
|
+
description: "Caller-identity lookups by outcome: served from cache, resolved, refused, or unavailable",
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* How the identity lookup behind a capability is behaving.
|
|
14
|
+
*
|
|
15
|
+
* No credential, user, or organization is ever a label — the outcome is a
|
|
16
|
+
* closed set, so the series count grows with code and not with traffic.
|
|
17
|
+
*/
|
|
18
|
+
export function recordIdentityReadOutcome(outcome) {
|
|
19
|
+
identityReadOutcomes.add(1, {
|
|
20
|
+
...resourceAttributes(),
|
|
21
|
+
"gateway.outcome": outcome,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export const processFaults = meter.createCounter("gateway.process.faults", {
|
|
25
|
+
description: "Faults the process-level barrier caught, by kind and whether it gave up the process",
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* How often the gateway is absorbing faults it used to die from (ENG-5809).
|
|
29
|
+
*
|
|
30
|
+
* Neither the message nor a stack is a label; both go to the log line instead.
|
|
31
|
+
*/
|
|
32
|
+
export function recordGatewayFault(fault) {
|
|
33
|
+
processFaults.add(1, {
|
|
34
|
+
...resourceAttributes(),
|
|
35
|
+
"gateway.fault.fatal": String(fault.fatal),
|
|
36
|
+
"gateway.fault.kind": fault.kind,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export const readinessVerdicts = meter.createCounter("gateway.readiness.verdicts", {
|
|
40
|
+
description: "Health and readiness probe verdicts, by dispatch state and upstream reachability",
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* Separates "this gateway is broken" from "the platform it fronts is down".
|
|
44
|
+
*
|
|
45
|
+
* Kept so in-process dispatch probes can still record a verdict. Stdio Gateway
|
|
46
|
+
* does not bind `/health`.
|
|
47
|
+
*/
|
|
48
|
+
export function recordReadinessVerdict(input) {
|
|
49
|
+
readinessVerdicts.add(1, {
|
|
50
|
+
...resourceAttributes(),
|
|
51
|
+
"gateway.readiness.dispatch": input.dispatch,
|
|
52
|
+
"gateway.readiness.probe": input.probe,
|
|
53
|
+
"gateway.readiness.ready": String(input.ready),
|
|
54
|
+
"gateway.readiness.upstream": input.upstream,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/telemetry/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAI7C,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAE1C,SAAS,kBAAkB;IACzB,OAAO;QACL,6BAA6B,EAC3B,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,OAAO;QACpD,cAAc,EAAE,SAAS;KAC1B,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC,aAAa,CACrD,gCAAgC,EAChC;IACE,WAAW,EACT,0FAA0F;CAC7F,CACF,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAA4B;IACpE,oBAAoB,CAAC,GAAG,CAAC,CAAC,EAAE;QAC1B,GAAG,kBAAkB,EAAE;QACvB,iBAAiB,EAAE,OAAO;KAC3B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC,wBAAwB,EAAE;IACzE,WAAW,EACT,qFAAqF;CACxF,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAGlC;IACC,aAAa,CAAC,GAAG,CAAC,CAAC,EAAE;QACnB,GAAG,kBAAkB,EAAE;QACvB,qBAAqB,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;QAC1C,oBAAoB,EAAE,KAAK,CAAC,IAAI;KACjC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC,aAAa,CAClD,4BAA4B,EAC5B;IACE,WAAW,EACT,kFAAkF;CACrF,CACF,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAKtC;IACC,iBAAiB,CAAC,GAAG,CAAC,CAAC,EAAE;QACvB,GAAG,kBAAkB,EAAE;QACvB,4BAA4B,EAAE,KAAK,CAAC,QAAQ;QAC5C,yBAAyB,EAAE,KAAK,CAAC,KAAK;QACtC,yBAAyB,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;QAC9C,4BAA4B,EAAE,KAAK,CAAC,QAAQ;KAC7C,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { type ToolRegistry } from "@superblocksteam/mcp-server";
|
|
6
|
+
export type RegisterAdminToolsOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* Superblocks Server base URL injected as `base_url` when the caller omits it.
|
|
9
|
+
* Models should not invent environments; the gateway already knows the control plane.
|
|
10
|
+
*/
|
|
11
|
+
serverUrl: string;
|
|
12
|
+
/**
|
|
13
|
+
* Fixed credential for tests and tightly scoped callers. Prefer `resolveApiKey`
|
|
14
|
+
* on the live HTTP path so each tool call uses the caller's downstream key.
|
|
15
|
+
*/
|
|
16
|
+
apiKey?: string;
|
|
17
|
+
resolveApiKey?: (auth: AuthInfo | undefined) => Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* Cloud-Prem / laptop Admin agents (`GATEWAY_LOCAL_AGENT`) talk to a control
|
|
20
|
+
* plane that carries no SaaS billing plan, so the Admin tools' Enterprise/POC
|
|
21
|
+
* plan lookup would refuse every call. Entitlement for those installs is
|
|
22
|
+
* established by the deployment itself. SaaS-hosted Gateways leave this off
|
|
23
|
+
* and keep the check.
|
|
24
|
+
*/
|
|
25
|
+
localAgentMode?: boolean;
|
|
26
|
+
};
|
|
27
|
+
export type JsonSchemaToZodShapeOptions = {
|
|
28
|
+
/** Advertised as optional no matter what the source schema requires. */
|
|
29
|
+
optionalKeys?: readonly string[];
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Loose JSON-Schema -> Zod conversion so `registerTool` can list Admin tools
|
|
33
|
+
* with usable shapes. Handlers still validate through `@superblocksteam/mcp-server`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function jsonSchemaToZodShape(inputSchema: Tool["inputSchema"], options?: JsonSchemaToZodShapeOptions): Record<string, z.ZodType>;
|
|
36
|
+
/**
|
|
37
|
+
* Gateway product copy names the builder Superblocks. Admin tool definitions
|
|
38
|
+
* still mention the retired "Clark" alias in places; rewrite for this surface
|
|
39
|
+
* so clients do not reintroduce that name.
|
|
40
|
+
*/
|
|
41
|
+
export declare function superblocksProductCopy(text: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Registers customer-profile Admin MCP tools on the same McpServer as Builder
|
|
44
|
+
* tools. Each call runs under `runWithAuthContext` with the Gateway's
|
|
45
|
+
* downstream credential, against the `serverUrl` that credential belongs to.
|
|
46
|
+
*/
|
|
47
|
+
export declare function registerAdminTools(server: McpServer, options: RegisterAdminToolsOptions): ToolRegistry;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { buildToolRegistry, executeToolCall, runWithAuthContext, toolDefinitions, } from "@superblocksteam/mcp-server";
|
|
3
|
+
import { gatewayDebug, gatewayDebugStack } from "../../debug.js";
|
|
4
|
+
/**
|
|
5
|
+
* Loose JSON-Schema -> Zod conversion so `registerTool` can list Admin tools
|
|
6
|
+
* with usable shapes. Handlers still validate through `@superblocksteam/mcp-server`.
|
|
7
|
+
*/
|
|
8
|
+
export function jsonSchemaToZodShape(inputSchema, options = {}) {
|
|
9
|
+
const properties = inputSchema?.properties ?? {};
|
|
10
|
+
const required = new Set(inputSchema?.required ?? []);
|
|
11
|
+
for (const key of options.optionalKeys ?? []) {
|
|
12
|
+
required.delete(key);
|
|
13
|
+
}
|
|
14
|
+
const shape = {};
|
|
15
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
16
|
+
let field = jsonSchemaPropertyToZod(prop ?? {});
|
|
17
|
+
if (!required.has(key)) {
|
|
18
|
+
field = field.optional();
|
|
19
|
+
}
|
|
20
|
+
shape[key] = field;
|
|
21
|
+
}
|
|
22
|
+
return shape;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Arguments the gateway fills in itself, so the model never has to go find
|
|
26
|
+
* them. A required `base_url` reads as missing information: rather than call
|
|
27
|
+
* the tool it was asked for, a client goes hunting for the environment first.
|
|
28
|
+
*/
|
|
29
|
+
const GATEWAY_SUPPLIED_ARGUMENTS = {
|
|
30
|
+
base_url: "Omit this. The gateway always sends its own Superblocks Server URL; any value here is replaced.",
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Admin tools the gateway keeps to itself.
|
|
34
|
+
*
|
|
35
|
+
* `get_auth_status` answers questions already settled before the model sees a
|
|
36
|
+
* tool (the CLI session already authenticated; the gateway injects its Server URL).
|
|
37
|
+
*
|
|
38
|
+
* `checkout_application` writes caller-controlled paths on the gateway host
|
|
39
|
+
* filesystem. Keep that off stdio MCP until a sandboxed destination model lands;
|
|
40
|
+
* use the CLI/workspace checkout path instead.
|
|
41
|
+
*/
|
|
42
|
+
const OMITTED_ADMIN_TOOLS = new Set([
|
|
43
|
+
"checkout_application",
|
|
44
|
+
"get_auth_status",
|
|
45
|
+
]);
|
|
46
|
+
function gatewayAdminInputShape(inputSchema) {
|
|
47
|
+
const shape = jsonSchemaToZodShape(inputSchema, {
|
|
48
|
+
optionalKeys: Object.keys(GATEWAY_SUPPLIED_ARGUMENTS),
|
|
49
|
+
});
|
|
50
|
+
for (const [key, description] of Object.entries(GATEWAY_SUPPLIED_ARGUMENTS)) {
|
|
51
|
+
const field = shape[key];
|
|
52
|
+
if (field) {
|
|
53
|
+
shape[key] = field.describe(description);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return shape;
|
|
57
|
+
}
|
|
58
|
+
function jsonSchemaPropertyToZod(prop) {
|
|
59
|
+
if (prop.enum && prop.enum.length > 0) {
|
|
60
|
+
const values = prop.enum.filter((v) => typeof v === "string");
|
|
61
|
+
if (values.length === prop.enum.length && values.length > 0) {
|
|
62
|
+
return z.enum(values);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const rawType = Array.isArray(prop.type) ? prop.type[0] : prop.type;
|
|
66
|
+
switch (rawType) {
|
|
67
|
+
case "string":
|
|
68
|
+
return z.string();
|
|
69
|
+
case "number":
|
|
70
|
+
case "integer":
|
|
71
|
+
return z.number();
|
|
72
|
+
case "boolean":
|
|
73
|
+
return z.boolean();
|
|
74
|
+
case "array":
|
|
75
|
+
return z.array(prop.items ? jsonSchemaPropertyToZod(prop.items) : z.unknown());
|
|
76
|
+
case "object":
|
|
77
|
+
if (prop.properties) {
|
|
78
|
+
return z
|
|
79
|
+
.object(jsonSchemaToZodShape({
|
|
80
|
+
type: "object",
|
|
81
|
+
properties: prop.properties,
|
|
82
|
+
required: prop.required,
|
|
83
|
+
}))
|
|
84
|
+
.passthrough();
|
|
85
|
+
}
|
|
86
|
+
return z.record(z.string(), z.unknown());
|
|
87
|
+
default:
|
|
88
|
+
return z.unknown();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function resolveToken(options, auth) {
|
|
92
|
+
if (options.resolveApiKey) {
|
|
93
|
+
return options.resolveApiKey(auth);
|
|
94
|
+
}
|
|
95
|
+
if (options.apiKey) {
|
|
96
|
+
return Promise.resolve(options.apiKey);
|
|
97
|
+
}
|
|
98
|
+
return Promise.reject(new Error("Admin tools require apiKey or resolveApiKey"));
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Gateway product copy names the builder Superblocks. Admin tool definitions
|
|
102
|
+
* still mention the retired "Clark" alias in places; rewrite for this surface
|
|
103
|
+
* so clients do not reintroduce that name.
|
|
104
|
+
*/
|
|
105
|
+
export function superblocksProductCopy(text) {
|
|
106
|
+
return text
|
|
107
|
+
.replaceAll(/\bClark AI Apps\b/g, "Superblocks AI Apps")
|
|
108
|
+
.replaceAll(/\bClark\b/g, "Superblocks");
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Registers customer-profile Admin MCP tools on the same McpServer as Builder
|
|
112
|
+
* tools. Each call runs under `runWithAuthContext` with the Gateway's
|
|
113
|
+
* downstream credential, against the `serverUrl` that credential belongs to.
|
|
114
|
+
*/
|
|
115
|
+
export function registerAdminTools(server, options) {
|
|
116
|
+
const registry = buildToolRegistry(toolDefinitions, "customer", {
|
|
117
|
+
requireEntitledPlan: options.localAgentMode !== true,
|
|
118
|
+
});
|
|
119
|
+
for (const tool of registry.listTools()) {
|
|
120
|
+
if (OMITTED_ADMIN_TOOLS.has(tool.name)) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const definition = toolDefinitions.find((d) => d.name === tool.name);
|
|
124
|
+
server.registerTool(tool.name, {
|
|
125
|
+
annotations: tool.annotations,
|
|
126
|
+
description: superblocksProductCopy(tool.description ?? ""),
|
|
127
|
+
inputSchema: gatewayAdminInputShape(tool.inputSchema),
|
|
128
|
+
title: tool.title ?? definition?.title,
|
|
129
|
+
}, async (args, extra) => {
|
|
130
|
+
gatewayDebugStack("admin tool started", {
|
|
131
|
+
"tool.argument_keys": Object.keys(args).sort(),
|
|
132
|
+
"tool.name": tool.name,
|
|
133
|
+
});
|
|
134
|
+
try {
|
|
135
|
+
const token = await resolveToken(options, extra.authInfo);
|
|
136
|
+
const baseUrl = options.serverUrl.replace(/\/$/, "");
|
|
137
|
+
const callArgs = {
|
|
138
|
+
...args,
|
|
139
|
+
// Overwritten, never merged. The credential this call runs with belongs
|
|
140
|
+
// to the Gateway's own control plane, so a base_url from the model
|
|
141
|
+
// would send that credential to a host the model chose - reachable
|
|
142
|
+
// through any prompt injection that gets as far as a tool call.
|
|
143
|
+
base_url: baseUrl,
|
|
144
|
+
};
|
|
145
|
+
const result = await runWithAuthContext({ baseUrl, token }, () => executeToolCall(tool.name, callArgs, registry));
|
|
146
|
+
gatewayDebug("admin tool finished", {
|
|
147
|
+
"result.kind": "success",
|
|
148
|
+
"tool.name": tool.name,
|
|
149
|
+
});
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
gatewayDebug("admin tool finished", {
|
|
154
|
+
"error.type": error instanceof Error ? error.constructor.name : typeof error,
|
|
155
|
+
"result.kind": "error",
|
|
156
|
+
"tool.name": tool.name,
|
|
157
|
+
});
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return registry;
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=admin-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-tools.js","sourceRoot":"","sources":["../../../src/transports/mcp/admin-tools.ts"],"names":[],"mappings":"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"}
|