@orkestrel/mcp 0.0.28 → 0.0.29
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 +11 -14
- package/dist/src/browser/index.d.ts +63 -65
- package/dist/src/browser/index.js +44 -44
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +233 -176
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +442 -366
- package/dist/src/core/index.d.ts +442 -366
- package/dist/src/core/index.js +233 -176
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +109 -109
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +209 -203
- package/dist/src/server/index.d.ts +209 -203
- package/dist/src/server/index.js +109 -109
- package/dist/src/server/index.js.map +1 -1
- package/package.json +24 -25
package/README.md
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
# @orkestrel/mcp
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
HTTP over `@orkestrel/router` / `@orkestrel/server`, WebSocket over
|
|
11
|
-
`@orkestrel/websocket`, and stdio over `@orkestrel/process`) lives one layer
|
|
12
|
-
out (`src/server`), each mechanism, not policy. Part of the `@orkestrel` line.
|
|
3
|
+
> The Model Context Protocol layer: a typed JSON-RPC 2.0 client/server pair with pluggable
|
|
4
|
+
> HTTP, WebSocket, stdio, and browser transports.
|
|
5
|
+
|
|
6
|
+
Bridge the `@orkestrel/tool` registry to MCP: `createMCPServer` exposes a live
|
|
7
|
+
`ToolManagerInterface` to any MCP client, and `createMCPClient` drives a remote MCP server
|
|
8
|
+
and surfaces its tools as local `ToolInterface`s. No agent runtime is required. Part of the
|
|
9
|
+
`@orkestrel` line.
|
|
13
10
|
|
|
14
11
|
## Install
|
|
15
12
|
|
|
@@ -30,8 +27,6 @@ npm install @orkestrel/mcp
|
|
|
30
27
|
|
|
31
28
|
## Usage
|
|
32
29
|
|
|
33
|
-
Expose a tool registry over MCP, mounted on the HTTP spine:
|
|
34
|
-
|
|
35
30
|
```ts
|
|
36
31
|
import { createMCPLegacy, createMCPServer } from '@orkestrel/mcp'
|
|
37
32
|
import { createMCPRoutes } from '@orkestrel/mcp/server'
|
|
@@ -46,6 +41,8 @@ const routes = createMCPRoutes(createMCPLegacy(mcp)) // answers `initialize` too
|
|
|
46
41
|
router.add(routes)
|
|
47
42
|
```
|
|
48
43
|
|
|
44
|
+
This example exposes a tool registry over MCP, mounted on the HTTP spine.
|
|
45
|
+
|
|
49
46
|
Drive a remote MCP server as a client, over the same transport-agnostic core:
|
|
50
47
|
|
|
51
48
|
```ts
|
|
@@ -60,7 +57,7 @@ const tools = await client.tools()
|
|
|
60
57
|
const outcome = await client.call('add', { x: 2, y: 5 })
|
|
61
58
|
```
|
|
62
59
|
|
|
63
|
-
The
|
|
60
|
+
The same `MCPClient` drives a `createWebSocketClientTransport` or
|
|
64
61
|
`createStdioClientTransport` instead — only the injected transport changes.
|
|
65
62
|
|
|
66
63
|
## Guide
|
|
@@ -96,7 +93,7 @@ The publication facts, each with its number. Full detail, plus every
|
|
|
96
93
|
protocol-level gap and non-goal, is in
|
|
97
94
|
[the MCP guide](https://github.com/orkestrel/mcp/blob/main/guides/mcp.md#declared-packaging-limits).
|
|
98
95
|
|
|
99
|
-
- **No IDE evidence.** See
|
|
96
|
+
- **No IDE evidence.** See the [wire conformance evidence](https://github.com/orkestrel/mcp#proven). The conformance number is about the wire
|
|
100
97
|
and does not transfer to a host application.
|
|
101
98
|
- **No top-level `types` field.** Every `exports` subpath carries a `types`
|
|
102
99
|
condition, so `node16`, `nodenext`, and `bundler` resolution find
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
import { EmitterInterface } from '@orkestrel/emitter';
|
|
2
|
-
import { HTTPClientTransportOptions } from '@orkestrel/mcp';
|
|
3
|
-
import { JSONRPCMessage } from '@orkestrel/mcp';
|
|
4
|
-
import { MCPMessageTransportEventMap } from '@orkestrel/mcp';
|
|
5
|
-
import { MCPMessageTransportInterface } from '@orkestrel/mcp';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { MCPTransportInterface as MCPTransportInterface_2 } from '@orkestrel/mcp';
|
|
10
|
-
import { ToolManagerInterface } from '@orkestrel/tool';
|
|
1
|
+
import type { EmitterInterface } from '@orkestrel/emitter';
|
|
2
|
+
import type { HTTPClientTransportOptions } from '@orkestrel/mcp';
|
|
3
|
+
import type { JSONRPCMessage } from '@orkestrel/mcp';
|
|
4
|
+
import type { MCPMessageTransportEventMap } from '@orkestrel/mcp';
|
|
5
|
+
import type { MCPMessageTransportInterface } from '@orkestrel/mcp';
|
|
6
|
+
import type { MCPServerInterface } from '@orkestrel/mcp';
|
|
7
|
+
import type { MCPTransportInterface } from '@orkestrel/mcp';
|
|
8
|
+
import type { ToolManagerInterface } from '@orkestrel/tool';
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
|
-
* Creates the HTTP
|
|
11
|
+
* Creates the HTTP client transport for an
|
|
14
12
|
* {@link import('@orkestrel/mcp').MCPClientInterface} — a {@link MCPMessageTransportInterface}
|
|
15
|
-
* that drives a
|
|
13
|
+
* that drives a remote Streamable-HTTP MCP server over the native `fetch`.
|
|
16
14
|
*
|
|
17
15
|
* @remarks
|
|
18
16
|
* It returns the core {@link import('@orkestrel/mcp').HTTPClientTransport}, the same class the
|
|
@@ -24,18 +22,18 @@ import { ToolManagerInterface } from '@orkestrel/tool';
|
|
|
24
22
|
* Hand it to `createMCPClient({ transport })`: each JSON-RPC message the client
|
|
25
23
|
* sends is `POST`ed to `options.url` with `content-type: application/json` and an
|
|
26
24
|
* `Accept` of both `application/json` and `text/event-stream` (the server answers
|
|
27
|
-
* with
|
|
25
|
+
* with either — a plain JSON envelope or a Streamable-HTTP SSE `data:` event,
|
|
28
26
|
* decoded with `@orkestrel/sse`), and the reply is surfaced on the transport's
|
|
29
27
|
* `message` event for the client's id correlation. Add `options.headers` (for example, an
|
|
30
28
|
* `Authorization` bearer) to reach a guarded server. `start` / `close` hold no
|
|
31
|
-
* connection; against a
|
|
29
|
+
* connection; against a stateful server it captures the `mcp-session-id` from
|
|
32
30
|
* `initialize` and echoes it on later requests. It also captures the initialize
|
|
33
31
|
* result's `protocolVersion` and sends `mcp-protocol-version` alone on subsequent
|
|
34
32
|
* legacy requests. Modern requests instead derive `mcp-protocol-version` and
|
|
35
33
|
* `mcp-method` from the message, plus `mcp-name` only for `tools/call`, so the
|
|
36
34
|
* same `MCPClient` passes either era's protocol gates without caller wiring.
|
|
37
35
|
*
|
|
38
|
-
* @param options - `url` (the remote endpoint;
|
|
36
|
+
* @param options - `url` (the remote endpoint; required), optional `headers` merged
|
|
39
37
|
* onto every request, optional `fetch` (default `globalThis.fetch`), and optional
|
|
40
38
|
* `timeout` (ms, applied with `AbortSignal.timeout`); see
|
|
41
39
|
* {@link HTTPClientTransportOptions}
|
|
@@ -53,12 +51,12 @@ import { ToolManagerInterface } from '@orkestrel/tool';
|
|
|
53
51
|
* const tools = await client.tools()
|
|
54
52
|
* ```
|
|
55
53
|
*/
|
|
56
|
-
export declare function createHTTPClientTransport(options: HTTPClientTransportOptions):
|
|
54
|
+
export declare function createHTTPClientTransport(options: HTTPClientTransportOptions): MCPMessageTransportInterface;
|
|
57
55
|
|
|
58
56
|
/**
|
|
59
57
|
* Creates the browser-face `MessagePort` transport — a
|
|
60
58
|
* {@link import('@orkestrel/mcp').MCPTransportInterface} over a native `MessagePort`, the
|
|
61
|
-
*
|
|
59
|
+
* symmetric carrier that works as either a server or a client transport depending on
|
|
62
60
|
* which binder ({@link import('@orkestrel/mcp').bindServer} or
|
|
63
61
|
* {@link import('@orkestrel/mcp').bindClient}) it is handed to.
|
|
64
62
|
*
|
|
@@ -68,7 +66,7 @@ export declare function createHTTPClientTransport(options: HTTPClientTransportOp
|
|
|
68
66
|
* dropped, never thrown); `messageerror` is ignored (one bad frame does not close the
|
|
69
67
|
* channel); `close()` closes the port and fires `closed` exactly once.
|
|
70
68
|
*
|
|
71
|
-
* @param options - `port` (the `MessagePort` half to drive;
|
|
69
|
+
* @param options - `port` (the `MessagePort` half to drive; required); see
|
|
72
70
|
* {@link MessagePortTransportOptions}
|
|
73
71
|
* @returns A working {@link import('@orkestrel/mcp').MCPTransportInterface} over the port
|
|
74
72
|
*
|
|
@@ -86,28 +84,28 @@ export declare function createMessagePortTransport(options: MessagePortTransport
|
|
|
86
84
|
|
|
87
85
|
/**
|
|
88
86
|
* Builds {@link createScopeServer}'s `message`-event listener — the unified dispatcher that
|
|
89
|
-
* routes
|
|
87
|
+
* routes every inbound event on a hostable scope, portless or port-bearing, to the right
|
|
90
88
|
* binding.
|
|
91
89
|
*
|
|
92
90
|
* @remarks
|
|
93
|
-
* Port-bearing events (`event.ports.length > 0`) are gated by `options.accept`
|
|
91
|
+
* Port-bearing events (`event.ports.length > 0`) are gated by `options.accept` first
|
|
94
92
|
* — when the gate returns `false` the event is dropped entirely (no binding, no reply).
|
|
95
93
|
* Accepted events spawn a fresh `MessagePortTransport` over `event.ports[0]`,
|
|
96
94
|
* `bindServer` `server` onto it, and record a teardown (`unbind` then `transport.close()`)
|
|
97
|
-
* into `teardowns`
|
|
95
|
+
* into `teardowns` keyed by that port. A port already present is ignored — repeated delivery
|
|
98
96
|
* of the same `MessagePort` would create duplicate bindings over one port (→ duplicated
|
|
99
97
|
* replies), so a repeat is silently dropped.
|
|
100
98
|
*
|
|
101
|
-
* The key is what makes `teardowns` the
|
|
99
|
+
* The key is what makes `teardowns` the only place an accepted port is remembered. A separate
|
|
102
100
|
* seen-port set would be a second collection over the same lifetime, and the scope server's
|
|
103
101
|
* `stop` would have to remember to empty both — so a long-lived scope such as a Service Worker
|
|
104
102
|
* would retain every port it ever accepted, closed and unbound ones included. Membership
|
|
105
103
|
* answers "already bound?" and `clear()` drops the binding and the dedup together.
|
|
106
104
|
*
|
|
107
|
-
* This branch fires on
|
|
105
|
+
* This branch fires on either a Service-Worker-shaped scope (its normal per-client
|
|
108
106
|
* channel) or a dedicated-worker-shaped one that happens to receive a port-bearing event
|
|
109
107
|
* (the unified design's deliberate cross-case, needing no upfront shape flag). An event
|
|
110
|
-
* with
|
|
108
|
+
* with no ports and a string `data` is pushed onto `scopeTransport.deliver` (the
|
|
111
109
|
* implicit, already-bound scope channel); any other event (no ports, non-string data)
|
|
112
110
|
* is silently dropped — total, never throws.
|
|
113
111
|
*
|
|
@@ -170,7 +168,7 @@ export declare function createScopeServer(options: ScopeServerOptions, scope?: S
|
|
|
170
168
|
*
|
|
171
169
|
* @remarks
|
|
172
170
|
* `send` writes each outbound string through `scope.postMessage`. `listen`/`closed`
|
|
173
|
-
* register the
|
|
171
|
+
* register the single handler `deliver` / the underlying close path route through —
|
|
174
172
|
* the scope server's own `scope` `message`-event listener calls `deliver(event.data)`
|
|
175
173
|
* for every portless, string-payload event (there is no native registration point on
|
|
176
174
|
* the scope itself for the scope server to hand a `listen` handler to, so `deliver` is
|
|
@@ -190,20 +188,20 @@ export declare function createScopeServer(options: ScopeServerOptions, scope?: S
|
|
|
190
188
|
export declare function createScopeTransport(scope: ScopeInterface): ScopeTransportInterface;
|
|
191
189
|
|
|
192
190
|
/**
|
|
193
|
-
* Creates the browser-face WebSocket
|
|
191
|
+
* Creates the browser-face WebSocket client transport for an
|
|
194
192
|
* {@link import('@orkestrel/mcp').MCPClientInterface} — a {@link MCPMessageTransportInterface}
|
|
195
|
-
* that drives a
|
|
196
|
-
* sibling of the Node face's `createWebSocketClientTransport` (`@orkestrel/mcp/server`).
|
|
193
|
+
* that drives a remote MCP server over the native `WebSocket` global. This factory is the
|
|
194
|
+
* browser sibling of the Node face's `createWebSocketClientTransport` (`@orkestrel/mcp/server`).
|
|
197
195
|
*
|
|
198
196
|
* @remarks
|
|
199
197
|
* Hand it to `createMCPClient({ transport })`: `start()` (run by `client.connect()`)
|
|
200
198
|
* opens `new WebSocket(options.url, options.protocols)` and awaits the native
|
|
201
199
|
* `'open'` event — the RFC 6455 handshake itself is the browser's concern. Each
|
|
202
|
-
* JSON-RPC message the client `send`s before the socket opens is
|
|
200
|
+
* JSON-RPC message the client `send`s before the socket opens is queued and flushed,
|
|
203
201
|
* in order, once it does; each decoded reply is surfaced on the transport's
|
|
204
202
|
* `message` event for the client's id correlation.
|
|
205
203
|
*
|
|
206
|
-
* @param options - `url` (the remote WebSocket endpoint;
|
|
204
|
+
* @param options - `url` (the remote WebSocket endpoint; required) and optional
|
|
207
205
|
* `protocols` (the WebSocket subprotocol(s) to request); see
|
|
208
206
|
* {@link WebSocketClientTransportOptions}
|
|
209
207
|
* @returns A working {@link MCPMessageTransportInterface} over the native `WebSocket`
|
|
@@ -220,7 +218,7 @@ export declare function createScopeTransport(scope: ScopeInterface): ScopeTransp
|
|
|
220
218
|
* const tools = await client.tools()
|
|
221
219
|
* ```
|
|
222
220
|
*/
|
|
223
|
-
export declare function createWebSocketClientTransport(options: WebSocketClientTransportOptions):
|
|
221
|
+
export declare function createWebSocketClientTransport(options: WebSocketClientTransportOptions): MCPMessageTransportInterface;
|
|
224
222
|
|
|
225
223
|
/** Supplies the default server name `createScopeServer` reports (`initialize`'s `serverInfo.name`) when `options.name` is omitted. */
|
|
226
224
|
export declare const DEFAULT_MCP_SERVER_NAME = "@orkestrel/mcp";
|
|
@@ -237,33 +235,33 @@ export declare const DEFAULT_MCP_SERVER_VERSION = "1.0.0";
|
|
|
237
235
|
* - **Symmetric.** Unlike {@link import('./WebSocketClientTransport.js').WebSocketClientTransport}
|
|
238
236
|
* / {@link import('@orkestrel/mcp').HTTPClientTransport} (CLIENT-only
|
|
239
237
|
* carriers of `@orkestrel/mcp`'s `MCPMessageTransportInterface`), a `MessagePort` is a
|
|
240
|
-
* plain duplex channel — the
|
|
241
|
-
* `MCPTransportInterface` and is handed to
|
|
238
|
+
* plain duplex channel — the same class implements `@orkestrel/mcp`'s
|
|
239
|
+
* `MCPTransportInterface` and is handed to either `bindServer` or
|
|
242
240
|
* `bindClient`/`createDuplexClientTransport`; which role it plays comes entirely
|
|
243
241
|
* from the binder it is given to, not from anything this class decides.
|
|
244
242
|
* - **`start()` at construction — bind synchronously.** `MessagePort.start()` is only
|
|
245
|
-
*
|
|
243
|
+
* required when listening with `addEventListener` (as opposed to the `onmessage`
|
|
246
244
|
* setter, which implies it) — this transport uses `addEventListener`, and
|
|
247
245
|
* `MCPTransportInterface` has no separate open/connect step for the caller to hook
|
|
248
246
|
* a start into, so the constructor calls `port.start()` immediately: the port
|
|
249
|
-
* begins dispatching
|
|
247
|
+
* begins dispatching queued messages the moment the transport exists. This is safe
|
|
250
248
|
* inside `createScopeServer`'s flow (the transport is synchronously handed to `bindServer`
|
|
251
249
|
* before control returns to the event loop), but is a **footgun for direct use**:
|
|
252
250
|
* if you construct `new MessagePortTransport({ port })` and then `await` anything
|
|
253
|
-
* before calling `listen`, messages that arrived in the gap are
|
|
251
|
+
* before calling `listen`, messages that arrived in the gap are dropped. **Bind
|
|
254
252
|
* synchronously after construction** — do not interleave an `await` between
|
|
255
253
|
* `new MessagePortTransport(…)` and `bindServer` / `listen`.
|
|
256
254
|
* - **String payloads only.** `send` posts the message string as-is (`postMessage`
|
|
257
255
|
* structured-clones it — a string clones to an identical string, so the wire stays
|
|
258
256
|
* plain JSON-RPC text like every other transport in this package). Inbound: a
|
|
259
257
|
* non-string `event.data` (a host or a misbehaving peer posting a structured
|
|
260
|
-
* object) is
|
|
258
|
+
* object) is ignored — dropped silently, never forwarded, never thrown —
|
|
261
259
|
* because `MCPTransportInterface` carries no `error` channel for this port to
|
|
262
260
|
* surface a non-string frame on (unlike `MCPMessageTransportInterface`'s `emitter`);
|
|
263
261
|
* silently ignoring is the total, contract-shaped choice.
|
|
264
|
-
* - **`messageerror` is
|
|
262
|
+
* - **`messageerror` is ignored, not routed to `closed`.** A `messageerror` event
|
|
265
263
|
* (the structured-clone deserialization of an inbound message threw) reports one
|
|
266
|
-
*
|
|
264
|
+
* bad frame, not a dead channel — the port itself keeps working and later, well-
|
|
267
265
|
* formed messages still arrive. This transport registers no listener for it: an
|
|
268
266
|
* unhandled `messageerror` on a `MessagePort` neither throws, closes the port, nor
|
|
269
267
|
* reaches this transport, so one bad frame costs exactly that frame and nothing
|
|
@@ -271,14 +269,14 @@ export declare const DEFAULT_MCP_SERVER_VERSION = "1.0.0";
|
|
|
271
269
|
* `bindServer`/`bindClient` wiring (and, transitively, every session it carries)
|
|
272
270
|
* over a single malformed frame.
|
|
273
271
|
* - **`close()`** is idempotent: it closes the underlying `port` (`MessagePort.close()`
|
|
274
|
-
* disconnects it — further `postMessage` calls on
|
|
272
|
+
* disconnects it — further `postMessage` calls on either end are silently
|
|
275
273
|
* undelivered, per the platform contract) and fires the registered `closed`
|
|
276
274
|
* handler exactly once, whether the caller closes it once or twice. There is no
|
|
277
275
|
* native "peer closed" signal for a `MessagePort` (unlike a WebSocket's `close`
|
|
278
|
-
* event) — `closed` fires
|
|
276
|
+
* event) — `closed` fires only from this transport's own `close()`.
|
|
279
277
|
* - **Single-handler-replace (the port contract, `@orkestrel/mcp`'s `MCPTransportInterface`
|
|
280
278
|
* doc).** `listen`/`closed` each hold the one active handler; a
|
|
281
|
-
* second call
|
|
279
|
+
* second call replaces the first rather than adding a second subscriber.
|
|
282
280
|
*
|
|
283
281
|
* @example
|
|
284
282
|
* ```ts
|
|
@@ -291,7 +289,7 @@ export declare const DEFAULT_MCP_SERVER_VERSION = "1.0.0";
|
|
|
291
289
|
* bindClient(client, clientTransport) // port2 side is the client's carrier
|
|
292
290
|
* ```
|
|
293
291
|
*/
|
|
294
|
-
export declare class MessagePortTransport implements
|
|
292
|
+
export declare class MessagePortTransport implements MCPTransportInterface {
|
|
295
293
|
#private;
|
|
296
294
|
constructor(options: MessagePortTransportOptions);
|
|
297
295
|
send(message: string): void;
|
|
@@ -306,7 +304,7 @@ export declare class MessagePortTransport implements MCPTransportInterface_2 {
|
|
|
306
304
|
*
|
|
307
305
|
* @remarks
|
|
308
306
|
* `port` — the channel half to drive (for example, one side of a `new MessageChannel()`, or
|
|
309
|
-
* the port a `message` event's `ports[0]` carried).
|
|
307
|
+
* the port a `message` event's `ports[0]` carried). Required. The same transport
|
|
310
308
|
* works as either a server or a client carrier — the role comes from whether it is
|
|
311
309
|
* handed to `bindServer` or `bindClient`/`createDuplexClientTransport` (`@orkestrel/mcp`).
|
|
312
310
|
*/
|
|
@@ -324,7 +322,7 @@ export declare interface MessagePortTransportOptions {
|
|
|
324
322
|
* Only the members the scope server actually touches: `postMessage` (the
|
|
325
323
|
* dedicated-worker implicit reply channel), and `addEventListener` /
|
|
326
324
|
* `removeEventListener` for `'message'` (every inbound event, portless or
|
|
327
|
-
* port-bearing, arrives through the
|
|
325
|
+
* port-bearing, arrives through the same listener — see {@link ScopeServerOptions}'s
|
|
328
326
|
* doc and the factory). A real `self` / `globalThis` inside a worker satisfies this
|
|
329
327
|
* structurally (it exposes far more, which this narrower shape ignores).
|
|
330
328
|
*/
|
|
@@ -354,20 +352,20 @@ export declare interface ScopeServerInterface {
|
|
|
354
352
|
* Options for {@link import('./factories.js').createScopeServer} — the live
|
|
355
353
|
* {@link ToolManagerInterface} to expose plus the optional server identity, mirroring
|
|
356
354
|
* `createMCPServer`'s `MCPServerOptions` (`@orkestrel/mcp`) but with `name`/`version`
|
|
357
|
-
*
|
|
355
|
+
* optional (defaulting to {@link import('./constants.js').DEFAULT_MCP_SERVER_NAME} /
|
|
358
356
|
* {@link import('./constants.js').DEFAULT_MCP_SERVER_VERSION}).
|
|
359
357
|
*
|
|
360
358
|
* @remarks
|
|
361
359
|
* - `accept` — optional identity gate consulted **before** a port-bearing `message`
|
|
362
360
|
* event is accepted; return `false` to drop the event (no binding, no reply).
|
|
363
|
-
* **`accept` gates
|
|
361
|
+
* **`accept` gates only port-bearing events** — portless messages bypass it and
|
|
364
362
|
* deliver directly to the implicit scope channel (the tool executes, blind; in a
|
|
365
363
|
* Service Worker the reply is silently dropped — see `createScopeServer`'s portless note).
|
|
366
364
|
* Prefer a handshake token in `event.data` as the primary pattern
|
|
367
365
|
* (for example, `(event) => event.data === token`) — for same-origin worker/MessagePort
|
|
368
366
|
* messages `event.origin` is frequently the empty string, making origin
|
|
369
367
|
* allow-listing unreliable; origin checks are meaningful for cross-origin
|
|
370
|
-
* `postMessage` only. When omitted,
|
|
368
|
+
* `postMessage` only. When omitted, all port-bearing events are accepted — every
|
|
371
369
|
* same-origin context that can reach the scope gets full tool-call access.
|
|
372
370
|
* See `createScopeServer`'s trust-boundary and portless-events notes.
|
|
373
371
|
*/
|
|
@@ -379,7 +377,7 @@ export declare interface ScopeServerOptions {
|
|
|
379
377
|
}
|
|
380
378
|
|
|
381
379
|
/**
|
|
382
|
-
* Adapts a message-event-bearing
|
|
380
|
+
* Adapts a message-event-bearing scope (`self` in a dedicated Web Worker, or any object
|
|
383
381
|
* shaped the same way) as a duplex {@link MCPTransportInterface} — the
|
|
384
382
|
* internal carrier `createScopeServer` binds to route the implicit (portless) message
|
|
385
383
|
* channel, plus the `deliver` entry point the scope's own `message` listener pushes
|
|
@@ -392,42 +390,42 @@ export declare interface ScopeTransportInterface extends MCPTransportInterface {
|
|
|
392
390
|
}
|
|
393
391
|
|
|
394
392
|
/**
|
|
395
|
-
* Drives a
|
|
396
|
-
*
|
|
397
|
-
* {@link import('@orkestrel/mcp/server').WebSocketClientTransport}.
|
|
393
|
+
* Drives a remote MCP server over the native `WebSocket` global from the browser face, as a
|
|
394
|
+
* client {@link MCPMessageTransportInterface}. This class is the browser sibling of the Node
|
|
395
|
+
* face's {@link import('@orkestrel/mcp/server').WebSocketClientTransport}.
|
|
398
396
|
*
|
|
399
397
|
* @remarks
|
|
400
398
|
* - **Host-performed handshake.** `start()` opens `new WebSocket(url, protocols)` and
|
|
401
399
|
* waits for the native `'open'` event — the RFC 6455 handshake itself is entirely
|
|
402
400
|
* the host's concern, so this transport carries none of the Node client's
|
|
403
401
|
* `node:crypto` / `node:http(s)` machinery. A connection failure (the native
|
|
404
|
-
* `'error'` event while not yet `OPEN`)
|
|
402
|
+
* `'error'` event while not yet `OPEN`) rejects `start()`.
|
|
405
403
|
* - **Queued sends.** `send` writes each message as one text frame immediately once
|
|
406
404
|
* the socket is `OPEN`; a `send` issued before `'open'` fires (or before `start()`
|
|
407
|
-
* is even called) is
|
|
408
|
-
* so a caller need not await `start()` before calling `send`. A queue rides
|
|
409
|
-
* connection: a close
|
|
410
|
-
* - **A closed channel
|
|
405
|
+
* is even called) is queued and flushed, in order, the moment the socket opens —
|
|
406
|
+
* so a caller need not await `start()` before calling `send`. A queue rides one
|
|
407
|
+
* connection: a close discards whatever is still in it.
|
|
408
|
+
* - **A closed channel rejects.** The native socket confirms nothing about a write, so this
|
|
411
409
|
* transport answers from its own state: a `send` after `close()`, or on a socket already
|
|
412
|
-
* reporting `CLOSING` / `CLOSED`,
|
|
410
|
+
* reporting `CLOSING` / `CLOSED`, rejects with `WebSocket transport is not connected` rather
|
|
413
411
|
* than resolving on a frame nobody wrote. Only the closed state rejects — a pre-open `send`
|
|
414
412
|
* still queues.
|
|
415
413
|
* - **Inbound (`message`).** Each decoded text frame runs through the shared
|
|
416
414
|
* `deliverMessage` fold (parse, then narrow) — a well-formed {@link JSONRPCMessage}
|
|
417
415
|
* re-emits on this transport's `message` event; a non-text (binary) frame or a
|
|
418
|
-
* non-JSON / non-message text frame surfaces on `error` and is
|
|
416
|
+
* non-JSON / non-message text frame surfaces on `error` and is dropped (never
|
|
419
417
|
* throws on adversarial wire input).
|
|
420
418
|
* - **`close()`** unsubscribes from the underlying socket, closes it, and fires `close`
|
|
421
419
|
* (idempotent); the socket's native `close` event (a server-initiated close) fires the
|
|
422
|
-
*
|
|
420
|
+
* same `close` exactly once total — `close()` first flips the guard, so the native event
|
|
423
421
|
* never double-emits, and the released socket reports its own close to nobody. Closing before
|
|
424
422
|
* the socket opens resolves the pending `start()` rather than leaving it pending, matching the
|
|
425
|
-
* Node face. A `send` issued after `close()`
|
|
426
|
-
* pre-open queue is
|
|
423
|
+
* Node face. A `send` issued after `close()` rejects (it is never queued), and the
|
|
424
|
+
* pre-open queue is discarded — by `close()` and by the native `close` event alike — so a
|
|
427
425
|
* closed transport delivers nothing until a `start()` opens a new connection, and nothing
|
|
428
426
|
* the caller handed the abandoned connection rides that one.
|
|
429
427
|
* - **Observable.** Owns the `emitter` ({@link MCPMessageTransportEventMap}); every
|
|
430
|
-
* emit the emitter isolates a listener throw; `error` is a
|
|
428
|
+
* emit the emitter isolates a listener throw; `error` is a domain event (a
|
|
431
429
|
* transport-level fault).
|
|
432
430
|
*
|
|
433
431
|
* @example
|
|
@@ -455,7 +453,7 @@ export declare class WebSocketClientTransport implements MCPMessageTransportInte
|
|
|
455
453
|
* @remarks
|
|
456
454
|
* - `url` — the absolute `ws://` / `wss://` (or `http://` / `https://`, accepted by
|
|
457
455
|
* the native `WebSocket` constructor the same way) URL of the remote server's
|
|
458
|
-
* WebSocket endpoint.
|
|
456
|
+
* WebSocket endpoint. Required.
|
|
459
457
|
* - `protocols` — the WebSocket subprotocol(s) to request. **Defaults to
|
|
460
458
|
* {@link import('@orkestrel/mcp').MCP_WEBSOCKET_SUBPROTOCOL} (`'mcp'`)**, which
|
|
461
459
|
* `createWebSocketServer` selects when the offer contains it. Per
|
|
@@ -471,7 +469,7 @@ export declare class WebSocketClientTransport implements MCPMessageTransportInte
|
|
|
471
469
|
* upgrade request header at all — there is no seam for an `Authorization` bearer to reach.
|
|
472
470
|
* The Node face owns its own `node:http(s)` upgrade request and therefore can, which is why
|
|
473
471
|
* only that side offers `headers`. Reach a guarded server from a page with a credential the
|
|
474
|
-
* platform
|
|
472
|
+
* platform does carry: a cookie the browser attaches to the upgrade, a subprotocol token, or
|
|
475
473
|
* a signed value in the URL. Adding a `headers` key here would be an option that silently
|
|
476
474
|
* did nothing.
|
|
477
475
|
*/
|