@gtkx/mcp 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"socket-server.js","sourceRoot":"","sources":["../src/socket-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as net from \"node:net\";\nimport { basename, dirname, join, resolve as resolvePath } from \"node:path\";\nimport type { ConnectionRegistry } from \"./connection-registry.js\";\nimport { DEFAULT_SOCKET_PATH } from \"./protocol/schemas.js\";\nimport { connectionErrorEvent } from \"./transport.js\";\n\ntype ProbeOutcome = { kind: \"live\" } | { kind: \"unknown\"; code: string } | { kind: \"vacant\" };\ntype PathVerdict = ProbeOutcome | { kind: \"directory\" };\ntype ClaimOutcome = \"occupied\" | \"published\";\n\nconst PROBE_TIMEOUT_MS = 1000;\nconst PROBE_ATTEMPTS = 3;\nconst PROBE_RETRY_DELAY_MS = 50;\nconst CLAIM_ATTEMPTS = 3;\nconst CLAIM_LOCK_PREFIX = \"\\0gtkx-mcp-claim-\";\nconst CLAIM_LOCK_RETRY_DELAY_MS = 25;\nconst CLAIM_LOCK_TIMEOUT_MS = 15_000;\n\nconst delay = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\nconst closeServer = (server: net.Server): Promise<void> =>\n new Promise((resolve) => {\n server.close(() => {\n resolve();\n });\n });\n\nconst digestFor = (socketPath: string): string =>\n createHash(\"sha256\").update(resolvePath(socketPath)).digest(\"hex\");\n\nconst privatePathFor = (socketPath: string): string => {\n const width = Math.max(1, basename(socketPath).length - 1);\n\n return join(dirname(socketPath), `.${digestFor(socketPath).slice(0, width)}`);\n};\n\nconst bindLock = (address: string): Promise<net.Server | null> =>\n new Promise((resolve) => {\n const lock = net.createServer();\n let isBound = false;\n\n lock.on(\"error\", () => {\n if (!isBound) {\n resolve(null);\n }\n });\n\n lock.listen(address, () => {\n isBound = true;\n resolve(lock);\n });\n });\n\nconst claimBlockedError = (socketPath: string): Error =>\n new Error(\n `Timed out waiting for another GTKX MCP server to finish claiming ${socketPath}. ` +\n \"Retry once no other server is starting on that path.\",\n );\n\nconst acquireClaimLock = async (socketPath: string): Promise<net.Server | null> => {\n const address = `${CLAIM_LOCK_PREFIX}${digestFor(socketPath)}`;\n const deadline = Date.now() + CLAIM_LOCK_TIMEOUT_MS;\n let lock = await bindLock(address);\n\n while (lock === null && Date.now() < deadline) {\n await delay(CLAIM_LOCK_RETRY_DELAY_MS);\n lock = await bindLock(address);\n }\n\n return lock;\n};\n\nconst releaseClaimLock = async (lock: net.Server | null): Promise<void> => {\n if (lock) {\n await closeServer(lock);\n }\n};\n\nconst withClaimLock = async <T>(socketPath: string, action: () => Promise<T> | T): Promise<T> => {\n const lock = await acquireClaimLock(socketPath);\n\n if (lock === null) {\n throw claimBlockedError(socketPath);\n }\n\n try {\n return await action();\n } finally {\n await closeServer(lock);\n }\n};\n\nconst entryFor = (target: string): fs.Stats | null => {\n try {\n return fs.lstatSync(target);\n } catch {\n return null;\n }\n};\n\nconst inodeFor = (target: string): number | null => entryFor(target)?.ino ?? null;\n\nconst outcomeForProbeError = (error: NodeJS.ErrnoException): ProbeOutcome => {\n if (error.code === \"ECONNREFUSED\" || error.code === \"ENOENT\") {\n return { kind: \"vacant\" };\n }\n\n return { kind: \"unknown\", code: error.code ?? error.message };\n};\n\nconst probeSocket = (target: string): Promise<ProbeOutcome> =>\n new Promise((resolve) => {\n const probe = net.connect({ path: target, timeout: PROBE_TIMEOUT_MS });\n\n const settleWith = (outcome: ProbeOutcome): void => {\n probe.destroy();\n resolve(outcome);\n };\n\n probe.once(\"connect\", () => {\n settleWith({ kind: \"live\" });\n });\n\n probe.once(\"timeout\", () => {\n settleWith({ kind: \"unknown\", code: \"ETIMEDOUT\" });\n });\n\n probe.once(\"error\", (error: NodeJS.ErrnoException) => {\n settleWith(outcomeForProbeError(error));\n });\n });\n\nconst probeUntilConclusive = async (target: string): Promise<ProbeOutcome> => {\n let outcome = await probeSocket(target);\n\n for (let attempt = 1; attempt < PROBE_ATTEMPTS && outcome.kind === \"unknown\"; attempt += 1) {\n await delay(PROBE_RETRY_DELAY_MS);\n outcome = await probeSocket(target);\n }\n\n return outcome;\n};\n\nconst alreadyOwnedError = (socketPath: string): Error =>\n 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\nconst undecidedOwnerError = (socketPath: string, code: string): Error =>\n new Error(\n `Could not tell whether another GTKX MCP server owns ${socketPath}: probing it failed with ${code}. ` +\n \"Leaving the socket in place instead of removing one that may still be serving another session. \" +\n \"Retry, or delete the file by hand once no server is running.\",\n );\n\nconst directoryPathError = (socketPath: string): Error =>\n new Error(\n `The GTKX MCP socket path ${socketPath} is a directory, not a socket. ` +\n \"Remove it, or point XDG_RUNTIME_DIR at a directory where GTKX can create its socket.\",\n );\n\nconst listenFailureError = (socketPath: string, code: string): Error =>\n new Error(\n `Could not create the GTKX MCP socket at ${socketPath}: listening failed with ${code}. ` +\n \"Check that its directory exists and is writable.\",\n );\n\nconst removeEntry = (target: string, inode: number): void => {\n if (inodeFor(target) === inode) {\n fs.rmSync(target, { force: true });\n }\n};\n\nconst clearStalePath = async (target: string): Promise<PathVerdict> => {\n const entry = entryFor(target);\n\n if (entry === null) {\n return { kind: \"vacant\" };\n }\n\n if (entry.isDirectory()) {\n return { kind: \"directory\" };\n }\n\n const outcome = await probeUntilConclusive(target);\n\n if (outcome.kind === \"vacant\") {\n removeEntry(target, entry.ino);\n }\n\n return outcome;\n};\n\nconst requireVacantPath = async (socketPath: string): Promise<void> => {\n const verdict = await clearStalePath(socketPath);\n\n if (verdict.kind === \"live\") {\n throw alreadyOwnedError(socketPath);\n }\n\n if (verdict.kind === \"directory\") {\n throw directoryPathError(socketPath);\n }\n\n if (verdict.kind === \"unknown\") {\n throw undecidedOwnerError(socketPath, verdict.code);\n }\n};\n\nconst claimSocketPath = (privatePath: string, socketPath: string): ClaimOutcome => {\n try {\n fs.linkSync(privatePath, socketPath);\n\n return \"published\";\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n return \"occupied\";\n }\n\n throw error;\n }\n};\n\nconst publishSocket = async (privatePath: string, socketPath: string): Promise<number | null> => {\n for (let attempt = 1; attempt <= CLAIM_ATTEMPTS; attempt += 1) {\n await requireVacantPath(socketPath);\n\n if (claimSocketPath(privatePath, socketPath) === \"published\") {\n const inode = inodeFor(socketPath);\n fs.rmSync(privatePath, { force: true });\n\n return inode;\n }\n }\n\n throw alreadyOwnedError(socketPath);\n};\n\nconst releaseSocketPath = async (socketPath: string, inode: number): Promise<void> => {\n const lock = await acquireClaimLock(socketPath);\n\n try {\n removeEntry(socketPath, inode);\n } finally {\n await releaseClaimLock(lock);\n }\n};\n\nclass SocketServer {\n private server: net.Server | null = null;\n private socketPath: string;\n private registry: ConnectionRegistry;\n private boundInode: number | null = null;\n private startup: Promise<void> | null = null;\n\n constructor(registry: ConnectionRegistry, socketPath: string = DEFAULT_SOCKET_PATH) {\n this.registry = registry;\n this.socketPath = socketPath;\n }\n\n private listen(privatePath: string): Promise<net.Server> {\n return new Promise((resolve, reject) => {\n const server = net.createServer((socket) => this.registry.register(socket));\n let isListening = false;\n\n server.on(\"error\", (error) => {\n this.registry.dispatchEvent(connectionErrorEvent(error));\n\n if (!isListening) {\n reject(error);\n }\n });\n\n server.listen(privatePath, () => {\n isListening = true;\n resolve(server);\n });\n });\n }\n\n private async listenPrivately(privatePath: string): Promise<net.Server> {\n try {\n return await this.listen(privatePath);\n } catch (error) {\n const failure = error as NodeJS.ErrnoException;\n throw listenFailureError(this.socketPath, failure.code ?? failure.message);\n }\n }\n\n private async bind(): Promise<void> {\n const privatePath = privatePathFor(this.socketPath);\n await clearStalePath(privatePath);\n const server = await this.listenPrivately(privatePath);\n\n try {\n this.boundInode = await publishSocket(privatePath, this.socketPath);\n this.server = server;\n } catch (error) {\n await closeServer(server);\n throw error;\n }\n }\n\n private open(): Promise<void> {\n return withClaimLock(this.socketPath, () => this.bind());\n }\n\n private async settleStartup(): Promise<void> {\n const startup = this.startup;\n this.startup = null;\n\n if (startup) {\n await Promise.allSettled([startup]);\n }\n }\n\n private async release(): Promise<void> {\n const inode = this.boundInode;\n this.boundInode = null;\n\n if (inode !== null) {\n await releaseSocketPath(this.socketPath, inode);\n }\n }\n\n async start(): Promise<void> {\n this.startup ??= this.open();\n const startup = this.startup;\n\n try {\n await startup;\n } catch (error) {\n if (this.startup === startup) {\n this.startup = null;\n }\n\n throw error;\n }\n }\n\n async stop(): Promise<void> {\n await this.settleStartup();\n const server = this.server;\n\n if (!server) {\n return;\n }\n\n this.server = null;\n this.registry.dispose(\"Server stopping\");\n await closeServer(server);\n await this.release();\n }\n}\n\nexport { SocketServer };\n"]}
1
+ {"version":3,"file":"socket-server.js","sourceRoot":"","sources":["../src/socket-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as net from \"node:net\";\nimport { basename, dirname, join, resolve as resolvePath } from \"node:path\";\nimport type { ConnectionRegistry } from \"./connection-registry.js\";\nimport { DEFAULT_SOCKET_PATH } from \"./protocol/schemas.js\";\nimport { connectionErrorEvent } from \"./transport.js\";\n\ntype ProbeOutcome = { kind: \"live\" } | { kind: \"unknown\"; code: string } | { kind: \"vacant\" };\ntype PathVerdict = ProbeOutcome | { kind: \"directory\" };\ntype ClaimOutcome = \"occupied\" | \"published\";\n\nconst PROBE_TIMEOUT_MS = 1000;\nconst PROBE_ATTEMPTS = 3;\nconst PROBE_RETRY_DELAY_MS = 50;\nconst CLAIM_ATTEMPTS = 3;\nconst CLAIM_LOCK_PREFIX = \"\\0gtkx-mcp-claim-\";\nconst CLAIM_LOCK_RETRY_DELAY_MS = 25;\nconst CLAIM_LOCK_TIMEOUT_MS = 15_000;\n\nconst delay = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\nconst closeServer = (server: net.Server): Promise<void> =>\n new Promise((resolve) => {\n server.close(() => {\n resolve();\n });\n });\n\nconst digestFor = (socketPath: string): string =>\n createHash(\"sha256\").update(resolvePath(socketPath)).digest(\"hex\");\n\nconst privatePathFor = (socketPath: string): string => {\n const width = Math.max(1, basename(socketPath).length - 1);\n\n return join(dirname(socketPath), `.${digestFor(socketPath).slice(0, width)}`);\n};\n\nconst bindLock = (address: string): Promise<net.Server | null> =>\n new Promise((resolve) => {\n const lock = net.createServer();\n let isBound = false;\n\n lock.on(\"error\", () => {\n if (!isBound) {\n resolve(null);\n }\n });\n\n lock.listen(address, () => {\n isBound = true;\n resolve(lock);\n });\n });\n\nconst claimBlockedError = (socketPath: string): Error =>\n new Error(\n `Timed out waiting for another GTKX MCP server to finish claiming ${socketPath}. ` +\n \"Retry once no other server is starting on that path.\",\n );\n\nconst acquireClaimLock = async (socketPath: string): Promise<net.Server | null> => {\n const address = `${CLAIM_LOCK_PREFIX}${digestFor(socketPath)}`;\n const deadline = Date.now() + CLAIM_LOCK_TIMEOUT_MS;\n let lock = await bindLock(address);\n\n while (lock === null && Date.now() < deadline) {\n await delay(CLAIM_LOCK_RETRY_DELAY_MS);\n lock = await bindLock(address);\n }\n\n return lock;\n};\n\nconst releaseClaimLock = async (lock: net.Server | null): Promise<void> => {\n if (lock) {\n await closeServer(lock);\n }\n};\n\nconst withClaimLock = async <T>(socketPath: string, action: () => Promise<T> | T): Promise<T> => {\n const lock = await acquireClaimLock(socketPath);\n\n if (lock === null) {\n throw claimBlockedError(socketPath);\n }\n\n try {\n return await action();\n } finally {\n await closeServer(lock);\n }\n};\n\nconst entryFor = (target: string): fs.Stats | null => {\n try {\n return fs.lstatSync(target);\n } catch {\n return null;\n }\n};\n\nconst inodeFor = (target: string): number | null => entryFor(target)?.ino ?? null;\n\nconst outcomeForProbeError = (error: NodeJS.ErrnoException): ProbeOutcome => {\n if (error.code === \"ECONNREFUSED\" || error.code === \"ENOENT\") {\n return { kind: \"vacant\" };\n }\n\n return { kind: \"unknown\", code: error.code ?? error.message };\n};\n\nconst probeSocket = (target: string): Promise<ProbeOutcome> =>\n new Promise((resolve) => {\n const probe = net.connect({ path: target, timeout: PROBE_TIMEOUT_MS });\n\n const settleWith = (outcome: ProbeOutcome): void => {\n probe.destroy();\n resolve(outcome);\n };\n\n probe.once(\"connect\", () => {\n settleWith({ kind: \"live\" });\n });\n\n probe.once(\"timeout\", () => {\n settleWith({ kind: \"unknown\", code: \"ETIMEDOUT\" });\n });\n\n probe.once(\"error\", (error: NodeJS.ErrnoException) => {\n settleWith(outcomeForProbeError(error));\n });\n });\n\nconst probeUntilConclusive = async (target: string): Promise<ProbeOutcome> => {\n let outcome = await probeSocket(target);\n\n for (let attempt = 1; attempt < PROBE_ATTEMPTS && outcome.kind === \"unknown\"; attempt += 1) {\n await delay(PROBE_RETRY_DELAY_MS);\n outcome = await probeSocket(target);\n }\n\n return outcome;\n};\n\nconst alreadyOwnedError = (socketPath: string): Error =>\n 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\nconst undecidedOwnerError = (socketPath: string, code: string): Error =>\n new Error(\n `Could not tell whether another GTKX MCP server owns ${socketPath}: probing it failed with ${code}. ` +\n \"Leaving the socket in place instead of removing one that may still be serving another session. \" +\n \"Retry, or delete the file by hand once no server is running.\",\n );\n\nconst directoryPathError = (socketPath: string): Error =>\n new Error(\n `The GTKX MCP socket path ${socketPath} is a directory, not a socket. ` +\n \"Remove it, or point XDG_RUNTIME_DIR at a directory where GTKX can create its socket.\",\n );\n\nconst listenFailureError = (socketPath: string, code: string): Error =>\n new Error(\n `Could not create the GTKX MCP socket at ${socketPath}: listening failed with ${code}. ` +\n \"Check that its directory exists and is writable.\",\n );\n\nconst removeEntry = (target: string, inode: number): void => {\n if (inodeFor(target) === inode) {\n fs.rmSync(target, { force: true });\n }\n};\n\nconst clearStalePath = async (target: string): Promise<PathVerdict> => {\n const entry = entryFor(target);\n\n if (entry === null) {\n return { kind: \"vacant\" };\n }\n\n if (entry.isDirectory()) {\n return { kind: \"directory\" };\n }\n\n const outcome = await probeUntilConclusive(target);\n\n if (outcome.kind === \"vacant\") {\n removeEntry(target, entry.ino);\n }\n\n return outcome;\n};\n\nconst requireVacantPath = async (socketPath: string): Promise<void> => {\n const verdict = await clearStalePath(socketPath);\n\n if (verdict.kind === \"live\") {\n throw alreadyOwnedError(socketPath);\n }\n\n if (verdict.kind === \"directory\") {\n throw directoryPathError(socketPath);\n }\n\n if (verdict.kind === \"unknown\") {\n throw undecidedOwnerError(socketPath, verdict.code);\n }\n};\n\nconst claimSocketPath = (privatePath: string, socketPath: string): ClaimOutcome => {\n try {\n fs.linkSync(privatePath, socketPath);\n\n return \"published\";\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n return \"occupied\";\n }\n\n throw error;\n }\n};\n\nconst publishSocket = async (privatePath: string, socketPath: string): Promise<number | null> => {\n for (let attempt = 1; attempt <= CLAIM_ATTEMPTS; attempt += 1) {\n await requireVacantPath(socketPath);\n\n if (claimSocketPath(privatePath, socketPath) === \"published\") {\n const inode = inodeFor(socketPath);\n fs.rmSync(privatePath, { force: true });\n\n return inode;\n }\n }\n\n throw alreadyOwnedError(socketPath);\n};\n\nconst releaseSocketPath = async (socketPath: string, inode: number): Promise<void> => {\n const lock = await acquireClaimLock(socketPath);\n\n try {\n removeEntry(socketPath, inode);\n } finally {\n await releaseClaimLock(lock);\n }\n};\n\nclass SocketServer {\n private server: net.Server | null = null;\n private socketPath: string;\n private registry: ConnectionRegistry;\n private boundInode: number | null = null;\n private startup: Promise<void> | null = null;\n\n constructor(registry: ConnectionRegistry, socketPath: string = DEFAULT_SOCKET_PATH) {\n this.registry = registry;\n this.socketPath = socketPath;\n }\n\n private listen(privatePath: string): Promise<net.Server> {\n return new Promise((resolve, reject) => {\n const server = net.createServer((socket) => this.registry.register(socket));\n let isListening = false;\n\n server.on(\"error\", (error) => {\n this.registry.dispatchEvent(connectionErrorEvent(error));\n\n if (!isListening) {\n reject(error);\n }\n });\n\n server.listen(privatePath, () => {\n isListening = true;\n resolve(server);\n });\n });\n }\n\n private async listenPrivately(privatePath: string): Promise<net.Server> {\n try {\n return await this.listen(privatePath);\n } catch (error) {\n const failure = error as NodeJS.ErrnoException;\n throw listenFailureError(this.socketPath, failure.code ?? failure.message);\n }\n }\n\n private async bind(): Promise<void> {\n const privatePath = privatePathFor(this.socketPath);\n await clearStalePath(privatePath);\n const server = await this.listenPrivately(privatePath);\n\n try {\n this.boundInode = await publishSocket(privatePath, this.socketPath);\n this.server = server;\n } catch (error) {\n await closeServer(server);\n throw error;\n }\n }\n\n private open(): Promise<void> {\n return withClaimLock(this.socketPath, () => this.bind());\n }\n\n private async settleStartup(): Promise<void> {\n const startup = this.startup;\n this.startup = null;\n\n if (startup) {\n await Promise.allSettled([startup]);\n }\n }\n\n private async release(): Promise<void> {\n const inode = this.boundInode;\n this.boundInode = null;\n\n if (inode !== null) {\n await releaseSocketPath(this.socketPath, inode);\n }\n }\n\n async start(): Promise<void> {\n this.startup ??= this.open();\n const startup = this.startup;\n\n try {\n await startup;\n } catch (error) {\n if (this.startup === startup) {\n this.startup = null;\n }\n\n throw error;\n }\n }\n\n async stop(): Promise<void> {\n await this.settleStartup();\n const server = this.server;\n\n if (!server) {\n return;\n }\n\n this.server = null;\n this.registry.dispose();\n await closeServer(server);\n await this.release();\n }\n}\n\nexport { SocketServer };\n"]}
@@ -1,49 +1,28 @@
1
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
- };
2
+ import { Protocol } from "@modelcontextprotocol/sdk/shared/protocol.js";
3
+ import { type JSONRPCRequest, type Notification, type Request, type Result } from "@modelcontextprotocol/sdk/types.js";
12
4
  type ConnectionEvent = CustomEvent<ProtocolConnection>;
