@gtkx/mcp 2.0.0-beta.10 → 2.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -172
- package/dist/app-router.d.ts +1 -5
- package/dist/app-router.d.ts.map +1 -1
- package/dist/app-router.js +8 -14
- package/dist/app-router.js.map +1 -1
- package/dist/connection-registry.d.ts +0 -1
- package/dist/connection-registry.d.ts.map +1 -1
- package/dist/connection-registry.js +0 -3
- package/dist/connection-registry.js.map +1 -1
- package/dist/protocol/schemas.d.ts +1 -5
- package/dist/protocol/schemas.d.ts.map +1 -1
- package/dist/protocol/schemas.js.map +1 -1
- package/dist/reference.d.ts.map +1 -1
- package/dist/reference.js +20 -7
- package/dist/reference.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +18 -18
- package/dist/server.js.map +1 -1
- package/dist/tool-filter.d.ts.map +1 -1
- package/dist/tool-filter.js +1 -3
- package/dist/tool-filter.js.map +1 -1
- package/package.json +20 -8
- package/src/app-router.ts +8 -18
- package/src/connection-registry.ts +0 -3
- package/src/protocol/schemas.ts +1 -5
- package/src/reference.ts +36 -8
- package/src/server.ts +19 -18
- package/src/tool-filter.ts +1 -4
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
} from "./transport.js";
|
|
10
10
|
|
|
11
11
|
class ConnectionRegistry extends EventTarget implements AppConnections {
|
|
12
|
-
private connections: Map<string, ProtocolConnection> = new Map();
|
|
13
12
|
private sockets: Map<string, Socket> = new Map();
|
|
14
13
|
|
|
15
14
|
onRequest: ConnectionRequestHandler = (_connection, request) => Promise.reject(methodNotFoundError(request.method));
|
|
@@ -17,14 +16,12 @@ class ConnectionRegistry extends EventTarget implements AppConnections {
|
|
|
17
16
|
register(socket: Socket): ProtocolConnection {
|
|
18
17
|
const connection = ProtocolConnection.fromSocket(socket, {
|
|
19
18
|
onClose: () => {
|
|
20
|
-
this.connections.delete(connection.id);
|
|
21
19
|
this.sockets.delete(connection.id);
|
|
22
20
|
this.dispatchEvent(connectionDisconnectionEvent(connection));
|
|
23
21
|
},
|
|
24
22
|
onError: (error) => this.dispatchEvent(connectionErrorEvent(error)),
|
|
25
23
|
});
|
|
26
24
|
|
|
27
|
-
this.connections.set(connection.id, connection);
|
|
28
25
|
this.sockets.set(connection.id, socket);
|
|
29
26
|
connection.fallbackRequestHandler = (request) => this.onRequest(connection, request);
|
|
30
27
|
|
package/src/protocol/schemas.ts
CHANGED
|
@@ -20,11 +20,7 @@ type SerializedProperty = {
|
|
|
20
20
|
note?: string;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
type AppInfo =
|
|
24
|
-
applicationId: string;
|
|
25
|
-
pid: number;
|
|
26
|
-
projectRoot?: string;
|
|
27
|
-
};
|
|
23
|
+
type AppInfo = z.output<typeof RegisterParamsSchema>;
|
|
28
24
|
|
|
29
25
|
type ServerRequestParams<Method extends keyof typeof ServerRequestParamsSchemas> = z.infer<
|
|
30
26
|
(typeof ServerRequestParamsSchemas)[Method]
|
package/src/reference.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type ApiReference,
|
|
3
|
+
type ApiSymbol,
|
|
4
|
+
loadApiReference,
|
|
5
|
+
mergeOmittedProps,
|
|
6
|
+
resolveGirPath,
|
|
7
|
+
resolveLibraries,
|
|
8
|
+
} from "@gtkx/codegen";
|
|
9
|
+
import { readBuiltinElementsForDocs } from "@gtkx/codegen/internal";
|
|
2
10
|
import { loadConfig } from "@gtkx/config";
|
|
11
|
+
import {
|
|
12
|
+
CONFIG_EXTENSIONS,
|
|
13
|
+
configDependenciesFor,
|
|
14
|
+
resolveAcceptedChildTypes,
|
|
15
|
+
resolveElementProps,
|
|
16
|
+
resolveOmittedProps,
|
|
17
|
+
} from "@gtkx/config/internal";
|
|
3
18
|
import { type McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
19
|
import { type CallToolResult, ErrorCode, McpError, type ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
|
|
5
20
|
import { existsSync, statSync } from "node:fs";
|
|
@@ -58,7 +73,7 @@ type ResourceServer = Pick<McpServer, "registerResource">;
|
|
|
58
73
|
|
|
59
74
|
const FRESHNESS_INTERVAL_MS = 2000;
|
|
60
75
|
const FAILURE_RETRY_MS = 5000;
|
|
61
|
-
const
|
|
76
|
+
const CONFIG_LOCATIONS = ["gtkx.config", ".config/gtkx", ".config/gtkx.config"];
|
|
62
77
|
|
|
63
78
|
const PROJECT_SOURCE_LABELS: Record<ProjectSource, string> = {
|
|
64
79
|
argument: "requested with `projectRoot`",
|
|
@@ -128,10 +143,13 @@ const isFresh = (loaded: LoadedReference): boolean =>
|
|
|
128
143
|
const current = watchFile(file.path);
|
|
129
144
|
|
|
130
145
|
return current.mtimeMs === file.mtimeMs && current.size === file.size;
|
|
131
|
-
});
|
|
146
|
+
}) && loaded.reference.hasFreshInputs();
|
|
147
|
+
|
|
148
|
+
const configCandidatePaths = (directory: string): string[] =>
|
|
149
|
+
CONFIG_LOCATIONS.flatMap((location) =>
|
|
150
|
+
CONFIG_EXTENSIONS.map((extension) => join(directory, `${location}${extension}`)));
|
|
132
151
|
|
|
133
|
-
const hasConfigFile = (directory: string): boolean =>
|
|
134
|
-
CONFIG_EXTENSIONS.some((extension) => existsSync(join(directory, `gtkx.config.${extension}`)));
|
|
152
|
+
const hasConfigFile = (directory: string): boolean => configCandidatePaths(directory).some((path) => existsSync(path));
|
|
135
153
|
|
|
136
154
|
const findProjectRoot = (start: string): string | undefined => {
|
|
137
155
|
const current = resolve(start);
|
|
@@ -172,7 +190,8 @@ const resolveProject = (
|
|
|
172
190
|
};
|
|
173
191
|
|
|
174
192
|
const loadReference = async (requestedRoot: string): Promise<LoadedReference> => {
|
|
175
|
-
const
|
|
193
|
+
const loaded = await loadConfig(requestedRoot);
|
|
194
|
+
const { config, root } = loaded;
|
|
176
195
|
|
|
177
196
|
if (config.codegen === false) {
|
|
178
197
|
throw new Error(
|
|
@@ -182,7 +201,7 @@ const loadReference = async (requestedRoot: string): Promise<LoadedReference> =>
|
|
|
182
201
|
);
|
|
183
202
|
}
|
|
184
203
|
|
|
185
|
-
const girPath = resolveGirPath(config.girPath);
|
|
204
|
+
const girPath = resolveGirPath(config.girPath, root);
|
|
186
205
|
|
|
187
206
|
if (girPath.length === 0) {
|
|
188
207
|
throw new Error(
|
|
@@ -193,13 +212,22 @@ const loadReference = async (requestedRoot: string): Promise<LoadedReference> =>
|
|
|
193
212
|
}
|
|
194
213
|
|
|
195
214
|
const libraries = resolveLibraries(config.libraries);
|
|
215
|
+
const builtin = await readBuiltinElementsForDocs();
|
|
196
216
|
|
|
197
217
|
const reference = loadApiReference({
|
|
198
218
|
libraries,
|
|
199
219
|
girPath,
|
|
220
|
+
resolveFrom: root,
|
|
221
|
+
props: { ...builtin.props, ...resolveElementProps(config.elements) },
|
|
222
|
+
omittedProps: mergeOmittedProps(builtin.omittedProps, resolveOmittedProps(config.elements)),
|
|
223
|
+
acceptedChildTypes: { ...builtin.acceptedChildTypes, ...resolveAcceptedChildTypes(config.elements) },
|
|
200
224
|
});
|
|
201
225
|
|
|
202
|
-
const watched = [
|
|
226
|
+
const watched = [...new Set([
|
|
227
|
+
...configDependenciesFor(loaded),
|
|
228
|
+
...configCandidatePaths(root),
|
|
229
|
+
...reference.inputFiles,
|
|
230
|
+
])].map((file) => watchFile(file));
|
|
203
231
|
|
|
204
232
|
return { reference, root, watched };
|
|
205
233
|
};
|
package/src/server.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import { loadConfig } from "@gtkx/config";
|
|
3
|
-
import { type McpSettings, resolveMcpSettings } from "@gtkx/config/internal";
|
|
3
|
+
import { configDependenciesFor, type McpSettings, resolveMcpSettings } from "@gtkx/config/internal";
|
|
4
4
|
import { createLogger, installGracefulShutdown, type Logger } from "@gtkx/utils";
|
|
5
5
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { resolve } from "node:path";
|
|
7
9
|
import { parseArgs } from "node:util";
|
|
8
10
|
import { z } from "zod";
|
|
9
11
|
import type { ConnectionErrorEvent } from "./transport.js";
|
|
@@ -175,9 +177,8 @@ const screenshotShape = {
|
|
|
175
177
|
.boolean()
|
|
176
178
|
.optional()
|
|
177
179
|
.describe(
|
|
178
|
-
"
|
|
179
|
-
"
|
|
180
|
-
"conversation until something actually needs to look at it.",
|
|
180
|
+
"Return the PNG as image content (default: true). Set false with `path` to save the image " +
|
|
181
|
+
"and return only its file location.",
|
|
181
182
|
),
|
|
182
183
|
};
|
|
183
184
|
|
|
@@ -296,18 +297,14 @@ const widgetPropsTool = (appRouter: AppRouter): Tool =>
|
|
|
296
297
|
title: "Get widget properties",
|
|
297
298
|
kind: "readOnly",
|
|
298
299
|
description:
|
|
299
|
-
"
|
|
300
|
-
|
|
301
|
-
`
|
|
302
|
-
|
|
303
|
-
"
|
|
304
|
-
"
|
|
305
|
-
"
|
|
306
|
-
"
|
|
307
|
-
"`{type, value}`, where an enum or flags value is its GType value name, a 64-bit integer a " +
|
|
308
|
-
"decimal string, and an object its GType name plus `widgetId` when it is a widget. Asking for " +
|
|
309
|
-
"a property the widget does not have fails; a value that cannot be marshalled carries a `note` " +
|
|
310
|
-
"instead.",
|
|
300
|
+
"Read a widget's type, accessible role, name, text, sensitivity, visibility, CSS classes, " +
|
|
301
|
+
`and descendants. Defaults to ${String(DEFAULT_SUBTREE_DEPTH)} levels; \`maxDepth\` changes ` +
|
|
302
|
+
`the depth. Results contain at most ${String(MAX_SUBTREE_WIDGETS)} widgets, in breadth-first ` +
|
|
303
|
+
"order. `hiddenChildren` counts omitted direct children; query that widget's ID to expand it. " +
|
|
304
|
+
"Use `gtkx_get_widget_tree` for an overview. Requested GObject `properties` appear first, " +
|
|
305
|
+
"keyed by canonical kebab-case name with `{type, value}`. Enum/flags values use GType value " +
|
|
306
|
+
"names, 64-bit integers use decimal strings, and objects use their GType name plus `widgetId` " +
|
|
307
|
+
"for widgets. Unknown properties fail; values that cannot be converted include a `note`.",
|
|
311
308
|
inputSchema: widgetPropsShape,
|
|
312
309
|
handler: async ({ applicationId, appTimeout, ...params }) => {
|
|
313
310
|
const result = await appRouter.sendToApp(applicationId, "widget.getProps", params, appTimeout);
|
|
@@ -365,7 +362,7 @@ function buildInteractionTools(appRouter: AppRouter): Tool[] {
|
|
|
365
362
|
title: "Click widget",
|
|
366
363
|
kind: "action",
|
|
367
364
|
description:
|
|
368
|
-
"Click a widget through userEvent.click
|
|
365
|
+
"Click a widget through userEvent.click. Works with " +
|
|
369
366
|
"buttons, checkboxes, switches, list and grid rows, tree expanders, and column headers: a " +
|
|
370
367
|
"row is selected, an expander toggles its row's expansion, and a header sorts its column.",
|
|
371
368
|
inputSchema: widgetIdShape,
|
|
@@ -539,7 +536,11 @@ const configuredSettings = async (cwd: string): Promise<McpSettings> => {
|
|
|
539
536
|
const { config } = await loadConfig(cwd);
|
|
540
537
|
|
|
541
538
|
return resolveMcpSettings(config);
|
|
542
|
-
} catch {
|
|
539
|
+
} catch (error) {
|
|
540
|
+
if (configDependenciesFor(error).some((path) => existsSync(resolve(cwd, path)))) {
|
|
541
|
+
throw error;
|
|
542
|
+
}
|
|
543
|
+
|
|
543
544
|
return DEFAULT_SETTINGS;
|
|
544
545
|
}
|
|
545
546
|
};
|
package/src/tool-filter.ts
CHANGED
|
@@ -2,12 +2,9 @@ import type { McpSettings } from "@gtkx/config/internal";
|
|
|
2
2
|
import type { Tool } from "./tool.js";
|
|
3
3
|
|
|
4
4
|
const NEGATION_PREFIX = "!";
|
|
5
|
-
const SPECIAL_CHARACTERS = /[.+?^${}()|[\]\\]/g;
|
|
6
|
-
|
|
7
|
-
const escapeLiteral = (value: string): string => value.replaceAll(SPECIAL_CHARACTERS, String.raw`\$&`);
|
|
8
5
|
|
|
9
6
|
const patternToRegExp = (pattern: string): RegExp =>
|
|
10
|
-
new RegExp(`^${pattern.split("*").map((part) =>
|
|
7
|
+
new RegExp(`^${pattern.split("*").map((part) => RegExp.escape(part)).join(".*")}$`);
|
|
11
8
|
|
|
12
9
|
const matchingNames = (tools: Tool[], pattern: string): string[] => {
|
|
13
10
|
const expression = patternToRegExp(pattern);
|