@gtkx/mcp 1.0.0-rc.3 → 1.0.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,70 +1,271 @@
1
+ import { createHash } from "node:crypto";
1
2
  import * as fs from "node:fs";
2
3
  import * as net from "node:net";
4
+ import { basename, dirname, join, resolve as resolvePath } from "node:path";
3
5
  import { DEFAULT_SOCKET_PATH } from "./protocol/schemas.js";
4
6
  import { connectionErrorEvent } from "./transport.js";
5
- const socketIsLive = (socketPath) => new Promise((resolve) => {
6
- const probe = net.connect(socketPath);
7
- probe.once("connect", () => {
7
+ const PROBE_TIMEOUT_MS = 1000;
8
+ const PROBE_ATTEMPTS = 3;
9
+ const PROBE_RETRY_DELAY_MS = 50;
10
+ const CLAIM_ATTEMPTS = 3;
11
+ const CLAIM_LOCK_PREFIX = "\0gtkx-mcp-claim-";
12
+ const CLAIM_LOCK_RETRY_DELAY_MS = 25;
13
+ const CLAIM_LOCK_TIMEOUT_MS = 15_000;
14
+ const delay = (ms) => new Promise((resolve) => {
15
+ setTimeout(resolve, ms);
16
+ });
17
+ const closeServer = (server) => new Promise((resolve) => {
18
+ server.close(() => {
19
+ resolve();
20
+ });
21
+ });
22
+ const digestFor = (socketPath) => createHash("sha256").update(resolvePath(socketPath)).digest("hex");
23
+ const privatePathFor = (socketPath) => {
24
+ const width = Math.max(1, basename(socketPath).length - 1);
25
+ return join(dirname(socketPath), `.${digestFor(socketPath).slice(0, width)}`);
26
+ };
27
+ const bindLock = (address) => new Promise((resolve) => {
28
+ const lock = net.createServer();
29
+ let isBound = false;
30
+ lock.on("error", () => {
31
+ if (!isBound) {
32
+ resolve(null);
33
+ }
34
+ });
35
+ lock.listen(address, () => {
36
+ isBound = true;
37
+ resolve(lock);
38
+ });
39
+ });
40
+ const claimBlockedError = (socketPath) => new Error(`Timed out waiting for another GTKX MCP server to finish claiming ${socketPath}. ` +
41
+ "Retry once no other server is starting on that path.");
42
+ const acquireClaimLock = async (socketPath) => {
43
+ const address = `${CLAIM_LOCK_PREFIX}${digestFor(socketPath)}`;
44
+ const deadline = Date.now() + CLAIM_LOCK_TIMEOUT_MS;
45
+ let lock = await bindLock(address);
46
+ while (lock === null && Date.now() < deadline) {
47
+ await delay(CLAIM_LOCK_RETRY_DELAY_MS);
48
+ lock = await bindLock(address);
49
+ }
50
+ return lock;
51
+ };
52
+ const releaseClaimLock = async (lock) => {
53
+ if (lock) {
54
+ await closeServer(lock);
55
+ }
56
+ };
57
+ const withClaimLock = async (socketPath, action) => {
58
+ const lock = await acquireClaimLock(socketPath);
59
+ if (lock === null) {
60
+ throw claimBlockedError(socketPath);
61
+ }
62
+ try {
63
+ return await action();
64
+ }
65
+ finally {
66
+ await closeServer(lock);
67
+ }
68
+ };
69
+ const entryFor = (target) => {
70
+ try {
71
+ return fs.lstatSync(target);
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ };
77
+ const inodeFor = (target) => entryFor(target)?.ino ?? null;
78
+ const outcomeForProbeError = (error) => {
79
+ if (error.code === "ECONNREFUSED" || error.code === "ENOENT") {
80
+ return { kind: "vacant" };
81
+ }
82
+ return { kind: "unknown", code: error.code ?? error.message };
83
+ };
84
+ const probeSocket = (target) => new Promise((resolve) => {
85
+ const probe = net.connect({ path: target, timeout: PROBE_TIMEOUT_MS });
86
+ const settleWith = (outcome) => {
8
87
  probe.destroy();
9
- resolve(true);
88
+ resolve(outcome);
89
+ };
90
+ probe.once("connect", () => {
91
+ settleWith({ kind: "live" });
92
+ });
93
+ probe.once("timeout", () => {
94
+ settleWith({ kind: "unknown", code: "ETIMEDOUT" });
10
95
  });
11
- probe.once("error", () => {
12
- resolve(false);
96
+ probe.once("error", (error) => {
97
+ settleWith(outcomeForProbeError(error));
13
98
  });
14
99
  });
15
- const removeStaleSocket = async (socketPath) => {
16
- if (!fs.existsSync(socketPath)) {
17
- return;
100
+ const probeUntilConclusive = async (target) => {
101
+ let outcome = await probeSocket(target);
102
+ for (let attempt = 1; attempt < PROBE_ATTEMPTS && outcome.kind === "unknown"; attempt += 1) {
103
+ await delay(PROBE_RETRY_DELAY_MS);
104
+ outcome = await probeSocket(target);
105
+ }
106
+ return outcome;
107
+ };
108
+ const alreadyOwnedError = (socketPath) => new Error(`Another GTKX MCP server already owns ${socketPath}. ` +
109
+ "Stop the other server (for example, the GTKX MCP server of another active session) and reconnect.");
110
+ const undecidedOwnerError = (socketPath, code) => new Error(`Could not tell whether another GTKX MCP server owns ${socketPath}: probing it failed with ${code}. ` +
111
+ "Leaving the socket in place instead of removing one that may still be serving another session. " +
112
+ "Retry, or delete the file by hand once no server is running.");
113
+ const directoryPathError = (socketPath) => new Error(`The GTKX MCP socket path ${socketPath} is a directory, not a socket. ` +
114
+ "Remove it, or point XDG_RUNTIME_DIR at a directory where GTKX can create its socket.");
115
+ const listenFailureError = (socketPath, code) => new Error(`Could not create the GTKX MCP socket at ${socketPath}: listening failed with ${code}. ` +
116
+ "Check that its directory exists and is writable.");
117
+ const removeEntry = (target, inode) => {
118
+ if (inodeFor(target) === inode) {
119
+ fs.rmSync(target, { force: true });
120
+ }
121
+ };
122
+ const clearStalePath = async (target) => {
123
+ const entry = entryFor(target);
124
+ if (entry === null) {
125
+ return { kind: "vacant" };
126
+ }
127
+ if (entry.isDirectory()) {
128
+ return { kind: "directory" };
129
+ }
130
+ const outcome = await probeUntilConclusive(target);
131
+ if (outcome.kind === "vacant") {
132
+ removeEntry(target, entry.ino);
133
+ }
134
+ return outcome;
135
+ };
136
+ const requireVacantPath = async (socketPath) => {
137
+ const verdict = await clearStalePath(socketPath);
138
+ if (verdict.kind === "live") {
139
+ throw alreadyOwnedError(socketPath);
18
140
  }
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.");
141
+ if (verdict.kind === "directory") {
142
+ throw directoryPathError(socketPath);
143
+ }
144
+ if (verdict.kind === "unknown") {
145
+ throw undecidedOwnerError(socketPath, verdict.code);
146
+ }
147
+ };
148
+ const claimSocketPath = (privatePath, socketPath) => {
149
+ try {
150
+ fs.linkSync(privatePath, socketPath);
151
+ return "published";
152
+ }
153
+ catch (error) {
154
+ if (error.code === "EEXIST") {
155
+ return "occupied";
156
+ }
157
+ throw error;
158
+ }
159
+ };
160
+ const publishSocket = async (privatePath, socketPath) => {
161
+ for (let attempt = 1; attempt <= CLAIM_ATTEMPTS; attempt += 1) {
162
+ await requireVacantPath(socketPath);
163
+ if (claimSocketPath(privatePath, socketPath) === "published") {
164
+ const inode = inodeFor(socketPath);
165
+ fs.rmSync(privatePath, { force: true });
166
+ return inode;
167
+ }
168
+ }
169
+ throw alreadyOwnedError(socketPath);
170
+ };
171
+ const releaseSocketPath = async (socketPath, inode) => {
172
+ const lock = await acquireClaimLock(socketPath);
173
+ try {
174
+ removeEntry(socketPath, inode);
175
+ }
176
+ finally {
177
+ await releaseClaimLock(lock);
22
178
  }
23
- fs.unlinkSync(socketPath);
24
179
  };
25
180
  class SocketServer {
26
181
  server = null;
27
182
  socketPath;
28
183
  registry;
184
+ boundInode = null;
185
+ startup = null;
29
186
  constructor(registry, socketPath = DEFAULT_SOCKET_PATH) {
30
187
  this.registry = registry;
31
188
  this.socketPath = socketPath;
32
189
  }
33
- async start() {
34
- if (this.server) {
35
- return;
36
- }
37
- await removeStaleSocket(this.socketPath);
190
+ listen(privatePath) {
38
191
  return new Promise((resolve, reject) => {
39
- this.server = net.createServer((socket) => this.registry.register(socket));
192
+ const server = net.createServer((socket) => this.registry.register(socket));
40
193
  let isListening = false;
41
- this.server.on("error", (error) => {
194
+ server.on("error", (error) => {
42
195
  this.registry.dispatchEvent(connectionErrorEvent(error));
43
196
  if (!isListening) {
44
197
  reject(error);
45
198
  }
46
199
  });
47
- this.server.listen(this.socketPath, () => {
200
+ server.listen(privatePath, () => {
48
201
  isListening = true;
49
- resolve();
202
+ resolve(server);
50
203
  });
51
204
  });
52
205
  }
206
+ async listenPrivately(privatePath) {
207
+ try {
208
+ return await this.listen(privatePath);
209
+ }
210
+ catch (error) {
211
+ const failure = error;
212
+ throw listenFailureError(this.socketPath, failure.code ?? failure.message);
213
+ }
214
+ }
215
+ async bind() {
216
+ const privatePath = privatePathFor(this.socketPath);
217
+ await clearStalePath(privatePath);
218
+ const server = await this.listenPrivately(privatePath);
219
+ try {
220
+ this.boundInode = await publishSocket(privatePath, this.socketPath);
221
+ this.server = server;
222
+ }
223
+ catch (error) {
224
+ await closeServer(server);
225
+ throw error;
226
+ }
227
+ }
228
+ open() {
229
+ return withClaimLock(this.socketPath, () => this.bind());
230
+ }
231
+ async settleStartup() {
232
+ const startup = this.startup;
233
+ this.startup = null;
234
+ if (startup) {
235
+ await Promise.allSettled([startup]);
236
+ }
237
+ }
238
+ async release() {
239
+ const inode = this.boundInode;
240
+ this.boundInode = null;
241
+ if (inode !== null) {
242
+ await releaseSocketPath(this.socketPath, inode);
243
+ }
244
+ }
245
+ async start() {
246
+ this.startup ??= this.open();
247
+ const startup = this.startup;
248
+ try {
249
+ await startup;
250
+ }
251
+ catch (error) {
252
+ if (this.startup === startup) {
253
+ this.startup = null;
254
+ }
255
+ throw error;
256
+ }
257
+ }
53
258
  async stop() {
54
- if (!this.server) {
259
+ await this.settleStartup();
260
+ const server = this.server;
261
+ if (!server) {
55
262
  return;
56
263
  }
264
+ this.server = null;
57
265
  this.registry.dispose("Server stopping");
58
- return new Promise((resolve) => {
59
- this.server?.close(() => {
60
- this.server = null;
61
- if (fs.existsSync(this.socketPath)) {
62
- fs.unlinkSync(this.socketPath);
63
- }
64
- resolve();
65
- });
66
- });
266
+ await closeServer(server);
267
+ await this.release();
67
268
  }
68
269
  }
69
- export { SocketServer };
270
+ export { SocketServer, withClaimLock };
70
271
  //# sourceMappingURL=socket-server.js.map
@@ -1 +1 @@
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"]}
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, withClaimLock };\n"]}
@@ -45,5 +45,5 @@ declare class ProtocolConnection extends EventTarget {
45
45
  send<T = unknown>(method: string, params: unknown, timeout: number): Promise<T>;
46
46
  rejectPending(error: Error): void;
47
47
  }
48
- export { ConnectionClosedError, connectionDisconnectionEvent, connectionErrorEvent, connectionRequestEvent, ProtocolConnection, type ProtocolConnectionEvents, type AppConnections, type ConnectionEvent, type ConnectionErrorEvent, type ConnectionRequestEvent, };
48
+ export { ConnectionClosedError, connectionDisconnectionEvent, connectionErrorEvent, connectionRequestEvent, ProtocolConnection, type AppConnections, type ConnectionEvent, type ConnectionErrorEvent, type ConnectionRequestEvent, };
49
49
  //# 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,wBAAwB,EAC7B,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":"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 +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"]}
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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtkx/mcp",
3
- "version": "1.0.0-rc.3",
3
+ "version": "1.0.0",
4
4
  "description": "Model Context Protocol server that gives an agent the GTKX API reference and control of a running application",
5
5
  "keywords": [
6
6
  "gtkx",
@@ -47,9 +47,9 @@
47
47
  "dependencies": {
48
48
  "@modelcontextprotocol/sdk": "^1.30.0",
49
49
  "zod": "^4.4.3",
50
- "@gtkx/codegen": "1.0.0-rc.3",
51
- "@gtkx/utils": "1.0.0-rc.3",
52
- "@gtkx/config": "1.0.0-rc.3"
50
+ "@gtkx/codegen": "1.0.0",
51
+ "@gtkx/utils": "1.0.0",
52
+ "@gtkx/config": "1.0.0"
53
53
  },
54
54
  "scripts": {
55
55
  "release": "tsx ../../scripts/release-package.ts"
package/src/internal.ts CHANGED
@@ -3,12 +3,16 @@ export {
3
3
  invalidRequestError,
4
4
  methodNotFoundError,
5
5
  ProtocolError,
6
+ propertyNotFoundError,
6
7
  widgetNotFoundError,
7
8
  } from "./protocol/errors.js";
8
9
  export {
9
10
  DEFAULT_SOCKET_PATH,
11
+ DEFAULT_SUBTREE_DEPTH,
12
+ MAX_SUBTREE_WIDGETS,
10
13
  type ParamsSchema,
11
14
  type Request,
15
+ type SerializedProperty,
12
16
  type SerializedWidget,
13
17
  type ServerInitiatedMethod,
14
18
  type ServerRequestParams,
@@ -9,6 +9,7 @@ const ErrorCode = {
9
9
  REQUEST_TIMEOUT: 1005,
10
10
  INVALID_REQUEST: 1006,
11
11
  METHOD_NOT_FOUND: 1007,
12
+ PROPERTY_NOT_FOUND: 1008,
12
13
  } as const;
13
14
 
14
15
  function isErrorCode(code: number): code is ErrorCode {
@@ -39,6 +40,18 @@ function widgetNotFoundError(widgetId: string): ProtocolError {
39
40
  return new ProtocolError(ErrorCode.WIDGET_NOT_FOUND, `Widget '${widgetId}' not found`, { widgetId });
40
41
  }
41
42
 
43
+ function propertyNotFoundError(widgetType: string, property: string, readableProperties: string[]): ProtocolError {
44
+ return new ProtocolError(
45
+ ErrorCode.PROPERTY_NOT_FOUND,
46
+ `${widgetType} has no readable property '${property}'`,
47
+ {
48
+ widgetType,
49
+ property,
50
+ hint: `Readable properties of ${widgetType}: ${readableProperties.join(", ")}`,
51
+ },
52
+ );
53
+ }
54
+
42
55
  function requestTimeoutError(timeout: number): ProtocolError {
43
56
  return new ProtocolError(ErrorCode.REQUEST_TIMEOUT, `Request timed out after ${String(timeout)}ms`, { timeout });
44
57
  }
@@ -78,6 +91,7 @@ export {
78
91
  appNotFoundError,
79
92
  connectionWriteFailedError,
80
93
  widgetNotFoundError,
94
+ propertyNotFoundError,
81
95
  requestTimeoutError,
82
96
  invalidRequestError,
83
97
  methodNotFoundError,
@@ -11,10 +11,18 @@ type SerializedWidget = {
11
11
  role: string;
12
12
  name: string | null;
13
13
  text: string | null;
14
- sensitive: boolean;
15
- visible: boolean;
14
+ isSensitive: boolean;
15
+ isVisible: boolean;
16
16
  cssClasses: string[];
17
17
  children: SerializedWidget[];
18
+ hiddenChildren?: number;
19
+ };
20
+
21
+ type SerializedProperty = {
22
+ type: string;
23
+ value: string | number | boolean | string[] | null;
24
+ widgetId?: string;
25
+ note?: string;
18
26
  };
19
27
 
20
28
  type AppInfo = {
@@ -81,11 +89,26 @@ const widgetIdParams: z.ZodObject<{ widgetId: z.ZodString }> = z.object({
81
89
  widgetId: z.string(),
82
90
  });
83
91
 
92
+ const DEFAULT_SUBTREE_DEPTH = 8;
93
+ const MAX_SUBTREE_WIDGETS = 30;
94
+
95
+ const widgetPropsParams: z.ZodObject<
96
+ {
97
+ widgetId: z.ZodString;
98
+ properties: z.ZodOptional<z.ZodArray<z.ZodString>>;
99
+ maxDepth: z.ZodOptional<z.ZodNumber>;
100
+ }
101
+ > = z.object({
102
+ widgetId: z.string(),
103
+ properties: z.array(z.string()).optional(),
104
+ maxDepth: z.number().int().nonnegative().optional(),
105
+ });
106
+
84
107
  const treeParams: z.ZodObject<
85
108
  { rootId: z.ZodOptional<z.ZodString>; maxDepth: z.ZodOptional<z.ZodNumber> }
86
109
  > = z.object({
87
110
  rootId: z.string().optional(),
88
- maxDepth: z.number().int().positive().optional(),
111
+ maxDepth: z.number().int().nonnegative().optional(),
89
112
  });
90
113
 
91
114
  const queryOptionsSchema: z.ZodObject<
@@ -127,7 +150,7 @@ const ServerRequestParamsSchemas: {
127
150
  "app.getWindows": typeof emptyParams;
128
151
  "widget.getTree": typeof treeParams;
129
152
  "widget.query": typeof queryParams;
130
- "widget.getProps": typeof widgetIdParams;
153
+ "widget.getProps": typeof widgetPropsParams;
131
154
  "widget.click": typeof widgetIdParams;
132
155
  "widget.type": typeof typeParams;
133
156
  "widget.fireEvent": typeof fireEventParams;
@@ -136,7 +159,7 @@ const ServerRequestParamsSchemas: {
136
159
  "app.getWindows": emptyParams,
137
160
  "widget.getTree": treeParams,
138
161
  "widget.query": queryParams,
139
- "widget.getProps": widgetIdParams,
162
+ "widget.getProps": widgetPropsParams,
140
163
  "widget.click": widgetIdParams,
141
164
  "widget.type": typeParams,
142
165
  "widget.fireEvent": fireEventParams,
@@ -150,12 +173,14 @@ function getRuntimeDir(): string {
150
173
  }
151
174
 
152
175
  export {
176
+ DEFAULT_SUBTREE_DEPTH,
177
+ MAX_SUBTREE_WIDGETS,
153
178
  RequestSchema,
154
179
  ResponseSchema,
155
180
  RegisterParamsSchema,
156
181
  widgetIdParams,
182
+ widgetPropsParams,
157
183
  treeParams,
158
- queryOptionsSchema,
159
184
  queryParams,
160
185
  typeParams,
161
186
  fireEventParams,
@@ -165,6 +190,7 @@ export {
165
190
  type Request,
166
191
  type Response,
167
192
  type SerializedWidget,
193
+ type SerializedProperty,
168
194
  type AppInfo,
169
195
  type ServerRequestParams,
170
196
  type ParamsSchema,