13
- type ConnectionRequestEvent = CustomEvent<{
14
- connection: ProtocolConnection;
15
- request: Request;
16
- }>;
17
5
  type ConnectionErrorEvent = CustomEvent<Error>;
6
+ type ConnectionRequestHandler = (connection: ProtocolConnection, request: JSONRPCRequest) => Promise<Result>;
7
+ type RequestParams = Request["params"];
8
+ type ConnectionOptions = {
9
+ onClose: () => void;
10
+ onError: (error: Error) => void;
11
+ };
18
12
  type AppConnections = {
19
- send(connectionId: string, message: Message): void;
13
+ onRequest: ConnectionRequestHandler;
20
14
  } & EventTarget;
21
- declare function connectionRequestEvent(connection: ProtocolConnection, request: Request): ConnectionRequestEvent;
22
15
  declare function connectionDisconnectionEvent(connection: ProtocolConnection): ConnectionEvent;
23
16
  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;
17
+ declare class ProtocolConnection extends Protocol<Request, Notification, Result> {
18
+ static fromSocket(socket: Socket, options: ConnectionOptions): ProtocolConnection;
35
19
  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;
20
+ protected assertCapabilityForMethod(): void;
21
+ protected assertNotificationCapability(): void;
22
+ protected assertRequestHandlerCapability(): void;
23
+ protected assertTaskCapability(): void;
24
+ protected assertTaskHandlerCapability(): void;
25
+ send<T>(method: string, params?: RequestParams, timeout?: number): Promise<T>;
47
26
  }
