@particle-academy/agent-integrations 0.2.4
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 +131 -0
- package/dist/bridges/flow.d.cts +72 -0
- package/dist/bridges/flow.d.ts +72 -0
- package/dist/bridges/whiteboard.d.cts +40 -0
- package/dist/bridges/whiteboard.d.ts +40 -0
- package/dist/bridges-flow.cjs +330 -0
- package/dist/bridges-flow.cjs.map +1 -0
- package/dist/bridges-flow.js +4 -0
- package/dist/bridges-flow.js.map +1 -0
- package/dist/bridges-whiteboard.cjs +409 -0
- package/dist/bridges-whiteboard.cjs.map +1 -0
- package/dist/bridges-whiteboard.js +4 -0
- package/dist/bridges-whiteboard.js.map +1 -0
- package/dist/chunk-2VOQJKSU.js +320 -0
- package/dist/chunk-2VOQJKSU.js.map +1 -0
- package/dist/chunk-5ZUHNNLR.js +398 -0
- package/dist/chunk-5ZUHNNLR.js.map +1 -0
- package/dist/chunk-6LTKCNLF.js +68 -0
- package/dist/chunk-6LTKCNLF.js.map +1 -0
- package/dist/chunk-FLEOQUKF.js +157 -0
- package/dist/chunk-FLEOQUKF.js.map +1 -0
- package/dist/chunk-QGCF7YKW.js +130 -0
- package/dist/chunk-QGCF7YKW.js.map +1 -0
- package/dist/index.cjs +1632 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +155 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.js +567 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.cts +73 -0
- package/dist/mcp/index.d.ts +73 -0
- package/dist/mcp.cjs +210 -0
- package/dist/mcp.cjs.map +1 -0
- package/dist/mcp.js +4 -0
- package/dist/mcp.js.map +1 -0
- package/dist/server-Bv985us3.d.cts +173 -0
- package/dist/server-Bv985us3.d.ts +173 -0
- package/dist/sharing/index.d.cts +89 -0
- package/dist/sharing/index.d.ts +89 -0
- package/dist/sharing.cjs +166 -0
- package/dist/sharing.cjs.map +1 -0
- package/dist/sharing.js +3 -0
- package/dist/sharing.js.map +1 -0
- package/dist/styles.css +331 -0
- package/dist/styles.css.map +1 -0
- package/dist/types-CRPA_D0z.d.ts +18 -0
- package/dist/types-DR5AS6Rd.d.cts +18 -0
- package/docs/relay-protocol.md +57 -0
- package/package.json +61 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal MCP (Model Context Protocol) types — covers the subset this
|
|
3
|
+
* package implements: initialize, tools/list, tools/call, plus JSON-RPC.
|
|
4
|
+
*
|
|
5
|
+
* Aligned with the public MCP spec but trimmed to v0.1 needs. See
|
|
6
|
+
* https://spec.modelcontextprotocol.io/ for the full surface.
|
|
7
|
+
*/
|
|
8
|
+
type JsonValue = string | number | boolean | null | {
|
|
9
|
+
[key: string]: JsonValue;
|
|
10
|
+
} | JsonValue[];
|
|
11
|
+
type JsonObject = {
|
|
12
|
+
[key: string]: JsonValue;
|
|
13
|
+
};
|
|
14
|
+
type JsonRpcId = string | number | null;
|
|
15
|
+
type JsonRpcRequest = {
|
|
16
|
+
jsonrpc: "2.0";
|
|
17
|
+
id: JsonRpcId;
|
|
18
|
+
method: string;
|
|
19
|
+
params?: JsonObject;
|
|
20
|
+
};
|
|
21
|
+
type JsonRpcNotification = {
|
|
22
|
+
jsonrpc: "2.0";
|
|
23
|
+
method: string;
|
|
24
|
+
params?: JsonObject;
|
|
25
|
+
};
|
|
26
|
+
type JsonRpcSuccess = {
|
|
27
|
+
jsonrpc: "2.0";
|
|
28
|
+
id: JsonRpcId;
|
|
29
|
+
result: JsonValue;
|
|
30
|
+
};
|
|
31
|
+
type JsonRpcError = {
|
|
32
|
+
jsonrpc: "2.0";
|
|
33
|
+
id: JsonRpcId;
|
|
34
|
+
error: {
|
|
35
|
+
code: number;
|
|
36
|
+
message: string;
|
|
37
|
+
data?: JsonValue;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcSuccess | JsonRpcError;
|
|
41
|
+
declare const JSONRPC_PARSE_ERROR = -32700;
|
|
42
|
+
declare const JSONRPC_INVALID_REQUEST = -32600;
|
|
43
|
+
declare const JSONRPC_METHOD_NOT_FOUND = -32601;
|
|
44
|
+
declare const JSONRPC_INVALID_PARAMS = -32602;
|
|
45
|
+
declare const JSONRPC_INTERNAL_ERROR = -32603;
|
|
46
|
+
type ServerCapabilities = {
|
|
47
|
+
tools?: {
|
|
48
|
+
listChanged?: boolean;
|
|
49
|
+
};
|
|
50
|
+
resources?: {
|
|
51
|
+
listChanged?: boolean;
|
|
52
|
+
subscribe?: boolean;
|
|
53
|
+
};
|
|
54
|
+
prompts?: {
|
|
55
|
+
listChanged?: boolean;
|
|
56
|
+
};
|
|
57
|
+
logging?: Record<string, never>;
|
|
58
|
+
};
|
|
59
|
+
type ServerInfo = {
|
|
60
|
+
name: string;
|
|
61
|
+
version: string;
|
|
62
|
+
title?: string;
|
|
63
|
+
};
|
|
64
|
+
type InitializeResult = {
|
|
65
|
+
protocolVersion: string;
|
|
66
|
+
capabilities: ServerCapabilities;
|
|
67
|
+
serverInfo: ServerInfo;
|
|
68
|
+
instructions?: string;
|
|
69
|
+
};
|
|
70
|
+
type ToolInputSchema = {
|
|
71
|
+
type: "object";
|
|
72
|
+
properties?: Record<string, JsonValue>;
|
|
73
|
+
required?: string[];
|
|
74
|
+
additionalProperties?: boolean;
|
|
75
|
+
};
|
|
76
|
+
type ToolDefinition = {
|
|
77
|
+
name: string;
|
|
78
|
+
title?: string;
|
|
79
|
+
description?: string;
|
|
80
|
+
inputSchema: ToolInputSchema;
|
|
81
|
+
};
|
|
82
|
+
type ContentBlock = {
|
|
83
|
+
type: "text";
|
|
84
|
+
text: string;
|
|
85
|
+
} | {
|
|
86
|
+
type: "image";
|
|
87
|
+
data: string;
|
|
88
|
+
mimeType: string;
|
|
89
|
+
} | {
|
|
90
|
+
type: "resource";
|
|
91
|
+
resource: {
|
|
92
|
+
uri: string;
|
|
93
|
+
text?: string;
|
|
94
|
+
mimeType?: string;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
type CallToolResult = {
|
|
98
|
+
content: ContentBlock[];
|
|
99
|
+
isError?: boolean;
|
|
100
|
+
/** Structured tool output — non-spec but useful for typed bridges. */
|
|
101
|
+
structuredContent?: JsonValue;
|
|
102
|
+
};
|
|
103
|
+
/** Handler signature for a tool registered on the MicroMcpServer. */
|
|
104
|
+
type ToolHandler = (args: JsonObject) => Promise<CallToolResult> | CallToolResult;
|
|
105
|
+
/** Internal record kept by the server. */
|
|
106
|
+
type RegisteredTool = {
|
|
107
|
+
definition: ToolDefinition;
|
|
108
|
+
handler: ToolHandler;
|
|
109
|
+
};
|
|
110
|
+
declare const MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
111
|
+
|
|
112
|
+
type McpServerOptions = {
|
|
113
|
+
info: ServerInfo;
|
|
114
|
+
/** Defaults to { tools: { listChanged: true } } */
|
|
115
|
+
capabilities?: ServerCapabilities;
|
|
116
|
+
/** Free-text instructions surfaced to clients during initialize. */
|
|
117
|
+
instructions?: string;
|
|
118
|
+
};
|
|
119
|
+
type Transport = {
|
|
120
|
+
/** Called by the server when it has a message to deliver to the client. */
|
|
121
|
+
send: (message: JsonRpcMessage) => void;
|
|
122
|
+
/** Called by the server when it's torn down so the transport can clean up. */
|
|
123
|
+
close?: () => void;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* MicroMcpServer — protocol-level MCP server, transport-agnostic.
|
|
127
|
+
*
|
|
128
|
+
* Use it like:
|
|
129
|
+
*
|
|
130
|
+
* const server = new MicroMcpServer({ info: { name: "session", version: "0.1" } });
|
|
131
|
+
* server.registerTool({ name: "...", inputSchema: { type: "object" } }, async (args) => ({...}));
|
|
132
|
+
* const transport = new InProcessTransport();
|
|
133
|
+
* server.attach(transport);
|
|
134
|
+
* transport.deliver({ ... }); // client → server frames
|
|
135
|
+
*
|
|
136
|
+
* The same server can serve multiple transports (e.g. an in-process agent
|
|
137
|
+
* AND a relayed external client) by attaching each one.
|
|
138
|
+
*/
|
|
139
|
+
declare class MicroMcpServer {
|
|
140
|
+
private tools;
|
|
141
|
+
private transports;
|
|
142
|
+
private notifyListChangedScheduled;
|
|
143
|
+
readonly info: ServerInfo;
|
|
144
|
+
readonly capabilities: ServerCapabilities;
|
|
145
|
+
readonly instructions?: string;
|
|
146
|
+
constructor(options: McpServerOptions);
|
|
147
|
+
attach(transport: Transport): () => void;
|
|
148
|
+
detach(transport: Transport): void;
|
|
149
|
+
registerTool(definition: ToolDefinition, handler: ToolHandler): () => void;
|
|
150
|
+
unregisterTool(name: string): void;
|
|
151
|
+
listTools(): ToolDefinition[];
|
|
152
|
+
/**
|
|
153
|
+
* Receive a JSON-RPC frame from a client (called by the transport).
|
|
154
|
+
* The transport is responsible for sending the response back.
|
|
155
|
+
*/
|
|
156
|
+
receive(transport: Transport, message: JsonRpcMessage): Promise<void>;
|
|
157
|
+
private handle;
|
|
158
|
+
private scheduleListChangedNotification;
|
|
159
|
+
private broadcast;
|
|
160
|
+
private toRpcError;
|
|
161
|
+
}
|
|
162
|
+
declare function rpcError(code: number, message: string, data?: any): {
|
|
163
|
+
data?: any;
|
|
164
|
+
code: number;
|
|
165
|
+
message: string;
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* Helper to build a CallToolResult from a string or structured value.
|
|
169
|
+
*/
|
|
170
|
+
declare function textResult(text: string, structured?: any): CallToolResult;
|
|
171
|
+
declare function errorResult(text: string): CallToolResult;
|
|
172
|
+
|
|
173
|
+
export { type CallToolResult as C, type InitializeResult as I, type JsonObject as J, MicroMcpServer as M, type RegisteredTool as R, type ServerCapabilities as S, type ToolDefinition as T, type ContentBlock as a, type JsonRpcMessage as b, type JsonRpcNotification as c, type JsonRpcRequest as d, type JsonValue as e, MCP_PROTOCOL_VERSION as f, type McpServerOptions as g, type ServerInfo as h, type ToolHandler as i, type Transport as j, errorResult as k, JSONRPC_INTERNAL_ERROR as l, JSONRPC_INVALID_PARAMS as m, JSONRPC_INVALID_REQUEST as n, JSONRPC_METHOD_NOT_FOUND as o, JSONRPC_PARSE_ERROR as p, type JsonRpcError as q, rpcError as r, type JsonRpcId as s, textResult as t, type JsonRpcSuccess as u, type ToolInputSchema as v };
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal MCP (Model Context Protocol) types — covers the subset this
|
|
3
|
+
* package implements: initialize, tools/list, tools/call, plus JSON-RPC.
|
|
4
|
+
*
|
|
5
|
+
* Aligned with the public MCP spec but trimmed to v0.1 needs. See
|
|
6
|
+
* https://spec.modelcontextprotocol.io/ for the full surface.
|
|
7
|
+
*/
|
|
8
|
+
type JsonValue = string | number | boolean | null | {
|
|
9
|
+
[key: string]: JsonValue;
|
|
10
|
+
} | JsonValue[];
|
|
11
|
+
type JsonObject = {
|
|
12
|
+
[key: string]: JsonValue;
|
|
13
|
+
};
|
|
14
|
+
type JsonRpcId = string | number | null;
|
|
15
|
+
type JsonRpcRequest = {
|
|
16
|
+
jsonrpc: "2.0";
|
|
17
|
+
id: JsonRpcId;
|
|
18
|
+
method: string;
|
|
19
|
+
params?: JsonObject;
|
|
20
|
+
};
|
|
21
|
+
type JsonRpcNotification = {
|
|
22
|
+
jsonrpc: "2.0";
|
|
23
|
+
method: string;
|
|
24
|
+
params?: JsonObject;
|
|
25
|
+
};
|
|
26
|
+
type JsonRpcSuccess = {
|
|
27
|
+
jsonrpc: "2.0";
|
|
28
|
+
id: JsonRpcId;
|
|
29
|
+
result: JsonValue;
|
|
30
|
+
};
|
|
31
|
+
type JsonRpcError = {
|
|
32
|
+
jsonrpc: "2.0";
|
|
33
|
+
id: JsonRpcId;
|
|
34
|
+
error: {
|
|
35
|
+
code: number;
|
|
36
|
+
message: string;
|
|
37
|
+
data?: JsonValue;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcSuccess | JsonRpcError;
|
|
41
|
+
declare const JSONRPC_PARSE_ERROR = -32700;
|
|
42
|
+
declare const JSONRPC_INVALID_REQUEST = -32600;
|
|
43
|
+
declare const JSONRPC_METHOD_NOT_FOUND = -32601;
|
|
44
|
+
declare const JSONRPC_INVALID_PARAMS = -32602;
|
|
45
|
+
declare const JSONRPC_INTERNAL_ERROR = -32603;
|
|
46
|
+
type ServerCapabilities = {
|
|
47
|
+
tools?: {
|
|
48
|
+
listChanged?: boolean;
|
|
49
|
+
};
|
|
50
|
+
resources?: {
|
|
51
|
+
listChanged?: boolean;
|
|
52
|
+
subscribe?: boolean;
|
|
53
|
+
};
|
|
54
|
+
prompts?: {
|
|
55
|
+
listChanged?: boolean;
|
|
56
|
+
};
|
|
57
|
+
logging?: Record<string, never>;
|
|
58
|
+
};
|
|
59
|
+
type ServerInfo = {
|
|
60
|
+
name: string;
|
|
61
|
+
version: string;
|
|
62
|
+
title?: string;
|
|
63
|
+
};
|
|
64
|
+
type InitializeResult = {
|
|
65
|
+
protocolVersion: string;
|
|
66
|
+
capabilities: ServerCapabilities;
|
|
67
|
+
serverInfo: ServerInfo;
|
|
68
|
+
instructions?: string;
|
|
69
|
+
};
|
|
70
|
+
type ToolInputSchema = {
|
|
71
|
+
type: "object";
|
|
72
|
+
properties?: Record<string, JsonValue>;
|
|
73
|
+
required?: string[];
|
|
74
|
+
additionalProperties?: boolean;
|
|
75
|
+
};
|
|
76
|
+
type ToolDefinition = {
|
|
77
|
+
name: string;
|
|
78
|
+
title?: string;
|
|
79
|
+
description?: string;
|
|
80
|
+
inputSchema: ToolInputSchema;
|
|
81
|
+
};
|
|
82
|
+
type ContentBlock = {
|
|
83
|
+
type: "text";
|
|
84
|
+
text: string;
|
|
85
|
+
} | {
|
|
86
|
+
type: "image";
|
|
87
|
+
data: string;
|
|
88
|
+
mimeType: string;
|
|
89
|
+
} | {
|
|
90
|
+
type: "resource";
|
|
91
|
+
resource: {
|
|
92
|
+
uri: string;
|
|
93
|
+
text?: string;
|
|
94
|
+
mimeType?: string;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
type CallToolResult = {
|
|
98
|
+
content: ContentBlock[];
|
|
99
|
+
isError?: boolean;
|
|
100
|
+
/** Structured tool output — non-spec but useful for typed bridges. */
|
|
101
|
+
structuredContent?: JsonValue;
|
|
102
|
+
};
|
|
103
|
+
/** Handler signature for a tool registered on the MicroMcpServer. */
|
|
104
|
+
type ToolHandler = (args: JsonObject) => Promise<CallToolResult> | CallToolResult;
|
|
105
|
+
/** Internal record kept by the server. */
|
|
106
|
+
type RegisteredTool = {
|
|
107
|
+
definition: ToolDefinition;
|
|
108
|
+
handler: ToolHandler;
|
|
109
|
+
};
|
|
110
|
+
declare const MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
111
|
+
|
|
112
|
+
type McpServerOptions = {
|
|
113
|
+
info: ServerInfo;
|
|
114
|
+
/** Defaults to { tools: { listChanged: true } } */
|
|
115
|
+
capabilities?: ServerCapabilities;
|
|
116
|
+
/** Free-text instructions surfaced to clients during initialize. */
|
|
117
|
+
instructions?: string;
|
|
118
|
+
};
|
|
119
|
+
type Transport = {
|
|
120
|
+
/** Called by the server when it has a message to deliver to the client. */
|
|
121
|
+
send: (message: JsonRpcMessage) => void;
|
|
122
|
+
/** Called by the server when it's torn down so the transport can clean up. */
|
|
123
|
+
close?: () => void;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* MicroMcpServer — protocol-level MCP server, transport-agnostic.
|
|
127
|
+
*
|
|
128
|
+
* Use it like:
|
|
129
|
+
*
|
|
130
|
+
* const server = new MicroMcpServer({ info: { name: "session", version: "0.1" } });
|
|
131
|
+
* server.registerTool({ name: "...", inputSchema: { type: "object" } }, async (args) => ({...}));
|
|
132
|
+
* const transport = new InProcessTransport();
|
|
133
|
+
* server.attach(transport);
|
|
134
|
+
* transport.deliver({ ... }); // client → server frames
|
|
135
|
+
*
|
|
136
|
+
* The same server can serve multiple transports (e.g. an in-process agent
|
|
137
|
+
* AND a relayed external client) by attaching each one.
|
|
138
|
+
*/
|
|
139
|
+
declare class MicroMcpServer {
|
|
140
|
+
private tools;
|
|
141
|
+
private transports;
|
|
142
|
+
private notifyListChangedScheduled;
|
|
143
|
+
readonly info: ServerInfo;
|
|
144
|
+
readonly capabilities: ServerCapabilities;
|
|
145
|
+
readonly instructions?: string;
|
|
146
|
+
constructor(options: McpServerOptions);
|
|
147
|
+
attach(transport: Transport): () => void;
|
|
148
|
+
detach(transport: Transport): void;
|
|
149
|
+
registerTool(definition: ToolDefinition, handler: ToolHandler): () => void;
|
|
150
|
+
unregisterTool(name: string): void;
|
|
151
|
+
listTools(): ToolDefinition[];
|
|
152
|
+
/**
|
|
153
|
+
* Receive a JSON-RPC frame from a client (called by the transport).
|
|
154
|
+
* The transport is responsible for sending the response back.
|
|
155
|
+
*/
|
|
156
|
+
receive(transport: Transport, message: JsonRpcMessage): Promise<void>;
|
|
157
|
+
private handle;
|
|
158
|
+
private scheduleListChangedNotification;
|
|
159
|
+
private broadcast;
|
|
160
|
+
private toRpcError;
|
|
161
|
+
}
|
|
162
|
+
declare function rpcError(code: number, message: string, data?: any): {
|
|
163
|
+
data?: any;
|
|
164
|
+
code: number;
|
|
165
|
+
message: string;
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* Helper to build a CallToolResult from a string or structured value.
|
|
169
|
+
*/
|
|
170
|
+
declare function textResult(text: string, structured?: any): CallToolResult;
|
|
171
|
+
declare function errorResult(text: string): CallToolResult;
|
|
172
|
+
|
|
173
|
+
export { type CallToolResult as C, type InitializeResult as I, type JsonObject as J, MicroMcpServer as M, type RegisteredTool as R, type ServerCapabilities as S, type ToolDefinition as T, type ContentBlock as a, type JsonRpcMessage as b, type JsonRpcNotification as c, type JsonRpcRequest as d, type JsonValue as e, MCP_PROTOCOL_VERSION as f, type McpServerOptions as g, type ServerInfo as h, type ToolHandler as i, type Transport as j, errorResult as k, JSONRPC_INTERNAL_ERROR as l, JSONRPC_INVALID_PARAMS as m, JSONRPC_INVALID_REQUEST as n, JSONRPC_METHOD_NOT_FOUND as o, JSONRPC_PARSE_ERROR as p, type JsonRpcError as q, rpcError as r, type JsonRpcId as s, textResult as t, type JsonRpcSuccess as u, type ToolInputSchema as v };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { j as Transport, M as MicroMcpServer, b as JsonRpcMessage } from '../server-Bv985us3.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Session-token utilities. The token is a high-entropy secret; possession
|
|
5
|
+
* grants read/write on the session. We don't HMAC frames — frames carry
|
|
6
|
+
* the token directly (which is fine for in-process / same-origin / TLS
|
|
7
|
+
* transports). For lower-trust transports, host apps can layer signing
|
|
8
|
+
* on top of the BroadcastChannelTransport.
|
|
9
|
+
*/
|
|
10
|
+
type SessionDescriptor = {
|
|
11
|
+
/** Stable session identifier. Channel name = `fai:share:${id}`. */
|
|
12
|
+
id: string;
|
|
13
|
+
/** Secret token. Treat as a password — anyone with it can read/write. */
|
|
14
|
+
token: string;
|
|
15
|
+
/** Pretty hash for display (first 8 chars of token). */
|
|
16
|
+
display: string;
|
|
17
|
+
};
|
|
18
|
+
declare function createSessionDescriptor(): SessionDescriptor;
|
|
19
|
+
declare function describeSession(id: string, token: string): SessionDescriptor;
|
|
20
|
+
/** Build the shareable URL for the current page (preserves path, adds session+token). */
|
|
21
|
+
declare function buildShareUrl(descriptor: SessionDescriptor, baseUrl?: string): string;
|
|
22
|
+
/** Build the JSON config form (suitable for Claude Desktop / Cline / etc.). */
|
|
23
|
+
declare function buildShareConfig(descriptor: SessionDescriptor, transport?: string): {
|
|
24
|
+
name: string;
|
|
25
|
+
transport: string;
|
|
26
|
+
session: string;
|
|
27
|
+
token: string;
|
|
28
|
+
channel: string;
|
|
29
|
+
protocol_version: string;
|
|
30
|
+
};
|
|
31
|
+
/** Read session descriptor from current URL, or null if not a shared link. */
|
|
32
|
+
declare function readSessionFromUrl(): SessionDescriptor | null;
|
|
33
|
+
/** Constant-time string compare so a mismatched token leaks no timing info. */
|
|
34
|
+
declare function constantTimeEqual(a: string, b: string): boolean;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* SseRelayTransport — bridges the in-page MicroMcpServer to a host-app
|
|
38
|
+
* relay broker over Server-Sent Events (inbound) + POST (outbound).
|
|
39
|
+
*
|
|
40
|
+
* Wire model:
|
|
41
|
+
* - Browser opens an EventSource at `${baseUrl}/${sessionId}/events?token=…`.
|
|
42
|
+
* Each `event: mcp` carries one JSON-RPC frame from a remote client.
|
|
43
|
+
* - Browser POSTs JSON-RPC frames to `${baseUrl}/${sessionId}/outbox?token=…`
|
|
44
|
+
* when the local server has a response/notification to send.
|
|
45
|
+
*
|
|
46
|
+
* The host provides the relay endpoint (any HTTP server). See the demo
|
|
47
|
+
* `WhiteboardShareController` for the reference implementation.
|
|
48
|
+
*
|
|
49
|
+
* Token authentication is the host's job — this transport just carries the
|
|
50
|
+
* token in the query string. For lower-trust deployments, layer signing on
|
|
51
|
+
* top by wrapping `send` / `deliverFromRemote`.
|
|
52
|
+
*/
|
|
53
|
+
type SseRelayOptions = {
|
|
54
|
+
baseUrl: string;
|
|
55
|
+
sessionId: string;
|
|
56
|
+
token: string;
|
|
57
|
+
/** Override fetch (testing / non-browser). Defaults to global fetch. */
|
|
58
|
+
fetch?: typeof fetch;
|
|
59
|
+
};
|
|
60
|
+
declare class SseRelayTransport implements Transport {
|
|
61
|
+
private server?;
|
|
62
|
+
private es?;
|
|
63
|
+
private opts;
|
|
64
|
+
private sendQueue;
|
|
65
|
+
private connected;
|
|
66
|
+
private listeners;
|
|
67
|
+
private state;
|
|
68
|
+
private expectedToken;
|
|
69
|
+
constructor(options: SseRelayOptions);
|
|
70
|
+
bindServer(server: MicroMcpServer): void;
|
|
71
|
+
/** Open the SSE channel. Idempotent. */
|
|
72
|
+
start(): void;
|
|
73
|
+
send(message: JsonRpcMessage): void;
|
|
74
|
+
close(): void;
|
|
75
|
+
onStateChange(listener: (state: RelayState) => void): () => void;
|
|
76
|
+
/**
|
|
77
|
+
* For relays that wrap each frame with auth metadata: hosts can call this
|
|
78
|
+
* directly when a frame arrives via a non-SSE path. The transport will
|
|
79
|
+
* dispatch it to the bound server.
|
|
80
|
+
*/
|
|
81
|
+
deliverFromRemote(payload: JsonRpcMessage | string, token?: string): Promise<void>;
|
|
82
|
+
private postOut;
|
|
83
|
+
private handleInbound;
|
|
84
|
+
private setState;
|
|
85
|
+
}
|
|
86
|
+
type RelayState = "idle" | "connecting" | "open" | "closed" | "error";
|
|
87
|
+
declare function attachSseRelay(server: MicroMcpServer, options: SseRelayOptions): SseRelayTransport;
|
|
88
|
+
|
|
89
|
+
export { type RelayState, type SessionDescriptor, type SseRelayOptions, SseRelayTransport, attachSseRelay, buildShareConfig, buildShareUrl, constantTimeEqual, createSessionDescriptor, describeSession, readSessionFromUrl };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { j as Transport, M as MicroMcpServer, b as JsonRpcMessage } from '../server-Bv985us3.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Session-token utilities. The token is a high-entropy secret; possession
|
|
5
|
+
* grants read/write on the session. We don't HMAC frames — frames carry
|
|
6
|
+
* the token directly (which is fine for in-process / same-origin / TLS
|
|
7
|
+
* transports). For lower-trust transports, host apps can layer signing
|
|
8
|
+
* on top of the BroadcastChannelTransport.
|
|
9
|
+
*/
|
|
10
|
+
type SessionDescriptor = {
|
|
11
|
+
/** Stable session identifier. Channel name = `fai:share:${id}`. */
|
|
12
|
+
id: string;
|
|
13
|
+
/** Secret token. Treat as a password — anyone with it can read/write. */
|
|
14
|
+
token: string;
|
|
15
|
+
/** Pretty hash for display (first 8 chars of token). */
|
|
16
|
+
display: string;
|
|
17
|
+
};
|
|
18
|
+
declare function createSessionDescriptor(): SessionDescriptor;
|
|
19
|
+
declare function describeSession(id: string, token: string): SessionDescriptor;
|
|
20
|
+
/** Build the shareable URL for the current page (preserves path, adds session+token). */
|
|
21
|
+
declare function buildShareUrl(descriptor: SessionDescriptor, baseUrl?: string): string;
|
|
22
|
+
/** Build the JSON config form (suitable for Claude Desktop / Cline / etc.). */
|
|
23
|
+
declare function buildShareConfig(descriptor: SessionDescriptor, transport?: string): {
|
|
24
|
+
name: string;
|
|
25
|
+
transport: string;
|
|
26
|
+
session: string;
|
|
27
|
+
token: string;
|
|
28
|
+
channel: string;
|
|
29
|
+
protocol_version: string;
|
|
30
|
+
};
|
|
31
|
+
/** Read session descriptor from current URL, or null if not a shared link. */
|
|
32
|
+
declare function readSessionFromUrl(): SessionDescriptor | null;
|
|
33
|
+
/** Constant-time string compare so a mismatched token leaks no timing info. */
|
|
34
|
+
declare function constantTimeEqual(a: string, b: string): boolean;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* SseRelayTransport — bridges the in-page MicroMcpServer to a host-app
|
|
38
|
+
* relay broker over Server-Sent Events (inbound) + POST (outbound).
|
|
39
|
+
*
|
|
40
|
+
* Wire model:
|
|
41
|
+
* - Browser opens an EventSource at `${baseUrl}/${sessionId}/events?token=…`.
|
|
42
|
+
* Each `event: mcp` carries one JSON-RPC frame from a remote client.
|
|
43
|
+
* - Browser POSTs JSON-RPC frames to `${baseUrl}/${sessionId}/outbox?token=…`
|
|
44
|
+
* when the local server has a response/notification to send.
|
|
45
|
+
*
|
|
46
|
+
* The host provides the relay endpoint (any HTTP server). See the demo
|
|
47
|
+
* `WhiteboardShareController` for the reference implementation.
|
|
48
|
+
*
|
|
49
|
+
* Token authentication is the host's job — this transport just carries the
|
|
50
|
+
* token in the query string. For lower-trust deployments, layer signing on
|
|
51
|
+
* top by wrapping `send` / `deliverFromRemote`.
|
|
52
|
+
*/
|
|
53
|
+
type SseRelayOptions = {
|
|
54
|
+
baseUrl: string;
|
|
55
|
+
sessionId: string;
|
|
56
|
+
token: string;
|
|
57
|
+
/** Override fetch (testing / non-browser). Defaults to global fetch. */
|
|
58
|
+
fetch?: typeof fetch;
|
|
59
|
+
};
|
|
60
|
+
declare class SseRelayTransport implements Transport {
|
|
61
|
+
private server?;
|
|
62
|
+
private es?;
|
|
63
|
+
private opts;
|
|
64
|
+
private sendQueue;
|
|
65
|
+
private connected;
|
|
66
|
+
private listeners;
|
|
67
|
+
private state;
|
|
68
|
+
private expectedToken;
|
|
69
|
+
constructor(options: SseRelayOptions);
|
|
70
|
+
bindServer(server: MicroMcpServer): void;
|
|
71
|
+
/** Open the SSE channel. Idempotent. */
|
|
72
|
+
start(): void;
|
|
73
|
+
send(message: JsonRpcMessage): void;
|
|
74
|
+
close(): void;
|
|
75
|
+
onStateChange(listener: (state: RelayState) => void): () => void;
|
|
76
|
+
/**
|
|
77
|
+
* For relays that wrap each frame with auth metadata: hosts can call this
|
|
78
|
+
* directly when a frame arrives via a non-SSE path. The transport will
|
|
79
|
+
* dispatch it to the bound server.
|
|
80
|
+
*/
|
|
81
|
+
deliverFromRemote(payload: JsonRpcMessage | string, token?: string): Promise<void>;
|
|
82
|
+
private postOut;
|
|
83
|
+
private handleInbound;
|
|
84
|
+
private setState;
|
|
85
|
+
}
|
|
86
|
+
type RelayState = "idle" | "connecting" | "open" | "closed" | "error";
|
|
87
|
+
declare function attachSseRelay(server: MicroMcpServer, options: SseRelayOptions): SseRelayTransport;
|
|
88
|
+
|
|
89
|
+
export { type RelayState, type SessionDescriptor, type SseRelayOptions, SseRelayTransport, attachSseRelay, buildShareConfig, buildShareUrl, constantTimeEqual, createSessionDescriptor, describeSession, readSessionFromUrl };
|
package/dist/sharing.cjs
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/sharing/token.ts
|
|
4
|
+
var TOKEN_BYTES = 24;
|
|
5
|
+
function createSessionDescriptor() {
|
|
6
|
+
const id = randomId(8);
|
|
7
|
+
const token = randomToken();
|
|
8
|
+
return { id, token, display: token.slice(0, 8) };
|
|
9
|
+
}
|
|
10
|
+
function describeSession(id, token) {
|
|
11
|
+
return { id, token, display: token.slice(0, 8) };
|
|
12
|
+
}
|
|
13
|
+
function buildShareUrl(descriptor, baseUrl = typeof window !== "undefined" ? window.location.href.split("?")[0] : "") {
|
|
14
|
+
const u = new URL(baseUrl);
|
|
15
|
+
u.searchParams.set("session", descriptor.id);
|
|
16
|
+
u.searchParams.set("token", descriptor.token);
|
|
17
|
+
return u.toString();
|
|
18
|
+
}
|
|
19
|
+
function buildShareConfig(descriptor, transport = "broadcast-channel") {
|
|
20
|
+
return {
|
|
21
|
+
name: `whiteboard-${descriptor.id}`,
|
|
22
|
+
transport,
|
|
23
|
+
session: descriptor.id,
|
|
24
|
+
token: descriptor.token,
|
|
25
|
+
channel: `fai:share:${descriptor.id}`,
|
|
26
|
+
protocol_version: "2025-06-18"
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function readSessionFromUrl() {
|
|
30
|
+
if (typeof window === "undefined") return null;
|
|
31
|
+
const params = new URL(window.location.href).searchParams;
|
|
32
|
+
const id = params.get("session");
|
|
33
|
+
const token = params.get("token");
|
|
34
|
+
if (!id || !token) return null;
|
|
35
|
+
return describeSession(id, token);
|
|
36
|
+
}
|
|
37
|
+
function randomToken() {
|
|
38
|
+
const bytes = new Uint8Array(TOKEN_BYTES);
|
|
39
|
+
crypto.getRandomValues(bytes);
|
|
40
|
+
return base64Url(bytes);
|
|
41
|
+
}
|
|
42
|
+
function randomId(len) {
|
|
43
|
+
const bytes = new Uint8Array(Math.ceil(len * 3 / 4));
|
|
44
|
+
crypto.getRandomValues(bytes);
|
|
45
|
+
return base64Url(bytes).slice(0, len);
|
|
46
|
+
}
|
|
47
|
+
function base64Url(bytes) {
|
|
48
|
+
let s = "";
|
|
49
|
+
for (const b of bytes) s += String.fromCharCode(b);
|
|
50
|
+
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
51
|
+
}
|
|
52
|
+
function constantTimeEqual(a, b) {
|
|
53
|
+
if (a.length !== b.length) return false;
|
|
54
|
+
let diff = 0;
|
|
55
|
+
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
56
|
+
return diff === 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/sharing/sse-relay.ts
|
|
60
|
+
var SseRelayTransport = class {
|
|
61
|
+
constructor(options) {
|
|
62
|
+
this.sendQueue = [];
|
|
63
|
+
this.connected = false;
|
|
64
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
65
|
+
this.state = "idle";
|
|
66
|
+
this.opts = options;
|
|
67
|
+
this.expectedToken = options.token;
|
|
68
|
+
}
|
|
69
|
+
bindServer(server) {
|
|
70
|
+
this.server = server;
|
|
71
|
+
}
|
|
72
|
+
/** Open the SSE channel. Idempotent. */
|
|
73
|
+
start() {
|
|
74
|
+
if (this.connected || typeof window === "undefined") return;
|
|
75
|
+
const url = `${this.opts.baseUrl}/${encodeURIComponent(this.opts.sessionId)}/events?token=${encodeURIComponent(this.opts.token)}`;
|
|
76
|
+
this.setState("connecting");
|
|
77
|
+
const es = new EventSource(url, { withCredentials: false });
|
|
78
|
+
this.es = es;
|
|
79
|
+
es.addEventListener("open", () => {
|
|
80
|
+
this.connected = true;
|
|
81
|
+
this.setState("open");
|
|
82
|
+
const queued = this.sendQueue.splice(0);
|
|
83
|
+
for (const msg of queued) this.postOut(msg);
|
|
84
|
+
});
|
|
85
|
+
es.addEventListener("mcp", (ev) => {
|
|
86
|
+
const raw = ev.data;
|
|
87
|
+
this.handleInbound(raw);
|
|
88
|
+
});
|
|
89
|
+
es.addEventListener("error", () => {
|
|
90
|
+
this.setState("error");
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
send(message) {
|
|
94
|
+
if (!this.connected) {
|
|
95
|
+
this.sendQueue.push(message);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
this.postOut(message);
|
|
99
|
+
}
|
|
100
|
+
close() {
|
|
101
|
+
this.es?.close();
|
|
102
|
+
this.es = void 0;
|
|
103
|
+
this.connected = false;
|
|
104
|
+
this.setState("closed");
|
|
105
|
+
}
|
|
106
|
+
onStateChange(listener) {
|
|
107
|
+
this.listeners.add(listener);
|
|
108
|
+
listener(this.state);
|
|
109
|
+
return () => this.listeners.delete(listener);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* For relays that wrap each frame with auth metadata: hosts can call this
|
|
113
|
+
* directly when a frame arrives via a non-SSE path. The transport will
|
|
114
|
+
* dispatch it to the bound server.
|
|
115
|
+
*/
|
|
116
|
+
async deliverFromRemote(payload, token) {
|
|
117
|
+
if (token !== void 0 && !constantTimeEqual(token, this.expectedToken)) return;
|
|
118
|
+
if (!this.server) throw new Error("SseRelayTransport has no bound server");
|
|
119
|
+
const message = typeof payload === "string" ? JSON.parse(payload) : payload;
|
|
120
|
+
await this.server.receive(this, message);
|
|
121
|
+
}
|
|
122
|
+
async postOut(message) {
|
|
123
|
+
const url = `${this.opts.baseUrl}/${encodeURIComponent(this.opts.sessionId)}/outbox?token=${encodeURIComponent(this.opts.token)}`;
|
|
124
|
+
const f = this.opts.fetch ?? fetch;
|
|
125
|
+
try {
|
|
126
|
+
await f(url, {
|
|
127
|
+
method: "POST",
|
|
128
|
+
headers: { "content-type": "application/json", "accept": "application/json" },
|
|
129
|
+
body: JSON.stringify(message)
|
|
130
|
+
});
|
|
131
|
+
} catch {
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async handleInbound(raw) {
|
|
135
|
+
if (!this.server) return;
|
|
136
|
+
let message;
|
|
137
|
+
try {
|
|
138
|
+
message = JSON.parse(raw);
|
|
139
|
+
} catch {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
await this.server.receive(this, message);
|
|
143
|
+
}
|
|
144
|
+
setState(state) {
|
|
145
|
+
this.state = state;
|
|
146
|
+
for (const l of this.listeners) l(state);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
function attachSseRelay(server, options) {
|
|
150
|
+
const transport = new SseRelayTransport(options);
|
|
151
|
+
transport.bindServer(server);
|
|
152
|
+
server.attach(transport);
|
|
153
|
+
transport.start();
|
|
154
|
+
return transport;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
exports.SseRelayTransport = SseRelayTransport;
|
|
158
|
+
exports.attachSseRelay = attachSseRelay;
|
|
159
|
+
exports.buildShareConfig = buildShareConfig;
|
|
160
|
+
exports.buildShareUrl = buildShareUrl;
|
|
161
|
+
exports.constantTimeEqual = constantTimeEqual;
|
|
162
|
+
exports.createSessionDescriptor = createSessionDescriptor;
|
|
163
|
+
exports.describeSession = describeSession;
|
|
164
|
+
exports.readSessionFromUrl = readSessionFromUrl;
|
|
165
|
+
//# sourceMappingURL=sharing.cjs.map
|
|
166
|
+
//# sourceMappingURL=sharing.cjs.map
|