@gtkx/mcp 0.21.0 → 1.0.0-rc.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 +137 -34
- package/bin/gtkx-mcp.js +9 -1
- package/dist/app-router.d.ts +35 -0
- package/dist/app-router.d.ts.map +1 -0
- package/dist/app-router.js +143 -0
- package/dist/app-router.js.map +1 -0
- package/dist/connection-registry.d.ts +12 -0
- package/dist/connection-registry.d.ts.map +1 -0
- package/dist/connection-registry.js +39 -0
- package/dist/connection-registry.js.map +1 -0
- package/dist/internal.d.ts +4 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +4 -0
- package/dist/internal.js.map +1 -0
- package/dist/protocol/errors.d.ts +25 -87
- package/dist/protocol/errors.d.ts.map +1 -1
- package/dist/protocol/errors.js +38 -101
- package/dist/protocol/errors.js.map +1 -1
- package/dist/protocol/schemas.d.ts +93 -0
- package/dist/protocol/schemas.d.ts.map +1 -0
- package/dist/protocol/schemas.js +63 -0
- package/dist/protocol/schemas.js.map +1 -0
- package/dist/reference.d.ts +13 -0
- package/dist/reference.d.ts.map +1 -0
- package/dist/reference.js +293 -0
- package/dist/reference.js.map +1 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +244 -0
- package/dist/server.js.map +1 -0
- package/dist/socket-server.d.ts +5 -38
- package/dist/socket-server.d.ts.map +1 -1
- package/dist/socket-server.js +36 -108
- package/dist/socket-server.js.map +1 -1
- package/dist/tool.d.ts +22 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +40 -0
- package/dist/tool.js.map +1 -0
- package/dist/transport.d.ts +49 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +152 -0
- package/dist/transport.js.map +1 -0
- package/package.json +15 -11
- package/src/app-router.ts +209 -0
- package/src/connection-registry.ts +57 -0
- package/src/internal.ts +17 -0
- package/src/protocol/errors.ts +67 -110
- package/src/protocol/schemas.ts +173 -0
- package/src/reference.ts +449 -0
- package/src/server.ts +334 -0
- package/src/socket-server.ts +46 -147
- package/src/tool.ts +73 -0
- package/src/transport.ts +242 -0
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -399
- package/dist/cli.js.map +0 -1
- package/dist/connection-manager.d.ts +0 -48
- package/dist/connection-manager.d.ts.map +0 -1
- package/dist/connection-manager.js +0 -185
- package/dist/connection-manager.js.map +0 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -5
- package/dist/index.js.map +0 -1
- package/dist/protocol/types.d.ts +0 -132
- package/dist/protocol/types.d.ts.map +0 -1
- package/dist/protocol/types.js +0 -46
- package/dist/protocol/types.js.map +0 -1
- package/src/cli.ts +0 -449
- package/src/connection-manager.ts +0 -247
- package/src/index.ts +0 -27
- package/src/protocol/types.ts +0 -153
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { createLogger, installGracefulShutdown, type Logger } from \"@gtkx/utils\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { createRequire } from \"node:module\";\nimport { z } from \"zod\";\nimport type { ConnectionErrorEvent } from \"./transport.js\";\nimport { type AppRegisteredEvent, AppRouter, type AppUnregisteredEvent } from \"./app-router.js\";\nimport { ConnectionRegistry } from \"./connection-registry.js\";\nimport {\n type AppInfo,\n DEFAULT_SOCKET_PATH,\n fireEventParams,\n queryParams,\n screenshotParams,\n treeParams,\n typeParams,\n widgetIdParams,\n} from \"./protocol/schemas.js\";\nimport { buildReferenceTools, createReferenceProvider, registerReferenceResources } from \"./reference.js\";\nimport { SocketServer } from \"./socket-server.js\";\nimport { defineTool, imageContent, registerTool, textContent, type Tool } from \"./tool.js\";\n\ntype CreateMcpServerOptions = {\n socketPath?: string;\n version: string;\n};\n\ntype McpServerHandle = {\n start(): Promise<void>;\n stop(): Promise<void>;\n};\n\ntype AppWindow = { id: string; title: string | null };\ntype AppWithWindows = AppInfo & { windows?: AppWindow[] };\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../package.json\") as { version: string };\nconst log: Logger = createLogger(\"mcp\");\nconst APPLICATION_ID_DESCRIPTION = \"Application ID to query. If not specified, uses the first connected app.\";\n\nconst WIDGET_ID_DESCRIPTION =\n \"Widget ID obtained from `gtkx_get_widget_tree`, `gtkx_query_widgets`, or `gtkx_get_widget_props`. \" +\n \"IDs are scoped to a single app. An ID stays valid for as long as its widget is mounted and stops \" +\n \"resolving once the widget is unmounted.\";\n\nconst applicationIdShape = { applicationId: z.string().optional().describe(APPLICATION_ID_DESCRIPTION) };\n\nconst widgetIdShape = {\n ...applicationIdShape,\n widgetId: widgetIdParams.shape.widgetId.describe(WIDGET_ID_DESCRIPTION),\n};\n\nconst treeShape = {\n ...applicationIdShape,\n rootId: treeParams.shape.rootId.describe(\n \"Render only the subtree rooted at this widget ID (from a prior tree or query). Omit for the whole app.\",\n ),\n maxDepth: treeParams.shape.maxDepth.describe(\n \"Limit how many levels deep to render; deeper descendants are summarized with a count. \" +\n \"Combine with rootId to drill in without dumping the whole tree.\",\n ),\n};\n\nconst listAppsShape = {\n waitForApps: z\n .boolean()\n .optional()\n .describe(\n \"If true, wait for at least one app to register before returning. Useful when app is still starting.\",\n ),\n timeout: z.number().optional().describe(\"Timeout in milliseconds when waitForApps is true (default: 10000)\"),\n};\n\nconst queryWidgetsShape = {\n ...applicationIdShape,\n by: queryParams.shape.by.describe(\"Query type\"),\n value: queryParams.shape.value.describe(\"Value to search for\"),\n options: queryParams.shape.options.describe(\"Additional query options\"),\n};\n\nconst typeShape = {\n ...widgetIdShape,\n text: typeParams.shape.text.describe(\"Text to type\"),\n clear: typeParams.shape.clear.describe(\"Clear existing text before typing\"),\n};\n\nconst fireEventShape = {\n ...widgetIdShape,\n signal: fireEventParams.shape.signal.describe(\"GTK4 signal name to emit\"),\n args: fireEventParams.shape.args.describe(\"Arguments to pass to the signal\"),\n};\n\nconst screenshotShape = {\n ...applicationIdShape,\n windowId: screenshotParams.shape.windowId.describe(\n \"Window ID to capture. If not specified, captures the first window.\",\n ),\n path: screenshotParams.shape.path.describe(\n \"Absolute path to write the PNG to on the app's machine. If set, the screenshot is saved there \" +\n \"in addition to being returned.\",\n ),\n};\n\nconst logSocketError = (event: Event): void => {\n const error = (event as ConnectionErrorEvent).detail;\n const code = (error as NodeJS.ErrnoException).code;\n\n if (code === \"EPIPE\" || code === \"ECONNRESET\") {\n return;\n }\n\n log.error(`socket error: ${error.message}`);\n};\n\nconst appWithWindows = async (appRouter: AppRouter, app: AppInfo): Promise<AppWithWindows> => {\n try {\n const result = await appRouter.sendToApp<{ windows: AppWindow[] }>(\n app.applicationId,\n \"app.getWindows\",\n {},\n );\n\n return { ...app, windows: result.windows };\n } catch {\n return app;\n }\n};\n\nconst listAppsTool = (appRouter: AppRouter): Tool =>\n defineTool({\n name: \"gtkx_list_apps\",\n title: \"List apps\",\n kind: \"readOnly\",\n description: \"List all connected GTKX applications and their open windows.\",\n inputSchema: listAppsShape,\n handler: async ({ waitForApps, timeout }) => {\n if (waitForApps && !appRouter.hasConnectedApps()) {\n await appRouter.waitForApp(timeout);\n }\n\n const apps = appRouter.getApps();\n const appsWithWindows = await Promise.all(apps.map((app) => appWithWindows(appRouter, app)));\n\n return textContent(JSON.stringify(appsWithWindows, null, 2));\n },\n });\n\nconst screenshotTool = (appRouter: AppRouter): Tool =>\n defineTool({\n name: \"gtkx_take_screenshot\",\n title: \"Take screenshot\",\n kind: \"readOnly\",\n description:\n \"Capture a screenshot of a window. Returns base64-encoded PNG image data, and optionally writes \" +\n \"the PNG to `path` on the app's machine. You can't target widgets from a screenshot; use \" +\n \"`gtkx_get_widget_tree` to find widget IDs for interaction.\",\n inputSchema: screenshotShape,\n handler: async ({ applicationId, ...params }) => {\n const result = await appRouter.sendToApp<{ data: string; mimeType: string; savedPath?: string }>(\n applicationId,\n \"widget.screenshot\",\n params,\n );\n\n if (result.savedPath) {\n return {\n content: [\n { type: \"text\", text: `Screenshot saved to ${result.savedPath}` },\n { type: \"image\", data: result.data, mimeType: result.mimeType },\n ],\n };\n }\n\n return imageContent(result.data, result.mimeType);\n },\n });\n\nfunction buildInspectionTools(appRouter: AppRouter): Tool[] {\n return [\n listAppsTool(appRouter),\n defineTool({\n name: \"gtkx_get_widget_tree\",\n title: \"Widget tree\",\n kind: \"readOnly\",\n description:\n \"Get the widget hierarchy for a connected GTKX app. Returns a tree of widgets with their IDs, \" +\n \"types, roles, and properties. For large apps, pass `maxDepth` for a shallow overview and/or \" +\n \"`rootId` to render just one subtree instead of the whole (possibly truncated) tree.\",\n inputSchema: treeShape,\n handler: async ({ applicationId, rootId, maxDepth }) => {\n const result = await appRouter.sendToApp<{ tree: string }>(applicationId, \"widget.getTree\", {\n rootId,\n maxDepth,\n });\n\n return textContent(result.tree);\n },\n }),\n defineTool({\n name: \"gtkx_query_widgets\",\n title: \"Query widgets\",\n kind: \"readOnly\",\n description:\n \"Find widgets by role, text, name, or label. Returns matching widgets with their IDs and properties.\",\n inputSchema: queryWidgetsShape,\n handler: async ({ applicationId, ...params }) => {\n const result = await appRouter.sendToApp(applicationId, \"widget.query\", params);\n\n return textContent(JSON.stringify(result, null, 2));\n },\n }),\n defineTool({\n name: \"gtkx_get_widget_props\",\n title: \"Get widget properties\",\n kind: \"readOnly\",\n description:\n \"Get a fixed summary of one widget by ID: type, accessible role, name, text, sensitivity, \" +\n \"visibility, CSS classes, and the full subtree of descendant widgets. It does not return \" +\n \"arbitrary GObject properties.\",\n inputSchema: widgetIdShape,\n handler: async ({ applicationId, ...params }) => {\n const result = await appRouter.sendToApp(applicationId, \"widget.getProps\", params);\n\n return textContent(JSON.stringify(result, null, 2));\n },\n }),\n screenshotTool(appRouter),\n ];\n}\n\nfunction buildInteractionTools(appRouter: AppRouter): Tool[] {\n return [\n defineTool({\n name: \"gtkx_click\",\n title: \"Click widget\",\n kind: \"action\",\n description: \"Click a widget. Works with buttons, checkboxes, and other interactive widgets.\",\n inputSchema: widgetIdShape,\n handler: async ({ applicationId, ...params }) => {\n await appRouter.sendToApp(applicationId, \"widget.click\", params);\n\n return textContent(\"Clicked\");\n },\n }),\n defineTool({\n name: \"gtkx_type\",\n title: \"Type text\",\n kind: \"action\",\n description: \"Type text into an editable widget like Entry or TextView\",\n inputSchema: typeShape,\n handler: async ({ applicationId, ...params }) => {\n await appRouter.sendToApp(applicationId, \"widget.type\", params);\n\n return textContent(\"Typed text\");\n },\n }),\n defineTool({\n name: \"gtkx_fire_event\",\n title: \"Fire event\",\n kind: \"action\",\n description: \"Emit a GTK4 signal on a widget. Use this for custom interactions.\",\n inputSchema: fireEventShape,\n handler: async ({ applicationId, ...params }) => {\n await appRouter.sendToApp(applicationId, \"widget.fireEvent\", params);\n\n return textContent(\"Fired event\");\n },\n }),\n ];\n}\n\nfunction buildTools(appRouter: AppRouter): Tool[] {\n return [...buildInspectionTools(appRouter), ...buildInteractionTools(appRouter)];\n}\n\nconst createMcpServer = (options: CreateMcpServerOptions): McpServerHandle => {\n const socketPath = options.socketPath ?? DEFAULT_SOCKET_PATH;\n const registry = new ConnectionRegistry();\n const socketServer = new SocketServer(registry, socketPath);\n const appRouter = new AppRouter(registry);\n registry.addEventListener(\"error\", logSocketError);\n\n appRouter.addEventListener(\"appRegistered\", (event) => {\n const appInfo = (event as AppRegisteredEvent).detail;\n log.info(`app registered: ${appInfo.applicationId} (PID: ${String(appInfo.pid)})`);\n });\n\n appRouter.addEventListener(\"appUnregistered\", (event) => {\n log.info(`app unregistered: ${(event as AppUnregisteredEvent).detail}`);\n });\n\n const mcpServer = new McpServer({ name: \"gtkx-mcp\", version: options.version });\n const referenceProvider = createReferenceProvider(() => appRouter.getProjectRoot() ?? process.cwd());\n\n for (const tool of [...buildTools(appRouter), ...buildReferenceTools(referenceProvider)]) {\n registerTool(mcpServer, tool);\n }\n\n registerReferenceResources(mcpServer, referenceProvider);\n let isStopped = false;\n\n const stop = async (): Promise<void> => {\n if (isStopped) {\n return;\n }\n\n isStopped = true;\n await socketServer.stop();\n await mcpServer.close();\n };\n\n const start = async (): Promise<void> => {\n await socketServer.start();\n log.info(`socket server listening on ${socketPath}`);\n const transport = new StdioServerTransport();\n process.stdin.on(\"end\", () => void stop());\n process.stdin.on(\"close\", () => void stop());\n await mcpServer.connect(transport);\n };\n\n return { start, stop };\n};\n\nasync function main(): Promise<void> {\n const server = createMcpServer({ version });\n\n installGracefulShutdown({\n onSignal: () => server.stop(),\n });\n\n await server.start();\n}\n\nexport { log, createMcpServer, main };\n"]}
|
package/dist/socket-server.d.ts
CHANGED
|
@@ -1,44 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { type IpcMessage, type IpcRequest, type IpcResponse } from "./protocol/types.js";
|
|
4
|
-
type SocketServerEventMap = {
|
|
5
|
-
connection: [AppConnection];
|
|
6
|
-
disconnection: [AppConnection];
|
|
7
|
-
request: [AppConnection, IpcRequest];
|
|
8
|
-
response: [AppConnection, IpcResponse];
|
|
9
|
-
error: [Error];
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* Represents a connected application.
|
|
13
|
-
*/
|
|
14
|
-
export type AppConnection = {
|
|
15
|
-
/** Unique connection identifier */
|
|
16
|
-
id: string;
|
|
17
|
-
/** The underlying socket */
|
|
18
|
-
socket: net.Socket;
|
|
19
|
-
/** Buffer for incomplete messages */
|
|
20
|
-
buffer: string;
|
|
21
|
-
};
|
|
22
|
-
/**
|
|
23
|
-
* Unix domain socket server for MCP communication.
|
|
24
|
-
*
|
|
25
|
-
* Manages connections from GTKX applications and handles IPC messaging.
|
|
26
|
-
*/
|
|
27
|
-
export declare class SocketServer extends EventEmitter<SocketServerEventMap> {
|
|
1
|
+
import type { ConnectionRegistry } from "./connection-registry.js";
|
|
2
|
+
declare class SocketServer {
|
|
28
3
|
private server;
|
|
29
|
-
private connections;
|
|
30
4
|
private socketPath;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
get isListening(): boolean;
|
|
34
|
-
getConnections(): AppConnection[];
|
|
35
|
-
getConnection(id: string): AppConnection | undefined;
|
|
5
|
+
private registry;
|
|
6
|
+
constructor(registry: ConnectionRegistry, socketPath?: string);
|
|
36
7
|
start(): Promise<void>;
|
|
37
8
|
stop(): Promise<void>;
|
|
38
|
-
send(connectionId: string, message: IpcMessage): boolean;
|
|
39
|
-
private handleConnection;
|
|
40
|
-
private handleData;
|
|
41
|
-
private processMessage;
|
|
42
9
|
}
|
|
43
|
-
export {};
|
|
10
|
+
export { SocketServer };
|
|
44
11
|
//# sourceMappingURL=socket-server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-server.d.ts","sourceRoot":"","sources":["../src/socket-server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"socket-server.d.ts","sourceRoot":"","sources":["../src/socket-server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAiCnE,cAAM,YAAY;IACd,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAqB;gBAEzB,QAAQ,EAAE,kBAAkB,EAAE,UAAU,GAAE,MAA4B;IAK5E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAmB9B;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
package/dist/socket-server.js
CHANGED
|
@@ -1,47 +1,51 @@
|
|
|
1
|
-
import EventEmitter from "node:events";
|
|
2
1
|
import * as fs from "node:fs";
|
|
3
2
|
import * as net from "node:net";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
import { DEFAULT_SOCKET_PATH } from "./protocol/schemas.js";
|
|
4
|
+
import { connectionErrorEvent } from "./transport.js";
|
|
5
|
+
const socketIsLive = (socketPath) => new Promise((resolve) => {
|
|
6
|
+
const probe = net.connect(socketPath);
|
|
7
|
+
probe.once("connect", () => {
|
|
8
|
+
probe.destroy();
|
|
9
|
+
resolve(true);
|
|
10
|
+
});
|
|
11
|
+
probe.once("error", () => {
|
|
12
|
+
resolve(false);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
const removeStaleSocket = async (socketPath) => {
|
|
16
|
+
if (!fs.existsSync(socketPath)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (await socketIsLive(socketPath)) {
|
|
20
|
+
throw new Error(`Another GTKX MCP server already owns ${socketPath}. ` +
|
|
21
|
+
"Stop the other server (for example, the gtkx MCP server of another active session) and reconnect.");
|
|
22
|
+
}
|
|
23
|
+
fs.unlinkSync(socketPath);
|
|
24
|
+
};
|
|
25
|
+
class SocketServer {
|
|
12
26
|
server = null;
|
|
13
|
-
connections = new Map();
|
|
14
27
|
socketPath;
|
|
15
|
-
|
|
16
|
-
|
|
28
|
+
registry;
|
|
29
|
+
constructor(registry, socketPath = DEFAULT_SOCKET_PATH) {
|
|
30
|
+
this.registry = registry;
|
|
17
31
|
this.socketPath = socketPath;
|
|
18
32
|
}
|
|
19
|
-
get path() {
|
|
20
|
-
return this.socketPath;
|
|
21
|
-
}
|
|
22
|
-
get isListening() {
|
|
23
|
-
return this.server?.listening ?? false;
|
|
24
|
-
}
|
|
25
|
-
getConnections() {
|
|
26
|
-
return Array.from(this.connections.values());
|
|
27
|
-
}
|
|
28
|
-
getConnection(id) {
|
|
29
|
-
return this.connections.get(id);
|
|
30
|
-
}
|
|
31
33
|
async start() {
|
|
32
34
|
if (this.server) {
|
|
33
35
|
return;
|
|
34
36
|
}
|
|
35
|
-
|
|
36
|
-
fs.unlinkSync(this.socketPath);
|
|
37
|
-
}
|
|
37
|
+
await removeStaleSocket(this.socketPath);
|
|
38
38
|
return new Promise((resolve, reject) => {
|
|
39
|
-
this.server = net.createServer((socket) => this.
|
|
39
|
+
this.server = net.createServer((socket) => this.registry.register(socket));
|
|
40
|
+
let isListening = false;
|
|
40
41
|
this.server.on("error", (error) => {
|
|
41
|
-
this.
|
|
42
|
-
|
|
42
|
+
this.registry.dispatchEvent(connectionErrorEvent(error));
|
|
43
|
+
if (!isListening) {
|
|
44
|
+
reject(error);
|
|
45
|
+
}
|
|
43
46
|
});
|
|
44
47
|
this.server.listen(this.socketPath, () => {
|
|
48
|
+
isListening = true;
|
|
45
49
|
resolve();
|
|
46
50
|
});
|
|
47
51
|
});
|
|
@@ -50,10 +54,7 @@ export class SocketServer extends EventEmitter {
|
|
|
50
54
|
if (!this.server) {
|
|
51
55
|
return;
|
|
52
56
|
}
|
|
53
|
-
|
|
54
|
-
connection.socket.destroy();
|
|
55
|
-
}
|
|
56
|
-
this.connections.clear();
|
|
57
|
+
this.registry.dispose("Server stopping");
|
|
57
58
|
return new Promise((resolve) => {
|
|
58
59
|
this.server?.close(() => {
|
|
59
60
|
this.server = null;
|
|
@@ -64,79 +65,6 @@ export class SocketServer extends EventEmitter {
|
|
|
64
65
|
});
|
|
65
66
|
});
|
|
66
67
|
}
|
|
67
|
-
send(connectionId, message) {
|
|
68
|
-
const connection = this.connections.get(connectionId);
|
|
69
|
-
if (!connection?.socket.writable) {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
const data = `${JSON.stringify(message)}\n`;
|
|
73
|
-
connection.socket.write(data);
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
handleConnection(socket) {
|
|
77
|
-
const connectionId = crypto.randomUUID();
|
|
78
|
-
const connection = {
|
|
79
|
-
id: connectionId,
|
|
80
|
-
socket,
|
|
81
|
-
buffer: "",
|
|
82
|
-
};
|
|
83
|
-
this.connections.set(connectionId, connection);
|
|
84
|
-
this.emit("connection", connection);
|
|
85
|
-
socket.on("data", (data) => this.handleData(connection, data));
|
|
86
|
-
socket.on("close", () => {
|
|
87
|
-
this.connections.delete(connectionId);
|
|
88
|
-
this.emit("disconnection", connection);
|
|
89
|
-
});
|
|
90
|
-
socket.on("error", (error) => {
|
|
91
|
-
this.emit("error", error);
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
handleData(connection, data) {
|
|
95
|
-
connection.buffer += data.toString();
|
|
96
|
-
let newlineIndex = connection.buffer.indexOf("\n");
|
|
97
|
-
while (newlineIndex !== -1) {
|
|
98
|
-
const line = connection.buffer.slice(0, newlineIndex);
|
|
99
|
-
connection.buffer = connection.buffer.slice(newlineIndex + 1);
|
|
100
|
-
if (line.trim()) {
|
|
101
|
-
this.processMessage(connection, line);
|
|
102
|
-
}
|
|
103
|
-
newlineIndex = connection.buffer.indexOf("\n");
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
processMessage(connection, line) {
|
|
107
|
-
let parsed;
|
|
108
|
-
try {
|
|
109
|
-
parsed = JSON.parse(line);
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
const response = {
|
|
113
|
-
id: "unknown",
|
|
114
|
-
error: invalidRequestError("Invalid JSON").toIpcError(),
|
|
115
|
-
};
|
|
116
|
-
this.send(connection.id, response);
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
const message = parsed;
|
|
120
|
-
const hasMethod = typeof message.method === "string";
|
|
121
|
-
if (hasMethod) {
|
|
122
|
-
const requestResult = IpcRequestSchema.safeParse(parsed);
|
|
123
|
-
if (requestResult.success) {
|
|
124
|
-
this.emit("request", connection, requestResult.data);
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
const responseResult = IpcResponseSchema.safeParse(parsed);
|
|
130
|
-
if (responseResult.success) {
|
|
131
|
-
this.emit("response", connection, responseResult.data);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
const response = {
|
|
136
|
-
id: message.id ?? "unknown",
|
|
137
|
-
error: invalidRequestError("Invalid message format").toIpcError(),
|
|
138
|
-
};
|
|
139
|
-
this.send(connection.id, response);
|
|
140
|
-
}
|
|
141
68
|
}
|
|
69
|
+
export { SocketServer };
|
|
142
70
|
//# sourceMappingURL=socket-server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-server.js","sourceRoot":"","sources":["../src/socket-server.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"socket-server.js","sourceRoot":"","sources":["../src/socket-server.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from \"node:fs\";\nimport * as net from \"node:net\";\nimport type { ConnectionRegistry } from \"./connection-registry.js\";\nimport { DEFAULT_SOCKET_PATH } from \"./protocol/schemas.js\";\nimport { connectionErrorEvent } from \"./transport.js\";\n\nconst socketIsLive = (socketPath: string): Promise<boolean> =>\n new Promise((resolve) => {\n const probe = net.connect(socketPath);\n\n probe.once(\"connect\", () => {\n probe.destroy();\n resolve(true);\n });\n\n probe.once(\"error\", () => {\n resolve(false);\n });\n });\n\nconst removeStaleSocket = async (socketPath: string): Promise<void> => {\n if (!fs.existsSync(socketPath)) {\n return;\n }\n\n if (await socketIsLive(socketPath)) {\n throw new Error(\n `Another GTKX MCP server already owns ${socketPath}. ` +\n \"Stop the other server (for example, the gtkx MCP server of another active session) and reconnect.\",\n );\n }\n\n fs.unlinkSync(socketPath);\n};\n\nclass SocketServer {\n private server: net.Server | null = null;\n private socketPath: string;\n private registry: ConnectionRegistry;\n\n constructor(registry: ConnectionRegistry, socketPath: string = DEFAULT_SOCKET_PATH) {\n this.registry = registry;\n this.socketPath = socketPath;\n }\n\n async start(): Promise<void> {\n if (this.server) {\n return;\n }\n\n await removeStaleSocket(this.socketPath);\n\n return new Promise((resolve, reject) => {\n this.server = net.createServer((socket) => this.registry.register(socket));\n let isListening = false;\n\n this.server.on(\"error\", (error) => {\n this.registry.dispatchEvent(connectionErrorEvent(error));\n\n if (!isListening) {\n reject(error);\n }\n });\n\n this.server.listen(this.socketPath, () => {\n isListening = true;\n resolve();\n });\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n return;\n }\n\n this.registry.dispose(\"Server stopping\");\n\n return new Promise((resolve) => {\n this.server?.close(() => {\n this.server = null;\n\n if (fs.existsSync(this.socketPath)) {\n fs.unlinkSync(this.socketPath);\n }\n\n resolve();\n });\n });\n }\n}\n\nexport { SocketServer };\n"]}
|
package/dist/tool.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import type { z } from "zod";
|
|
4
|
+
type ToolArgs<Shape extends Record<string, z.ZodType>> = {
|
|
5
|
+
[K in keyof Shape]: z.output<Shape[K]>;
|
|
6
|
+
};
|
|
7
|
+
type ToolKind = "readOnly" | "action";
|
|
8
|
+
type Tool<Shape extends Record<string, z.ZodType> = Record<string, z.ZodType>> = {
|
|
9
|
+
name: string;
|
|
10
|
+
title: string;
|
|
11
|
+
kind: ToolKind;
|
|
12
|
+
description: string;
|
|
13
|
+
inputSchema: Shape;
|
|
14
|
+
handler: (args: ToolArgs<Shape>) => Promise<CallToolResult>;
|
|
15
|
+
};
|
|
16
|
+
declare const textContent: (text: string) => CallToolResult;
|
|
17
|
+
declare const textError: (text: string) => CallToolResult;
|
|
18
|
+
declare const imageContent: (data: string, mimeType: string) => CallToolResult;
|
|
19
|
+
declare const defineTool: <Shape extends Record<string, z.ZodType>>(tool: Tool<Shape>) => Tool;
|
|
20
|
+
declare const registerTool: (server: McpServer, tool: Tool) => void;
|
|
21
|
+
export { textContent, textError, imageContent, defineTool, registerTool, type ToolArgs, type Tool };
|
|
22
|
+
//# sourceMappingURL=tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../src/tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAgB,MAAM,yCAAyC,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAG7B,KAAK,QAAQ,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC;AACpG,KAAK,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEtC,KAAK,IAAI,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC;IACnB,OAAO,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;CAC/D,CAAC;AAEF,QAAA,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,cAAyD,CAAC;AAE9F,QAAA,MAAM,SAAS,GAAI,MAAM,MAAM,KAAG,cAGhC,CAAC;AAEH,QAAA,MAAM,YAAY,GAAI,MAAM,MAAM,EAAE,UAAU,MAAM,KAAG,cAErD,CAAC;AAwBH,QAAA,MAAM,UAAU,GAAI,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC,KAAK,CAAC,KAAG,IAAoB,CAAC;AAEtG,QAAA,MAAM,YAAY,GAAI,QAAQ,SAAS,EAAE,MAAM,IAAI,KAAG,IAkBrD,CAAC;AAEF,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC"}
|
package/dist/tool.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ProtocolError } from "./protocol/errors.js";
|
|
2
|
+
const textContent = (text) => ({ content: [{ type: "text", text }] });
|
|
3
|
+
const textError = (text) => ({
|
|
4
|
+
content: [{ type: "text", text }],
|
|
5
|
+
isError: true,
|
|
6
|
+
});
|
|
7
|
+
const imageContent = (data, mimeType) => ({
|
|
8
|
+
content: [{ type: "image", data, mimeType }],
|
|
9
|
+
});
|
|
10
|
+
const hasStringHint = (data) => typeof data === "object" && data !== null && "hint" in data && typeof data.hint === "string";
|
|
11
|
+
const errorToResult = (error) => {
|
|
12
|
+
if (error instanceof ProtocolError) {
|
|
13
|
+
return textError(hasStringHint(error.data) ? `${error.message}\n${error.data.hint}` : error.message);
|
|
14
|
+
}
|
|
15
|
+
return textError(error instanceof Error ? error.message : String(error));
|
|
16
|
+
};
|
|
17
|
+
const runTool = async (handler, args) => {
|
|
18
|
+
try {
|
|
19
|
+
return await handler(args);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return errorToResult(error);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const defineTool = (tool) => tool;
|
|
26
|
+
const registerTool = (server, tool) => {
|
|
27
|
+
const callback = ((args) => runTool(tool.handler, args));
|
|
28
|
+
server.registerTool(tool.name, {
|
|
29
|
+
description: tool.description,
|
|
30
|
+
inputSchema: tool.inputSchema,
|
|
31
|
+
annotations: {
|
|
32
|
+
title: tool.title,
|
|
33
|
+
readOnlyHint: tool.kind === "readOnly",
|
|
34
|
+
destructiveHint: tool.kind === "action",
|
|
35
|
+
openWorldHint: true,
|
|
36
|
+
},
|
|
37
|
+
}, callback);
|
|
38
|
+
};
|
|
39
|
+
export { textContent, textError, imageContent, defineTool, registerTool };
|
|
40
|
+
//# sourceMappingURL=tool.js.map
|
package/dist/tool.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.js","sourceRoot":"","sources":["../src/tool.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAcrD,MAAM,WAAW,GAAG,CAAC,IAAY,EAAkB,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;AAE9F,MAAM,SAAS,GAAG,CAAC,IAAY,EAAkB,EAAE,CAAC,CAAC;IACjD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACjC,OAAO,EAAE,IAAI;CAChB,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,QAAgB,EAAkB,EAAE,CAAC,CAAC;IACtE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;CAC/C,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,CAAC,IAAa,EAA4B,EAAE,CAC9D,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC;AAEjG,MAAM,aAAa,GAAG,CAAC,KAAc,EAAkB,EAAE;IACrD,IAAI,KAAK,YAAY,aAAa,EAAE,CAAC;QACjC,OAAO,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACzG,CAAC;IAED,OAAO,SAAS,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7E,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,KAAK,EACjB,OAA+E,EAC/E,IAAyC,EAClB,EAAE;IACzB,IAAI,CAAC;QACD,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAA0C,IAAiB,EAAQ,EAAE,CAAC,IAAY,CAAC;AAEtG,MAAM,YAAY,GAAG,CAAC,MAAiB,EAAE,IAAU,EAAQ,EAAE;IACzD,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAyC,EAAE,EAAE,CAC5D,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAA4C,CAAC;IAE5E,MAAM,CAAC,YAAY,CACf,IAAI,CAAC,IAAI,EACT;QACI,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,WAAW,EAAE;YACT,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,YAAY,EAAE,IAAI,CAAC,IAAI,KAAK,UAAU;YACtC,eAAe,EAAE,IAAI,CAAC,IAAI,KAAK,QAAQ;YACvC,aAAa,EAAE,IAAI;SACtB;KACJ,EACD,QAAQ,CACX,CAAC;AACN,CAAC,CAAC;AAEF,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAA4B,CAAC","sourcesContent":["import type { McpServer, ToolCallback } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { z } from \"zod\";\nimport { ProtocolError } from \"./protocol/errors.js\";\n\ntype ToolArgs<Shape extends Record<string, z.ZodType>> = { [K in keyof Shape]: z.output<Shape[K]> };\ntype ToolKind = \"readOnly\" | \"action\";\n\ntype Tool<Shape extends Record<string, z.ZodType> = Record<string, z.ZodType>> = {\n name: string;\n title: string;\n kind: ToolKind;\n description: string;\n inputSchema: Shape;\n handler: (args: ToolArgs<Shape>) => Promise<CallToolResult>;\n};\n\nconst textContent = (text: string): CallToolResult => ({ content: [{ type: \"text\", text }] });\n\nconst textError = (text: string): CallToolResult => ({\n content: [{ type: \"text\", text }],\n isError: true,\n});\n\nconst imageContent = (data: string, mimeType: string): CallToolResult => ({\n content: [{ type: \"image\", data, mimeType }],\n});\n\nconst hasStringHint = (data: unknown): data is { hint: string } =>\n typeof data === \"object\" && data !== null && \"hint\" in data && typeof data.hint === \"string\";\n\nconst errorToResult = (error: unknown): CallToolResult => {\n if (error instanceof ProtocolError) {\n return textError(hasStringHint(error.data) ? `${error.message}\\n${error.data.hint}` : error.message);\n }\n\n return textError(error instanceof Error ? error.message : String(error));\n};\n\nconst runTool = async (\n handler: (args: ToolArgs<Record<string, z.ZodType>>) => Promise<CallToolResult>,\n args: ToolArgs<Record<string, z.ZodType>>,\n): Promise<CallToolResult> => {\n try {\n return await handler(args);\n } catch (error) {\n return errorToResult(error);\n }\n};\n\nconst defineTool = <Shape extends Record<string, z.ZodType>>(tool: Tool<Shape>): Tool => tool as Tool;\n\nconst registerTool = (server: McpServer, tool: Tool): void => {\n const callback = ((args: ToolArgs<Record<string, z.ZodType>>) =>\n runTool(tool.handler, args)) as ToolCallback<Record<string, z.ZodType>>;\n\n server.registerTool(\n tool.name,\n {\n description: tool.description,\n inputSchema: tool.inputSchema,\n annotations: {\n title: tool.title,\n readOnlyHint: tool.kind === \"readOnly\",\n destructiveHint: tool.kind === \"action\",\n openWorldHint: true,\n },\n },\n callback,\n );\n};\n\nexport { textContent, textError, imageContent, defineTool, registerTool, type ToolArgs, type Tool };\n"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Socket } from "node:net";
|
|
2
|
+
import type { Duplex } from "node:stream";
|
|
3
|
+
import { ProtocolError } from "./protocol/errors.js";
|
|
4
|
+
import { type Message, type Request } from "./protocol/schemas.js";
|
|
5
|
+
type ProtocolConnectionEvents = {
|
|
6
|
+
request: Request;
|
|
7
|
+
invalid: {
|
|
8
|
+
id: string;
|
|
9
|
+
error: ProtocolError;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
type ConnectionEvent = CustomEvent<ProtocolConnection>;
|
|
13
|
+
type ConnectionRequestEvent = CustomEvent<{
|
|
14
|
+
connection: ProtocolConnection;
|
|
15
|
+
request: Request;
|
|
16
|
+
}>;
|
|
17
|
+
type ConnectionErrorEvent = CustomEvent<Error>;
|
|
18
|
+
type AppConnections = {
|
|
19
|
+
send(connectionId: string, message: Message): void;
|
|
20
|
+
} & EventTarget;
|
|
21
|
+
declare function connectionRequestEvent(connection: ProtocolConnection, request: Request): ConnectionRequestEvent;
|
|
22
|
+
declare function connectionDisconnectionEvent(connection: ProtocolConnection): ConnectionEvent;
|
|
23
|
+
declare function connectionErrorEvent(error: Error): ConnectionErrorEvent;
|
|
24
|
+
declare class ConnectionClosedError extends Error {
|
|
25
|
+
constructor();
|
|
26
|
+
}
|
|
27
|
+
declare class ProtocolConnection extends EventTarget {
|
|
28
|
+
static fromSocket(socket: Socket, options?: {
|
|
29
|
+
onClose?: () => void;
|
|
30
|
+
onError?: (error: Error) => void;
|
|
31
|
+
}): ProtocolConnection;
|
|
32
|
+
private buffer;
|
|
33
|
+
private pending;
|
|
34
|
+
private writer;
|
|
35
|
+
id: string;
|
|
36
|
+
constructor(writer: Duplex);
|
|
37
|
+
private notify;
|
|
38
|
+
private rejectWhenClosed;
|
|
39
|
+
private dispatchParsed;
|
|
40
|
+
private processLine;
|
|
41
|
+
private handleResponse;
|
|
42
|
+
on<K extends keyof ProtocolConnectionEvents>(type: K, listener: (detail: ProtocolConnectionEvents[K]) => void): void;
|
|
43
|
+
feed(data: Buffer | string): void;
|
|
44
|
+
write(message: Message): void;
|
|
45
|
+
send<T = unknown>(method: string, params: unknown, timeout: number): Promise<T>;
|
|
46
|
+
rejectPending(error: Error): void;
|
|
47
|
+
}
|
|
48
|
+
export { ConnectionClosedError, connectionDisconnectionEvent, connectionErrorEvent, connectionRequestEvent, ProtocolConnection, type ProtocolConnectionEvents, type AppConnections, type ConnectionEvent, type ConnectionErrorEvent, type ConnectionRequestEvent, };
|
|
49
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAA+C,aAAa,EAAuB,MAAM,sBAAsB,CAAC;AACvH,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAgD,MAAM,uBAAuB,CAAC;AAEjH,KAAK,wBAAwB,GAAG;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE,CAAC;CACjD,CAAC;AAQF,KAAK,eAAe,GAAG,WAAW,CAAC,kBAAkB,CAAC,CAAC;AACvD,KAAK,sBAAsB,GAAG,WAAW,CAAC;IAAE,UAAU,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAChG,KAAK,oBAAoB,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;AAE/C,KAAK,cAAc,GAAG;IAClB,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACtD,GAAG,WAAW,CAAC;AAEhB,iBAAS,sBAAsB,CAAC,UAAU,EAAE,kBAAkB,EAAE,OAAO,EAAE,OAAO,GAAG,sBAAsB,CAExG;AAED,iBAAS,4BAA4B,CAAC,UAAU,EAAE,kBAAkB,GAAG,eAAe,CAErF;AAED,iBAAS,oBAAoB,CAAC,KAAK,EAAE,KAAK,GAAG,oBAAoB,CAEhE;AAED,cAAM,qBAAsB,SAAQ,KAAK;;CAKxC;AAED,cAAM,kBAAmB,SAAQ,WAAW;IACxC,MAAM,CAAC,UAAU,CACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;QACL,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;QACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;KAC/B,GACP,kBAAkB;IAmBrB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,OAAO,CAA0C;IACzD,OAAO,CAAC,MAAM,CAAS;IAEvB,EAAE,EAAE,MAAM,CAAuB;gBAErB,MAAM,EAAE,MAAM;IAK1B,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,cAAc;IAqBtB,EAAE,CAAC,CAAC,SAAS,MAAM,wBAAwB,EACvC,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC,KAAK,IAAI,GACxD,IAAI;IAMP,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAgBjC,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAQ7B,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IA0B/E,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;CAQpC;AAED,OAAO,EACH,qBAAqB,EACrB,4BAA4B,EAC5B,oBAAoB,EACpB,sBAAsB,EACtB,kBAAkB,EAClB,KAAK,wBAAwB,EAC7B,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,GAC9B,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { ErrorCode, invalidRequestError, isErrorCode, ProtocolError, requestTimeoutError } from "./protocol/errors.js";
|
|
2
|
+
import { RequestSchema, ResponseSchema } from "./protocol/schemas.js";
|
|
3
|
+
function connectionRequestEvent(connection, request) {
|
|
4
|
+
return new CustomEvent("request", { detail: { connection, request } });
|
|
5
|
+
}
|
|
6
|
+
function connectionDisconnectionEvent(connection) {
|
|
7
|
+
return new CustomEvent("disconnection", { detail: connection });
|
|
8
|
+
}
|
|
9
|
+
function connectionErrorEvent(error) {
|
|
10
|
+
return new CustomEvent("error", { detail: error });
|
|
11
|
+
}
|
|
12
|
+
class ConnectionClosedError extends Error {
|
|
13
|
+
constructor() {
|
|
14
|
+
super("Connection stream is not writable");
|
|
15
|
+
this.name = "ConnectionClosedError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
class ProtocolConnection extends EventTarget {
|
|
19
|
+
static fromSocket(socket, options = {}) {
|
|
20
|
+
const connection = new ProtocolConnection(socket);
|
|
21
|
+
socket.on("data", (data) => {
|
|
22
|
+
connection.feed(data);
|
|
23
|
+
});
|
|
24
|
+
socket.on("close", () => {
|
|
25
|
+
connection.rejectPending(new Error("Connection closed"));
|
|
26
|
+
options.onClose?.();
|
|
27
|
+
});
|
|
28
|
+
if (options.onError) {
|
|
29
|
+
socket.on("error", options.onError);
|
|
30
|
+
}
|
|
31
|
+
return connection;
|
|
32
|
+
}
|
|
33
|
+
buffer = "";
|
|
34
|
+
pending = new Map();
|
|
35
|
+
writer;
|
|
36
|
+
id = crypto.randomUUID();
|
|
37
|
+
constructor(writer) {
|
|
38
|
+
super();
|
|
39
|
+
this.writer = writer;
|
|
40
|
+
}
|
|
41
|
+
notify(type, detail) {
|
|
42
|
+
this.dispatchEvent(new CustomEvent(type, { detail }));
|
|
43
|
+
}
|
|
44
|
+
rejectWhenClosed(id, timeoutHandle, reject) {
|
|
45
|
+
if (this.writer.writable) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
clearTimeout(timeoutHandle);
|
|
49
|
+
this.pending.delete(id);
|
|
50
|
+
reject(new ConnectionClosedError());
|
|
51
|
+
}
|
|
52
|
+
dispatchParsed(parsed) {
|
|
53
|
+
const message = parsed;
|
|
54
|
+
if (typeof message.method === "string") {
|
|
55
|
+
const requestResult = RequestSchema.safeParse(parsed);
|
|
56
|
+
if (!requestResult.success) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
this.notify("request", requestResult.data);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
const responseResult = ResponseSchema.safeParse(parsed);
|
|
63
|
+
if (!responseResult.success) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
this.handleResponse(responseResult.data);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
processLine(line) {
|
|
70
|
+
let parsed;
|
|
71
|
+
try {
|
|
72
|
+
parsed = JSON.parse(line);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
this.notify("invalid", { id: "unknown", error: invalidRequestError("Invalid JSON") });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (this.dispatchParsed(parsed)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const message = parsed;
|
|
82
|
+
const id = typeof message.id === "string" ? message.id : "unknown";
|
|
83
|
+
this.notify("invalid", { id, error: invalidRequestError("Invalid message format") });
|
|
84
|
+
}
|
|
85
|
+
handleResponse(response) {
|
|
86
|
+
const entry = this.pending.get(response.id);
|
|
87
|
+
if (!entry) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
clearTimeout(entry.timeout);
|
|
91
|
+
this.pending.delete(response.id);
|
|
92
|
+
if (response.error) {
|
|
93
|
+
const err = response.error;
|
|
94
|
+
entry.reject(new ProtocolError(isErrorCode(err.code) ? err.code : ErrorCode.INTERNAL_ERROR, err.message, err.data));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
entry.resolve(response.result);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
on(type, listener) {
|
|
101
|
+
this.addEventListener(type, (event) => {
|
|
102
|
+
listener(event.detail);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
feed(data) {
|
|
106
|
+
this.buffer += typeof data === "string" ? data : data.toString();
|
|
107
|
+
let newlineIndex = this.buffer.indexOf("\n");
|
|
108
|
+
while (newlineIndex !== -1) {
|
|
109
|
+
const line = this.buffer.slice(0, newlineIndex);
|
|
110
|
+
this.buffer = this.buffer.slice(newlineIndex + 1);
|
|
111
|
+
if (line.trim()) {
|
|
112
|
+
this.processLine(line);
|
|
113
|
+
}
|
|
114
|
+
newlineIndex = this.buffer.indexOf("\n");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
write(message) {
|
|
118
|
+
if (!this.writer.writable) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
this.writer.write(`${JSON.stringify(message)}\n`);
|
|
122
|
+
}
|
|
123
|
+
send(method, params, timeout) {
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
if (!this.writer.writable) {
|
|
126
|
+
reject(new ConnectionClosedError());
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const id = crypto.randomUUID();
|
|
130
|
+
const timeoutHandle = setTimeout(() => {
|
|
131
|
+
this.pending.delete(id);
|
|
132
|
+
reject(requestTimeoutError(timeout));
|
|
133
|
+
}, timeout);
|
|
134
|
+
this.pending.set(id, {
|
|
135
|
+
resolve: resolve,
|
|
136
|
+
reject,
|
|
137
|
+
timeout: timeoutHandle,
|
|
138
|
+
});
|
|
139
|
+
this.write({ id, method, params });
|
|
140
|
+
this.rejectWhenClosed(id, timeoutHandle, reject);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
rejectPending(error) {
|
|
144
|
+
for (const entry of this.pending.values()) {
|
|
145
|
+
clearTimeout(entry.timeout);
|
|
146
|
+
entry.reject(error);
|
|
147
|
+
}
|
|
148
|
+
this.pending.clear();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export { ConnectionClosedError, connectionDisconnectionEvent, connectionErrorEvent, connectionRequestEvent, ProtocolConnection, };
|
|
152
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Socket } from \"node:net\";\nimport type { Duplex } from \"node:stream\";\nimport { ErrorCode, invalidRequestError, isErrorCode, ProtocolError, requestTimeoutError } from \"./protocol/errors.js\";\nimport { type Message, type Request, RequestSchema, type Response, ResponseSchema } from \"./protocol/schemas.js\";\n\ntype ProtocolConnectionEvents = {\n request: Request;\n invalid: { id: string; error: ProtocolError };\n};\n\ntype PendingRequest = {\n resolve: (result: unknown) => void;\n reject: (error: Error) => void;\n timeout: NodeJS.Timeout;\n};\n\ntype ConnectionEvent = CustomEvent<ProtocolConnection>;\ntype ConnectionRequestEvent = CustomEvent<{ connection: ProtocolConnection; request: Request }>;\ntype ConnectionErrorEvent = CustomEvent<Error>;\n\ntype AppConnections = {\n send(connectionId: string, message: Message): void;\n} & EventTarget;\n\nfunction connectionRequestEvent(connection: ProtocolConnection, request: Request): ConnectionRequestEvent {\n return new CustomEvent(\"request\", { detail: { connection, request } });\n}\n\nfunction connectionDisconnectionEvent(connection: ProtocolConnection): ConnectionEvent {\n return new CustomEvent(\"disconnection\", { detail: connection });\n}\n\nfunction connectionErrorEvent(error: Error): ConnectionErrorEvent {\n return new CustomEvent(\"error\", { detail: error });\n}\n\nclass ConnectionClosedError extends Error {\n constructor() {\n super(\"Connection stream is not writable\");\n this.name = \"ConnectionClosedError\";\n }\n}\n\nclass ProtocolConnection extends EventTarget {\n static fromSocket(\n socket: Socket,\n options: {\n onClose?: () => void;\n onError?: (error: Error) => void;\n } = {},\n ): ProtocolConnection {\n const connection = new ProtocolConnection(socket);\n\n socket.on(\"data\", (data: Buffer) => {\n connection.feed(data);\n });\n\n socket.on(\"close\", () => {\n connection.rejectPending(new Error(\"Connection closed\"));\n options.onClose?.();\n });\n\n if (options.onError) {\n socket.on(\"error\", options.onError);\n }\n\n return connection;\n }\n\n private buffer = \"\";\n private pending: Map<string, PendingRequest> = new Map();\n private writer: Duplex;\n\n id: string = crypto.randomUUID();\n\n constructor(writer: Duplex) {\n super();\n this.writer = writer;\n }\n\n private notify<K extends keyof ProtocolConnectionEvents>(type: K, detail: ProtocolConnectionEvents[K]): void {\n this.dispatchEvent(new CustomEvent(type, { detail }));\n }\n\n private rejectWhenClosed(id: string, timeoutHandle: NodeJS.Timeout, reject: (error: Error) => void): void {\n if (this.writer.writable) {\n return;\n }\n\n clearTimeout(timeoutHandle);\n this.pending.delete(id);\n reject(new ConnectionClosedError());\n }\n\n private dispatchParsed(parsed: unknown): boolean {\n const message = parsed as Record<string, unknown>;\n\n if (typeof message.method === \"string\") {\n const requestResult = RequestSchema.safeParse(parsed);\n\n if (!requestResult.success) {\n return false;\n }\n\n this.notify(\"request\", requestResult.data);\n\n return true;\n }\n\n const responseResult = ResponseSchema.safeParse(parsed);\n\n if (!responseResult.success) {\n return false;\n }\n\n this.handleResponse(responseResult.data);\n\n return true;\n }\n\n private processLine(line: string): void {\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(line);\n } catch {\n this.notify(\"invalid\", { id: \"unknown\", error: invalidRequestError(\"Invalid JSON\") });\n\n return;\n }\n\n if (this.dispatchParsed(parsed)) {\n return;\n }\n\n const message = parsed as Record<string, unknown>;\n const id = typeof message.id === \"string\" ? message.id : \"unknown\";\n this.notify(\"invalid\", { id, error: invalidRequestError(\"Invalid message format\") });\n }\n\n private handleResponse(response: Response): void {\n const entry = this.pending.get(response.id);\n\n if (!entry) {\n return;\n }\n\n clearTimeout(entry.timeout);\n this.pending.delete(response.id);\n\n if (response.error) {\n const err = response.error;\n\n entry.reject(\n new ProtocolError(isErrorCode(err.code) ? err.code : ErrorCode.INTERNAL_ERROR, err.message, err.data),\n );\n } else {\n entry.resolve(response.result);\n }\n }\n\n on<K extends keyof ProtocolConnectionEvents>(\n type: K,\n listener: (detail: ProtocolConnectionEvents[K]) => void,\n ): void {\n this.addEventListener(type, (event) => {\n listener((event as CustomEvent<ProtocolConnectionEvents[K]>).detail);\n });\n }\n\n feed(data: Buffer | string): void {\n this.buffer += typeof data === \"string\" ? data : data.toString();\n let newlineIndex = this.buffer.indexOf(\"\\n\");\n\n while (newlineIndex !== -1) {\n const line = this.buffer.slice(0, newlineIndex);\n this.buffer = this.buffer.slice(newlineIndex + 1);\n\n if (line.trim()) {\n this.processLine(line);\n }\n\n newlineIndex = this.buffer.indexOf(\"\\n\");\n }\n }\n\n write(message: Message): void {\n if (!this.writer.writable) {\n return;\n }\n\n this.writer.write(`${JSON.stringify(message)}\\n`);\n }\n\n send<T = unknown>(method: string, params: unknown, timeout: number): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n if (!this.writer.writable) {\n reject(new ConnectionClosedError());\n\n return;\n }\n\n const id = crypto.randomUUID();\n\n const timeoutHandle = setTimeout(() => {\n this.pending.delete(id);\n reject(requestTimeoutError(timeout));\n }, timeout);\n\n this.pending.set(id, {\n resolve: resolve as (result: unknown) => void,\n reject,\n timeout: timeoutHandle,\n });\n\n this.write({ id, method, params });\n this.rejectWhenClosed(id, timeoutHandle, reject);\n });\n }\n\n rejectPending(error: Error): void {\n for (const entry of this.pending.values()) {\n clearTimeout(entry.timeout);\n entry.reject(error);\n }\n\n this.pending.clear();\n }\n}\n\nexport {\n ConnectionClosedError,\n connectionDisconnectionEvent,\n connectionErrorEvent,\n connectionRequestEvent,\n ProtocolConnection,\n type ProtocolConnectionEvents,\n type AppConnections,\n type ConnectionEvent,\n type ConnectionErrorEvent,\n type ConnectionRequestEvent,\n};\n"]}
|