@vurb/swarm 3.8.2
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/README.md +315 -0
- package/dist/NamespaceRewriter.d.ts +45 -0
- package/dist/NamespaceRewriter.d.ts.map +1 -0
- package/dist/NamespaceRewriter.js +70 -0
- package/dist/NamespaceRewriter.js.map +1 -0
- package/dist/ReturnTripInjector.d.ts +62 -0
- package/dist/ReturnTripInjector.d.ts.map +1 -0
- package/dist/ReturnTripInjector.js +120 -0
- package/dist/ReturnTripInjector.js.map +1 -0
- package/dist/SwarmGateway.d.ts +146 -0
- package/dist/SwarmGateway.d.ts.map +1 -0
- package/dist/SwarmGateway.js +347 -0
- package/dist/SwarmGateway.js.map +1 -0
- package/dist/UpstreamMcpClient.d.ts +85 -0
- package/dist/UpstreamMcpClient.d.ts.map +1 -0
- package/dist/UpstreamMcpClient.js +266 -0
- package/dist/UpstreamMcpClient.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Federated Handoff Protocol — Upstream MCP Client
|
|
3
|
+
*
|
|
4
|
+
* Outbound MCP client that tunnels from the SwarmGateway to an upstream
|
|
5
|
+
* micro-server. Implements:
|
|
6
|
+
*
|
|
7
|
+
* - **AbortSignal cascade**: closing the parent connection aborts the tunnel
|
|
8
|
+
* - **Connect timeout**: the timeout AbortController cancels the in-flight HTTP request
|
|
9
|
+
* - **Idle timeout**: closes zombie tunnels after configurable inactivity
|
|
10
|
+
* - **Progress passthrough**: pipes `notifications/progress` and
|
|
11
|
+
* `notifications/message` from upstream back to the LLM client
|
|
12
|
+
*
|
|
13
|
+
* @module
|
|
14
|
+
*/
|
|
15
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
16
|
+
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
17
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
18
|
+
/**
|
|
19
|
+
* Outbound MCP client for the SwarmGateway B2BUA face.
|
|
20
|
+
*
|
|
21
|
+
* One instance per active handoff session. Closed via `dispose()` or
|
|
22
|
+
* automatically when the idle timeout or AbortSignal fires.
|
|
23
|
+
*/
|
|
24
|
+
export class UpstreamMcpClient {
|
|
25
|
+
_targetUri;
|
|
26
|
+
_config;
|
|
27
|
+
_abortController;
|
|
28
|
+
// keep ref to listener so we can remove it in dispose() to prevent
|
|
29
|
+
// the closure from holding a reference to this instance after the session ends.
|
|
30
|
+
_onParentAbort;
|
|
31
|
+
_parentSignal;
|
|
32
|
+
_client;
|
|
33
|
+
_idleTimer;
|
|
34
|
+
_progressForwarder;
|
|
35
|
+
// tracks concurrent tool calls to suspend the idle timer during execution
|
|
36
|
+
_activeCalls = 0;
|
|
37
|
+
// set to true by dispose() to prevent orphan timers from firing
|
|
38
|
+
_disposed = false;
|
|
39
|
+
constructor(_targetUri, _config, parentSignal) {
|
|
40
|
+
this._targetUri = _targetUri;
|
|
41
|
+
this._config = _config;
|
|
42
|
+
this._abortController = new AbortController();
|
|
43
|
+
// save ref to both signal and listener for cleanup in dispose().
|
|
44
|
+
// Using `{ once: true }` auto-removes on fire, but NOT on voluntary dispose().
|
|
45
|
+
// Without explicit removal, the closure keeps this entire instance alive
|
|
46
|
+
// in memory for as long as parentSignal lives (typically the MCP connection lifetime).
|
|
47
|
+
this._onParentAbort = () => this._abortController.abort();
|
|
48
|
+
this._parentSignal = parentSignal;
|
|
49
|
+
parentSignal.addEventListener('abort', this._onParentAbort, { once: true });
|
|
50
|
+
}
|
|
51
|
+
/** Register a forwarder for upstream progress/logging notifications. */
|
|
52
|
+
setProgressForwarder(forwarder) {
|
|
53
|
+
this._progressForwarder = forwarder;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Connect to the upstream server.
|
|
57
|
+
*
|
|
58
|
+
* @throws `Error` with `code: 'UPSTREAM_CONNECT_TIMEOUT'` if unreachable within `connectTimeoutMs`
|
|
59
|
+
*/
|
|
60
|
+
async connect() {
|
|
61
|
+
const headers = {
|
|
62
|
+
'x-vurb-delegation': this._config.delegationToken,
|
|
63
|
+
};
|
|
64
|
+
if (this._config.traceparent) {
|
|
65
|
+
headers['traceparent'] = this._config.traceparent;
|
|
66
|
+
}
|
|
67
|
+
// use a dedicated connect-phase controller merged with the session-lifetime
|
|
68
|
+
// signal. The SSEClientTransport holds the signal for the full SSE stream lifetime,
|
|
69
|
+
// so it must be the session signal — not the one-shot connect timeout signal.
|
|
70
|
+
// We create a merged signal that fires on either: (a) connect timeout, or (b) session abort.
|
|
71
|
+
const connectController = new AbortController();
|
|
72
|
+
// AbortSignal.any is available in Node ≥18.17.
|
|
73
|
+
// the old fallback used only connectController.signal, ignoring
|
|
74
|
+
// _abortController.signal entirely on older runtimes. This meant a parent-abort
|
|
75
|
+
// during connect() would not cancel the in-flight TCP/HTTP request immediately.
|
|
76
|
+
// The manual merge below ensures both signals are observed on all runtimes.
|
|
77
|
+
const connectSignal = typeof AbortSignal.any === 'function'
|
|
78
|
+
? AbortSignal.any([connectController.signal, this._abortController.signal])
|
|
79
|
+
: (() => {
|
|
80
|
+
const mergedController = new AbortController();
|
|
81
|
+
const abort = () => mergedController.abort();
|
|
82
|
+
connectController.signal.addEventListener('abort', abort, { once: true });
|
|
83
|
+
this._abortController.signal.addEventListener('abort', abort, { once: true });
|
|
84
|
+
return mergedController.signal;
|
|
85
|
+
})();
|
|
86
|
+
const transport = this._resolveTransport(headers, connectSignal);
|
|
87
|
+
// do NOT set this._client before the promise settles.
|
|
88
|
+
// We use a local variable during connect; only assign to this._client on success.
|
|
89
|
+
const client = new Client({ name: 'vurb-swarm-gateway', version: '0.1.0' });
|
|
90
|
+
await new Promise((resolve, reject) => {
|
|
91
|
+
const timer = setTimeout(() => {
|
|
92
|
+
connectController.abort(); // cancel the in-flight request
|
|
93
|
+
reject(Object.assign(new Error(`[vurb/swarm] Upstream "${this._targetUri}" did not respond within ${this._config.connectTimeoutMs}ms.`), { code: 'UPSTREAM_CONNECT_TIMEOUT' }));
|
|
94
|
+
}, this._config.connectTimeoutMs);
|
|
95
|
+
void client.connect(transport).then(() => {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
resolve();
|
|
98
|
+
}, (err) => {
|
|
99
|
+
clearTimeout(timer);
|
|
100
|
+
reject(err);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
// only set this._client AFTER a successful connect.
|
|
104
|
+
// If the promise above rejected (timeout, network error), this line is never reached
|
|
105
|
+
// and _client remains undefined — _assertConnected() correctly blocks further calls.
|
|
106
|
+
this._client = client;
|
|
107
|
+
this._wireNotifications();
|
|
108
|
+
this._resetIdleTimer();
|
|
109
|
+
}
|
|
110
|
+
/** List all tools exposed by the upstream server. */
|
|
111
|
+
async listTools() {
|
|
112
|
+
this._assertConnected();
|
|
113
|
+
// apply the same idle-timer suspension pattern as callTool.
|
|
114
|
+
// Without this, a slow upstream (large tool list) could cause the idle timer
|
|
115
|
+
// to fire mid-awaiting the listTools RPC and close the tunnel prematurely.
|
|
116
|
+
this._activeCalls++;
|
|
117
|
+
clearTimeout(this._idleTimer);
|
|
118
|
+
this._idleTimer = undefined;
|
|
119
|
+
try {
|
|
120
|
+
// declare inside the try block (same pattern as callTool)
|
|
121
|
+
// so TypeScript never infers `response` as `T | undefined` outside it.
|
|
122
|
+
const response = await this._client.listTools();
|
|
123
|
+
return response.tools;
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
this._activeCalls--;
|
|
127
|
+
if (this._activeCalls === 0) {
|
|
128
|
+
this._resetIdleTimer();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Call a tool on the upstream server.
|
|
134
|
+
* @param name - Tool name (without namespace prefix)
|
|
135
|
+
* @param args - Tool arguments
|
|
136
|
+
* @param signal - AbortSignal from the parent tools/call request
|
|
137
|
+
*/
|
|
138
|
+
async callTool(name, args, signal) {
|
|
139
|
+
this._assertConnected();
|
|
140
|
+
// suspend the idle timer while a tool call is in progress.
|
|
141
|
+
// Previously, the timer was reset only at the START of the call. A long-running
|
|
142
|
+
// tool (e.g., 4+ minutes) would not reset the timer on completion, causing
|
|
143
|
+
// the tunnel to close while the tool is still executing. Now we pause the
|
|
144
|
+
// timer during the call and restart it only after the call completes.
|
|
145
|
+
this._activeCalls++;
|
|
146
|
+
clearTimeout(this._idleTimer);
|
|
147
|
+
this._idleTimer = undefined;
|
|
148
|
+
// declare result inside the try block (same pattern as listTools after ).
|
|
149
|
+
// With `let result` outside, TypeScript infers `result: T | undefined` — if callTool()
|
|
150
|
+
// throws, `result.content` below would never execute but TS doesn't know that.
|
|
151
|
+
// Moving everything inside the try makes the types strict and intent crystal-clear.
|
|
152
|
+
try {
|
|
153
|
+
const result = await this._client.callTool({ name, arguments: args }, undefined, { signal });
|
|
154
|
+
// ToolResponse only supports type:'text'. Produce human-readable representations
|
|
155
|
+
// for non-text blocks (image, resource, audio) rather than losing type information
|
|
156
|
+
// via a raw JSON.stringify. LLMs benefit from knowing what was returned.
|
|
157
|
+
const content = result.content.map(c => {
|
|
158
|
+
if (c.type === 'text') {
|
|
159
|
+
return { type: 'text', text: c.text ?? '' };
|
|
160
|
+
}
|
|
161
|
+
if (c.type === 'image') {
|
|
162
|
+
const size = c.data?.length ?? 0;
|
|
163
|
+
return { type: 'text', text: `[Image (${c.mimeType ?? 'unknown type'}); ${size} base64 chars — not renderable as text]` };
|
|
164
|
+
}
|
|
165
|
+
if (c.type === 'resource') {
|
|
166
|
+
return { type: 'text', text: `[Resource: ${JSON.stringify(c.resource)}]` };
|
|
167
|
+
}
|
|
168
|
+
return { type: 'text', text: `[${c.type}: ${JSON.stringify(c)}]` };
|
|
169
|
+
});
|
|
170
|
+
return { content, isError: result.isError === true };
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
this._activeCalls--;
|
|
174
|
+
// Restart the idle timer only when no other calls are pending
|
|
175
|
+
if (this._activeCalls === 0) {
|
|
176
|
+
this._resetIdleTimer();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/** Close the connection and clear all timers. */
|
|
181
|
+
async dispose() {
|
|
182
|
+
// mark as disposed FIRST so any in-flight finally blocks
|
|
183
|
+
// (e.g. callTool's _activeCalls--) won't restart the idle timer.
|
|
184
|
+
this._disposed = true;
|
|
185
|
+
// remove the parent signal listener to break the closure reference.
|
|
186
|
+
// Without this, parentSignal retains a reference to this instance for its entire
|
|
187
|
+
// lifetime (usually the MCP connection), preventing GC after the session ends.
|
|
188
|
+
this._parentSignal.removeEventListener('abort', this._onParentAbort);
|
|
189
|
+
clearTimeout(this._idleTimer);
|
|
190
|
+
this._abortController.abort();
|
|
191
|
+
try {
|
|
192
|
+
await this._client?.close();
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// Best-effort close — swallow errors during cleanup
|
|
196
|
+
}
|
|
197
|
+
this._client = undefined;
|
|
198
|
+
}
|
|
199
|
+
// ── Private ─────────────────────────────────────────────
|
|
200
|
+
_assertConnected() {
|
|
201
|
+
if (!this._client) {
|
|
202
|
+
throw new Error('[vurb/swarm] UpstreamMcpClient is not connected. Call connect() first.');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Build the MCP transport for the given headers and connect-phase abort signal.
|
|
207
|
+
*
|
|
208
|
+
* The `connectSignal` is only used for the initial TCP/SSE handshake.
|
|
209
|
+
* It is NOT the same as the session lifetime signal (`_abortController`).
|
|
210
|
+
*
|
|
211
|
+
* `mcps://` (secure MCP) is mapped to `https://`.
|
|
212
|
+
*/
|
|
213
|
+
_resolveTransport(headers, sessionSignal) {
|
|
214
|
+
const mode = this._config.transport ?? 'auto';
|
|
215
|
+
const useHttp = mode === 'http' ||
|
|
216
|
+
(mode === 'auto' && typeof globalThis['EdgeRuntime'] !== 'undefined');
|
|
217
|
+
const url = new URL(this._targetUri
|
|
218
|
+
.replace(/^mcps:\/\//, 'https://') // secure MCP scheme
|
|
219
|
+
.replace(/^mcp:\/\//, 'http://'));
|
|
220
|
+
if (useHttp) {
|
|
221
|
+
return new StreamableHTTPClientTransport(url, {
|
|
222
|
+
requestInit: { headers, signal: sessionSignal },
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return new SSEClientTransport(url, {
|
|
226
|
+
requestInit: { headers, signal: sessionSignal },
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Register notification handlers with correctly typed wrappers.
|
|
231
|
+
*
|
|
232
|
+
* The MCP SDK's `setNotificationHandler` expects a Zod schema as the first argument.
|
|
233
|
+
* We create a minimal compliant object that satisfies the runtime duck-type without
|
|
234
|
+
* importing Zod directly (the SDK validates at transport level, not via our schema).
|
|
235
|
+
* The `as never` cast is retained on the schema only — all handler types are explicit.
|
|
236
|
+
*/
|
|
237
|
+
_wireNotifications() {
|
|
238
|
+
if (!this._client)
|
|
239
|
+
return;
|
|
240
|
+
// include safeParse() so future MCP SDK versions that call it
|
|
241
|
+
// don't silently break notification forwarding. The cast to `never` is retained
|
|
242
|
+
// only on the schema argument — handler types remain explicit.
|
|
243
|
+
const makeSchema = (method) => ({
|
|
244
|
+
parse: (v) => v,
|
|
245
|
+
safeParse: (v) => ({ success: true, data: v }),
|
|
246
|
+
shape: { method: { value: method } },
|
|
247
|
+
});
|
|
248
|
+
this._client.setNotificationHandler(makeSchema('notifications/progress'), (n) => {
|
|
249
|
+
this._progressForwarder?.({ method: 'notifications/progress', params: n.params ?? {} });
|
|
250
|
+
});
|
|
251
|
+
this._client.setNotificationHandler(makeSchema('notifications/message'), (n) => {
|
|
252
|
+
this._progressForwarder?.({ method: 'notifications/message', params: n.params ?? {} });
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
_resetIdleTimer() {
|
|
256
|
+
// never restart the timer after dispose() — that would schedule
|
|
257
|
+
// another dispose() call on a dead client, leaking the timer handle.
|
|
258
|
+
if (this._disposed)
|
|
259
|
+
return;
|
|
260
|
+
clearTimeout(this._idleTimer);
|
|
261
|
+
this._idleTimer = setTimeout(() => {
|
|
262
|
+
void this.dispose();
|
|
263
|
+
}, this._config.idleTimeoutMs);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=UpstreamMcpClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UpstreamMcpClient.js","sourceRoot":"","sources":["../src/UpstreamMcpClient.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vurb/swarm — Federated Handoff Protocol
|
|
3
|
+
*
|
|
4
|
+
* Multi-agent orchestration for Vurb MCP servers.
|
|
5
|
+
* Implements the SwarmGateway B2BUA pattern for secure, efficient
|
|
6
|
+
* agent handoffs with zero-trust delegation and distributed tracing.
|
|
7
|
+
*
|
|
8
|
+
* @example Gateway setup
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { SwarmGateway } from '@vurb/swarm';
|
|
11
|
+
* import { f } from '@vurb/core';
|
|
12
|
+
*
|
|
13
|
+
* const gateway = new SwarmGateway({
|
|
14
|
+
* registry: { finance: 'http://finance-agent:8081' },
|
|
15
|
+
* delegationSecret: process.env.VURB_DELEGATION_SECRET!,
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* export const triage = f.tool('system.triage')
|
|
19
|
+
* .withEnum('domain', ['finance', 'devops'])
|
|
20
|
+
* .withString('context', 'What the user needs.')
|
|
21
|
+
* .handle(async (input) => f.handoff(`mcp://${input.domain}-agent`, {
|
|
22
|
+
* carryOverState: { intent: input.context },
|
|
23
|
+
* reason: `Triage → ${input.domain}`,
|
|
24
|
+
* }));
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example Upstream micro-server
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { requireGatewayClearance } from '@vurb/core';
|
|
30
|
+
*
|
|
31
|
+
* export const refund = f.tool('finance.refund')
|
|
32
|
+
* .use(requireGatewayClearance(process.env.VURB_DELEGATION_SECRET!))
|
|
33
|
+
* .withString('invoiceId', 'Invoice ID')
|
|
34
|
+
* .handle(async (input, ctx) => success(await stripe.refund(input.invoiceId)));
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @module
|
|
38
|
+
*/
|
|
39
|
+
export { SwarmGateway } from './SwarmGateway.js';
|
|
40
|
+
export type { SwarmGatewayConfig } from './SwarmGateway.js';
|
|
41
|
+
export { UpstreamMcpClient } from './UpstreamMcpClient.js';
|
|
42
|
+
export type { UpstreamMcpClientConfig, ProgressForwarder, ProgressNotification } from './UpstreamMcpClient.js';
|
|
43
|
+
export { NamespaceRewriter, NamespaceError } from './NamespaceRewriter.js';
|
|
44
|
+
export { injectReturnTripTool, formatSafeReturn } from './ReturnTripInjector.js';
|
|
45
|
+
export { handoff, isHandoffResponse, mintDelegationToken, verifyDelegationToken, HandoffAuthError, InMemoryHandoffStateStore, requireGatewayClearance, } from '@vurb/core';
|
|
46
|
+
export type { HandoffPayload, HandoffResponse, HandoffStateStore, DelegationClaims, GatewayClearanceContext, } from '@vurb/core';
|
|
47
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAG/G,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAMjF,OAAO,EACH,OAAO,EAAE,iBAAiB,EAC1B,mBAAmB,EAAE,qBAAqB,EAAE,gBAAgB,EAC5D,yBAAyB,EACzB,uBAAuB,GAC1B,MAAM,YAAY,CAAC;AACpB,YAAY,EACR,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAClD,gBAAgB,EAChB,uBAAuB,GAC1B,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vurb/swarm — Federated Handoff Protocol
|
|
3
|
+
*
|
|
4
|
+
* Multi-agent orchestration for Vurb MCP servers.
|
|
5
|
+
* Implements the SwarmGateway B2BUA pattern for secure, efficient
|
|
6
|
+
* agent handoffs with zero-trust delegation and distributed tracing.
|
|
7
|
+
*
|
|
8
|
+
* @example Gateway setup
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { SwarmGateway } from '@vurb/swarm';
|
|
11
|
+
* import { f } from '@vurb/core';
|
|
12
|
+
*
|
|
13
|
+
* const gateway = new SwarmGateway({
|
|
14
|
+
* registry: { finance: 'http://finance-agent:8081' },
|
|
15
|
+
* delegationSecret: process.env.VURB_DELEGATION_SECRET!,
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* export const triage = f.tool('system.triage')
|
|
19
|
+
* .withEnum('domain', ['finance', 'devops'])
|
|
20
|
+
* .withString('context', 'What the user needs.')
|
|
21
|
+
* .handle(async (input) => f.handoff(`mcp://${input.domain}-agent`, {
|
|
22
|
+
* carryOverState: { intent: input.context },
|
|
23
|
+
* reason: `Triage → ${input.domain}`,
|
|
24
|
+
* }));
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example Upstream micro-server
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { requireGatewayClearance } from '@vurb/core';
|
|
30
|
+
*
|
|
31
|
+
* export const refund = f.tool('finance.refund')
|
|
32
|
+
* .use(requireGatewayClearance(process.env.VURB_DELEGATION_SECRET!))
|
|
33
|
+
* .withString('invoiceId', 'Invoice ID')
|
|
34
|
+
* .handle(async (input, ctx) => success(await stripe.refund(input.invoiceId)));
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @module
|
|
38
|
+
*/
|
|
39
|
+
// ── Gateway ──────────────────────────────────────────────
|
|
40
|
+
export { SwarmGateway } from './SwarmGateway.js';
|
|
41
|
+
// ── Client (upstream connection) ─────────────────────────
|
|
42
|
+
export { UpstreamMcpClient } from './UpstreamMcpClient.js';
|
|
43
|
+
// ── Utilities ────────────────────────────────────────────
|
|
44
|
+
export { NamespaceRewriter, NamespaceError } from './NamespaceRewriter.js';
|
|
45
|
+
export { injectReturnTripTool, formatSafeReturn } from './ReturnTripInjector.js';
|
|
46
|
+
// ── FHP primitives (re-exported from @vurb/core) ────────
|
|
47
|
+
// Provided here for convenience — teams importing from @vurb/swarm
|
|
48
|
+
// get the full FHP API surface without a separate @vurb/core import.
|
|
49
|
+
// The canonical source is always @vurb/core.
|
|
50
|
+
export { handoff, isHandoffResponse, mintDelegationToken, verifyDelegationToken, HandoffAuthError, InMemoryHandoffStateStore, requireGatewayClearance, } from '@vurb/core';
|
|
51
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,4DAA4D;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,4DAA4D;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAG3D,4DAA4D;AAC5D,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEjF,2DAA2D;AAC3D,mEAAmE;AACnE,qEAAqE;AACrE,6CAA6C;AAC7C,OAAO,EACH,OAAO,EAAE,iBAAiB,EAC1B,mBAAmB,EAAE,qBAAqB,EAAE,gBAAgB,EAC5D,yBAAyB,EACzB,uBAAuB,GAC1B,MAAM,YAAY,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vurb/swarm",
|
|
3
|
+
"version": "3.8.2",
|
|
4
|
+
"description": "Federated Handoff Protocol for Vurb — SwarmGateway B2BUA, multi-agent orchestration, zero-trust delegation, and upstream MCP client tunneling.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "rimraf dist && tsc",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"vurb",
|
|
22
|
+
"swarm",
|
|
23
|
+
"multi-agent",
|
|
24
|
+
"handoff",
|
|
25
|
+
"gateway",
|
|
26
|
+
"b2bua",
|
|
27
|
+
"federation",
|
|
28
|
+
"ai",
|
|
29
|
+
"llm",
|
|
30
|
+
"delegation",
|
|
31
|
+
"zero-trust"
|
|
32
|
+
],
|
|
33
|
+
"author": "Renato Marinho",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/vinkius-labs/vurb.ts.git",
|
|
37
|
+
"directory": "packages/swarm"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/vinkius-labs/vurb.ts/issues"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://vurb.vinkius.com/",
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"README.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
],
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18.0.0"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@vurb/core": "^3.8.0",
|
|
56
|
+
"@modelcontextprotocol/sdk": "^1.12.1"
|
|
57
|
+
},
|
|
58
|
+
"license": "Apache-2.0",
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@vurb/core": ">=3.8.0",
|
|
61
|
+
"rimraf": "^6.0.0",
|
|
62
|
+
"vitest": "^3.0.0"
|
|
63
|
+
}
|
|
64
|
+
}
|