48
- export { ConnectionClosedError, connectionDisconnectionEvent, connectionErrorEvent, connectionRequestEvent, ProtocolConnection, type AppConnections, type ConnectionEvent, type ConnectionErrorEvent, type ConnectionRequestEvent, };
27
+ export { connectionDisconnectionEvent, connectionErrorEvent, ProtocolConnection, type AppConnections, type ConnectionEvent, type ConnectionErrorEvent, type ConnectionRequestHandler, type RequestParams, };
49
28
  //# sourceMappingURL=transport.d.ts.map
@@ -1 +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,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,GAC9B,CAAC"}
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,OAAO,EAAE,QAAQ,EAAE,MAAM,8CAA8C,CAAC;AAExE,OAAO,EAGH,KAAK,cAAc,EAEnB,KAAK,YAAY,EACjB,KAAK,OAAO,EACZ,KAAK,MAAM,EAEd,MAAM,oCAAoC,CAAC;AAE5C,KAAK,eAAe,GAAG,WAAW,CAAC,kBAAkB,CAAC,CAAC;AACvD,KAAK,oBAAoB,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;AAC/C,KAAK,wBAAwB,GAAG,CAAC,UAAU,EAAE,kBAAkB,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAC7G,KAAK,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvC,KAAK,iBAAiB,GAAG;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF,KAAK,cAAc,GAAG;IAClB,SAAS,EAAE,wBAAwB,CAAC;CACvC,GAAG,WAAW,CAAC;AAKhB,iBAAS,4BAA4B,CAAC,UAAU,EAAE,kBAAkB,GAAG,eAAe,CAErF;AAED,iBAAS,oBAAoB,CAAC,KAAK,EAAE,KAAK,GAAG,oBAAoB,CAEhE;AA2FD,cAAM,kBAAmB,SAAQ,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC;IACpE,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,kBAAkB;IAWjF,EAAE,EAAE,MAAM,CAAuB;IAEjC,SAAS,CAAC,yBAAyB,IAAI,IAAI;IAE3C,SAAS,CAAC,4BAA4B,IAAI,IAAI;IAE9C,SAAS,CAAC,8BAA8B,IAAI,IAAI;IAEhD,SAAS,CAAC,oBAAoB,IAAI,IAAI;IAEtC,SAAS,CAAC,2BAA2B,IAAI,IAAI;IAEvC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CAStF;AAED,OAAO,EACH,4BAA4B,EAC5B,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,aAAa,GACrB,CAAC"}
package/dist/transport.js CHANGED
@@ -1,152 +1,103 @@
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
- }
1
+ import { normalizeError } from "@gtkx/utils";
2
+ import { Protocol } from "@modelcontextprotocol/sdk/shared/protocol.js";
3
+ import { ReadBuffer, serializeMessage } from "@modelcontextprotocol/sdk/shared/stdio.js";
4
+ import { ErrorCode, McpError, ResultSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ const MAX_MESSAGE_BYTES = 64 * 1024 * 1024;
6
+ const MAX_PENDING_WRITE_BYTES = 8 * 1024 * 1024;
6
7
  function connectionDisconnectionEvent(connection) {
7
8
  return new CustomEvent("disconnection", { detail: connection });
8
9
  }
9
10
  function connectionErrorEvent(error) {
10
11
  return new CustomEvent("error", { detail: error });
11
12
  }
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 }));
13
+ class SocketTransport {
14
+ readBuffer = new ReadBuffer({ maxBufferSize: MAX_MESSAGE_BYTES });
15
+ socket;
16
+ onclose;
17
+ onerror;
18
+ onmessage;
19
+ constructor(socket) {
20
+ this.socket = socket;
43
21
  }
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;
22
+ read() {
23
+ for (;;) {
24
+ let message;
25
+ try {
26
+ message = this.readBuffer.readMessage();
58
27
  }
59
- this.notify("request", requestResult.data);
60
- return true;
61
- }
62
- const responseResult = ResponseSchema.safeParse(parsed);
63
- if (!responseResult.success) {
64
- return false;
28
+ catch (error) {
29
+ this.onerror?.(normalizeError(error));
30
+ continue;
31
+ }
32
+ if (message === null) {
33
+ return;
34
+ }
35
+ this.onmessage?.(message);
65
36
  }
66
- this.handleResponse(responseResult.data);
67
- return true;
68
37
  }
69
- processLine(line) {
70
- let parsed;
38
+ drop(reason) {
39
+ const error = new McpError(ErrorCode.ConnectionClosed, reason);
40
+ this.onerror?.(error);
41
+ this.socket.destroy();
42
+ return Promise.reject(error);
43
+ }
44
+ receive(chunk) {
71
45
  try {
72
- parsed = JSON.parse(line);
46
+ this.readBuffer.append(chunk);
73
47
  }
74
- catch {
75
- this.notify("invalid", { id: "unknown", error: invalidRequestError("Invalid JSON") });
48
+ catch (error) {
49
+ this.onerror?.(normalizeError(error));
50
+ this.socket.destroy();
76
51
  return;
77
52
  }
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") });
53
+ this.read();
84
54
  }
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);
55
+ start() {
56
+ this.socket.on("data", (chunk) => {
57
+ this.receive(chunk);
58
+ });
59
+ this.socket.on("close", () => {
60
+ this.onclose?.();
103
61
  });
62
+ this.socket.on("error", (error) => {
63
+ this.onerror?.(error);
64
+ });
65
+ return Promise.resolve();
104
66
  }
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");
67
+ send(message) {
68
+ if (!this.socket.writable) {
69
+ return Promise.reject(new McpError(ErrorCode.ConnectionClosed, "Connection stream is not writable"));
115
70
  }
116
- }
117
- write(message) {
118
- if (!this.writer.writable) {
119
- return;
71
+ if (this.socket.writableLength > MAX_PENDING_WRITE_BYTES) {
72
+ return this.drop("Connection stream is not draining");
120
73
  }
121
- this.writer.write(`${JSON.stringify(message)}\n`);
74
+ this.socket.write(serializeMessage(message));
75
+ return Promise.resolve();
122
76
  }
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);
77
+ close() {
78
+ this.socket.destroy();
79
+ return Promise.resolve();
80
+ }
81
+ }
82
+ class ProtocolConnection extends Protocol {
83
+ static fromSocket(socket, options) {
84
+ const connection = Object.assign(new ProtocolConnection(), {
85
+ onclose: options.onClose,
86
+ onerror: options.onError,
141
87
  });
88
+ void connection.connect(new SocketTransport(socket));
89
+ return connection;
142
90
  }
143
- rejectPending(error) {
144
- for (const entry of this.pending.values()) {
145
- clearTimeout(entry.timeout);
146
- entry.reject(error);
147
- }
148
- this.pending.clear();
91
+ id = crypto.randomUUID();
92
+ assertCapabilityForMethod() { }
93
+ assertNotificationCapability() { }
94
+ assertRequestHandlerCapability() { }
95
+ assertTaskCapability() { }
96
+ assertTaskHandlerCapability() { }
97
+ async send(method, params, timeout) {
98
+ const result = await this.request({ method, ...(params !== undefined && { params }) }, ResultSchema, { ...(timeout !== undefined && { timeout }) });
99
+ return result;
149
100
  }
150
101
  }
151
- export { ConnectionClosedError, connectionDisconnectionEvent, connectionErrorEvent, connectionRequestEvent, ProtocolConnection, };
102
+ export { connectionDisconnectionEvent, connectionErrorEvent, ProtocolConnection, };
152
103
  //# sourceMappingURL=transport.js.map
@@ -1 +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 AppConnections,\n type ConnectionEvent,\n type ConnectionErrorEvent,\n type ConnectionRequestEvent,\n};\n"]}
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,8CAA8C,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AACzF,OAAO,EACH,SAAS,EAGT,QAAQ,EAIR,YAAY,GACf,MAAM,oCAAoC,CAAC;AAgB5C,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAC3C,MAAM,uBAAuB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhD,SAAS,4BAA4B,CAAC,UAA8B;IAChE,OAAO,IAAI,WAAW,CAAC,eAAe,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;AACpE,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAY;IACtC,OAAO,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,eAAe;IACA,UAAU,GAAe,IAAI,UAAU,CAAC,EAAE,aAAa,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC9E,MAAM,CAAS;IAEhC,OAAO,CAAc;IACrB,OAAO,CAA0B;IACjC,SAAS,CAAqC;IAE9C,YAAY,MAAc;QACtB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;IAEO,IAAI;QACR,SAAS,CAAC;YACN,IAAI,OAA8B,CAAC;YAEnC,IAAI,CAAC;gBACD,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;YAC5C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,IAAI,CAAC,OAAO,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;gBACtC,SAAS;YACb,CAAC;YAED,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACnB,OAAO;YACX,CAAC;YAED,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;IACL,CAAC;IAEO,IAAI,CAAC,MAAc;QACvB,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,SAAS,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;QAC/D,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAEtB,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IAEO,OAAO,CAAC,KAAa;QACzB,IAAI,CAAC;YACD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,OAAO,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAEtB,OAAO;QACX,CAAC;QAED,IAAI,CAAC,IAAI,EAAE,CAAC;IAChB,CAAC;IAED,KAAK;QACD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACrC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACzB,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC9B,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;IAED,IAAI,CAAC,OAAuB;QACxB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACxB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,SAAS,CAAC,gBAAgB,EAAE,mCAAmC,CAAC,CAAC,CAAC;QACzG,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,CAAC,cAAc,GAAG,uBAAuB,EAAE,CAAC;YACvD,OAAO,IAAI,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;QAC1D,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;QAE7C,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;IAED,KAAK;QACD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAEtB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;CACJ;AAED,MAAM,kBAAmB,SAAQ,QAAuC;IACpE,MAAM,CAAC,UAAU,CAAC,MAAc,EAAE,OAA0B;QACxD,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,kBAAkB,EAAE,EAAE;YACvD,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,OAAO,EAAE,OAAO,CAAC,OAAO;SAC3B,CAAC,CAAC;QAEH,KAAK,UAAU,CAAC,OAAO,CAAC,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;QAErD,OAAO,UAAU,CAAC;IACtB,CAAC;IAED,EAAE,GAAW,MAAM,CAAC,UAAU,EAAE,CAAC;IAEvB,yBAAyB,KAAU,CAAC;IAEpC,4BAA4B,KAAU,CAAC;IAEvC,8BAA8B,KAAU,CAAC;IAEzC,oBAAoB,KAAU,CAAC;IAE/B,2BAA2B,KAAU,CAAC;IAEhD,KAAK,CAAC,IAAI,CAAI,MAAc,EAAE,MAAsB,EAAE,OAAgB;QAClE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAC7B,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,EACnD,YAAY,EACZ,EAAE,GAAG,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAChD,CAAC;QAEF,OAAO,MAAW,CAAC;IACvB,CAAC;CACJ;AAED,OAAO,EACH,4BAA4B,EAC5B,oBAAoB,EACpB,kBAAkB,GAMrB,CAAC","sourcesContent":["/* eslint-disable @typescript-eslint/no-empty-function -- the app link negotiates no MCP capabilities */\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport type { Socket } from \"node:net\";\nimport { normalizeError } from \"@gtkx/utils\";\nimport { Protocol } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport { ReadBuffer, serializeMessage } from \"@modelcontextprotocol/sdk/shared/stdio.js\";\nimport {\n ErrorCode,\n type JSONRPCMessage,\n type JSONRPCRequest,\n McpError,\n type Notification,\n type Request,\n type Result,\n ResultSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\ntype ConnectionEvent = CustomEvent<ProtocolConnection>;\ntype ConnectionErrorEvent = CustomEvent<Error>;\ntype ConnectionRequestHandler = (connection: ProtocolConnection, request: JSONRPCRequest) => Promise<Result>;\ntype RequestParams = Request[\"params\"];\n\ntype ConnectionOptions = {\n onClose: () => void;\n onError: (error: Error) => void;\n};\n\ntype AppConnections = {\n onRequest: ConnectionRequestHandler;\n} & EventTarget;\n\nconst MAX_MESSAGE_BYTES = 64 * 1024 * 1024;\nconst MAX_PENDING_WRITE_BYTES = 8 * 1024 * 1024;\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 SocketTransport implements Transport {\n private readonly readBuffer: ReadBuffer = new ReadBuffer({ maxBufferSize: MAX_MESSAGE_BYTES });\n private readonly socket: Socket;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n constructor(socket: Socket) {\n this.socket = socket;\n }\n\n private read(): void {\n for (;;) {\n let message: JSONRPCMessage | null;\n\n try {\n message = this.readBuffer.readMessage();\n } catch (error) {\n this.onerror?.(normalizeError(error));\n continue;\n }\n\n if (message === null) {\n return;\n }\n\n this.onmessage?.(message);\n }\n }\n\n private drop(reason: string): Promise<void> {\n const error = new McpError(ErrorCode.ConnectionClosed, reason);\n this.onerror?.(error);\n this.socket.destroy();\n\n return Promise.reject(error);\n }\n\n private receive(chunk: Buffer): void {\n try {\n this.readBuffer.append(chunk);\n } catch (error) {\n this.onerror?.(normalizeError(error));\n this.socket.destroy();\n\n return;\n }\n\n this.read();\n }\n\n start(): Promise<void> {\n this.socket.on(\"data\", (chunk: Buffer) => {\n this.receive(chunk);\n });\n\n this.socket.on(\"close\", () => {\n this.onclose?.();\n });\n\n this.socket.on(\"error\", (error) => {\n this.onerror?.(error);\n });\n\n return Promise.resolve();\n }\n\n send(message: JSONRPCMessage): Promise<void> {\n if (!this.socket.writable) {\n return Promise.reject(new McpError(ErrorCode.ConnectionClosed, \"Connection stream is not writable\"));\n }\n\n if (this.socket.writableLength > MAX_PENDING_WRITE_BYTES) {\n return this.drop(\"Connection stream is not draining\");\n }\n\n this.socket.write(serializeMessage(message));\n\n return Promise.resolve();\n }\n\n close(): Promise<void> {\n this.socket.destroy();\n\n return Promise.resolve();\n }\n}\n\nclass ProtocolConnection extends Protocol<Request, Notification, Result> {\n static fromSocket(socket: Socket, options: ConnectionOptions): ProtocolConnection {\n const connection = Object.assign(new ProtocolConnection(), {\n onclose: options.onClose,\n onerror: options.onError,\n });\n\n void connection.connect(new SocketTransport(socket));\n\n return connection;\n }\n\n id: string = crypto.randomUUID();\n\n protected assertCapabilityForMethod(): void {}\n\n protected assertNotificationCapability(): void {}\n\n protected assertRequestHandlerCapability(): void {}\n\n protected assertTaskCapability(): void {}\n\n protected assertTaskHandlerCapability(): void {}\n\n async send<T>(method: string, params?: RequestParams, timeout?: number): Promise<T> {\n const result = await this.request(\n { method, ...(params !== undefined && { params }) },\n ResultSchema,\n { ...(timeout !== undefined && { timeout }) },\n );\n\n return result as T;\n }\n}\n\nexport {\n connectionDisconnectionEvent,\n connectionErrorEvent,\n ProtocolConnection,\n type AppConnections,\n type ConnectionEvent,\n type ConnectionErrorEvent,\n type ConnectionRequestHandler,\n type RequestParams,\n};\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gtkx/mcp",
3
- "version": "1.3.0",
4
- "description": "Model Context Protocol server that gives an agent the GTKX API reference and control of a running application",
3
+ "version": "1.5.0",
4
+ "description": "MCP server exposing the GTK API reference and a running GTK4 app.",
5
5
  "keywords": [
6
6
  "gtkx",
7
7
  "gtk",
@@ -57,13 +57,13 @@
57
57
  "dependencies": {
58
58
  "@modelcontextprotocol/sdk": "^1.30.0",
59
59
  "zod": "^4.4.3",
60
- "@gtkx/codegen": "1.3.0",
61
- "@gtkx/config": "1.3.0",
62
- "@gtkx/utils": "1.3.0"
60
+ "@gtkx/config": "1.5.0",
61
+ "@gtkx/codegen": "1.5.0",
62
+ "@gtkx/utils": "1.5.0"
63
63
  },
64
64
  "devDependencies": {
65
65
  "vitest": "^4.1.11",
66
- "@gtkx/vitest": "1.3.0"
66
+ "@gtkx/vitest": "1.5.0"
67
67
  },
68
68
  "scripts": {
69
69
  "release": "tsx ../../scripts/release-package.ts"
package/src/app-router.ts CHANGED
@@ -1,19 +1,18 @@
1
+ import { type JSONRPCRequest, McpError, type Result } from "@modelcontextprotocol/sdk/types.js";
2
+ import type { AppConnections, ConnectionEvent, ProtocolConnection, RequestParams } from "./transport.js";
1
3
  import {
2
4
  appNotFoundError,
5
+ CONNECTION_CLOSED_CODE,
3
6
  connectionWriteFailedError,
4
7
  invalidRequestError,
5
8
  methodNotFoundError,
6
9
  noAppConnectedError,
7
10
  type ProtocolError,
11
+ protocolErrorFrom,
12
+ REQUEST_TIMEOUT_CODE,
13
+ requestTimeoutError,
8
14
  } from "./protocol/errors.js";
9
- import { type AppInfo, RegisterParamsSchema, type Request, type Response } from "./protocol/schemas.js";
10
- import {
11
- type AppConnections,
12
- ConnectionClosedError,
13
- type ConnectionEvent,
14
- type ConnectionRequestEvent,
15
- type ProtocolConnection,
16
- } from "./transport.js";
15
+ import { type AppInfo, RegisterParamsSchema } from "./protocol/schemas.js";
17
16
 
18
17
  type AppRegisteredEvent = CustomEvent<AppInfo>;
19
18
  type AppUnregisteredEvent = CustomEvent<string>;
@@ -48,11 +47,7 @@ class AppRouter extends EventTarget {
48
47
  super();
49
48
  this.connections = connections;
50
49
  this.requestTimeout = options.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT_MS;
51
-
52
- this.connections.addEventListener("request", (event) => {
53
- const { connection, request } = (event as ConnectionRequestEvent).detail;
54
- this.handleRequest(connection, request);
55
- });
50
+ this.connections.onRequest = (connection, request) => this.handleRequest(connection, request);
56
51
 
57
52
  this.connections.addEventListener("disconnection", (event) => {
58
53
  this.removeApp((event as ConnectionEvent).detail);
@@ -73,23 +68,25 @@ class AppRouter extends EventTarget {
73
68
  throw noAppConnectedError();
74
69
  }
75
70
 
76
- private handleRequest(connection: ProtocolConnection, request: Request): void {
71
+ private handleRequest(connection: ProtocolConnection, request: JSONRPCRequest): Promise<Result> {
77
72
  if (request.method === "app.register") {
78
- this.handleRegister(connection, request);
79
- } else if (request.method === "app.unregister") {
80
- this.handleUnregister(connection, request);
81
- } else {
82
- this.sendError(connection, request, methodNotFoundError(request.method));
73
+ return Promise.resolve(this.handleRegister(connection, request));
74
+ }
75
+
76
+ if (request.method === "app.unregister") {
77
+ this.removeApp(connection);
78
+
79
+ return Promise.resolve({ success: true });
83
80
  }
81
+
82
+ return Promise.reject(methodNotFoundError(request.method));
84
83
  }
85
84
 
86
- private handleRegister(connection: ProtocolConnection, request: Request): void {
85
+ private handleRegister(connection: ProtocolConnection, request: JSONRPCRequest): Result {
87
86
  const parseResult = RegisterParamsSchema.safeParse(request.params);
88
87
 
89
88
  if (!parseResult.success) {
90
- this.sendError(connection, request, invalidRequestError(parseResult.error.message));
91
-
92
- return;
89
+ throw invalidRequestError(parseResult.error.message);
93
90
  }
94
91
 
95
92
  const params = parseResult.data;
@@ -102,29 +99,23 @@ class AppRouter extends EventTarget {
102
99
 
103
100
  this.apps.set(params.applicationId, { info: appInfo, connection });
104
101
  this.connectionToApp.set(connection.id, params.applicationId);
105
- this.acknowledge(connection, request);
106
102
  this.dispatchEvent(appRegisteredEvent(appInfo));
107
- }
108
103
 
109
- private handleUnregister(connection: ProtocolConnection, request: Request): void {
110
- this.removeApp(connection);
111
- this.acknowledge(connection, request);
104
+ return { success: true };
112
105
  }
113
106
 
114
- private acknowledge(connection: ProtocolConnection, request: Request): void {
115
- const response: Response = {
116
- id: request.id,
117
- result: { success: true },
118
- };
107
+ private toAppError(app: RegisteredApp, error: McpError): ProtocolError {
108
+ if (error.code === CONNECTION_CLOSED_CODE) {
109
+ this.removeApp(app.connection);
119
110
 
120
- this.connections.send(connection.id, response);
121
- }
111
+ return connectionWriteFailedError(app.info.applicationId);
112
+ }
122
113
 
123
- private sendError(connection: ProtocolConnection, request: Request, error: ProtocolError): void {
124
- this.connections.send(connection.id, {
125
- id: request.id,
126
- error: error.toErrorObject(),
127
- });
114
+ if (error.code === REQUEST_TIMEOUT_CODE) {
115
+ return requestTimeoutError(this.requestTimeout);
116
+ }
117
+
118
+ return protocolErrorFrom(error);
128
119
  }
129
120
 
130
121
  private removeApp(connection: ProtocolConnection): void {
@@ -190,15 +181,14 @@ class AppRouter extends EventTarget {
190
181
  });
191
182
  }
192
183
 
193
- async sendToApp<T>(applicationId: string | undefined, method: string, params?: unknown): Promise<T> {
184
+ async sendToApp<T>(applicationId: string | undefined, method: string, params?: RequestParams): Promise<T> {
194
185
  const app = this.resolveTargetApp(applicationId);
195
186
 
196
187
  try {
197
188
  return await app.connection.send<T>(method, params, this.requestTimeout);
198
189
  } catch (error) {
199
- if (error instanceof ConnectionClosedError) {
200
- this.removeApp(app.connection);
201
- throw connectionWriteFailedError(app.info.applicationId);
190
+ if (error instanceof McpError) {
191
+ throw this.toAppError(app, error);
202
192
  }
203
193
 
204
194
  throw error;