@gtkx/mcp 0.21.0 → 1.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +137 -34
- package/bin/gtkx-mcp.js +9 -1
- package/dist/app-router.d.ts +35 -0
- package/dist/app-router.d.ts.map +1 -0
- package/dist/app-router.js +143 -0
- package/dist/app-router.js.map +1 -0
- package/dist/connection-registry.d.ts +12 -0
- package/dist/connection-registry.d.ts.map +1 -0
- package/dist/connection-registry.js +39 -0
- package/dist/connection-registry.js.map +1 -0
- package/dist/internal.d.ts +4 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +4 -0
- package/dist/internal.js.map +1 -0
- package/dist/protocol/errors.d.ts +25 -87
- package/dist/protocol/errors.d.ts.map +1 -1
- package/dist/protocol/errors.js +38 -101
- package/dist/protocol/errors.js.map +1 -1
- package/dist/protocol/schemas.d.ts +93 -0
- package/dist/protocol/schemas.d.ts.map +1 -0
- package/dist/protocol/schemas.js +63 -0
- package/dist/protocol/schemas.js.map +1 -0
- package/dist/reference.d.ts +13 -0
- package/dist/reference.d.ts.map +1 -0
- package/dist/reference.js +293 -0
- package/dist/reference.js.map +1 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +244 -0
- package/dist/server.js.map +1 -0
- package/dist/socket-server.d.ts +5 -38
- package/dist/socket-server.d.ts.map +1 -1
- package/dist/socket-server.js +36 -108
- package/dist/socket-server.js.map +1 -1
- package/dist/tool.d.ts +22 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +40 -0
- package/dist/tool.js.map +1 -0
- package/dist/transport.d.ts +49 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +152 -0
- package/dist/transport.js.map +1 -0
- package/package.json +15 -11
- package/src/app-router.ts +209 -0
- package/src/connection-registry.ts +57 -0
- package/src/internal.ts +17 -0
- package/src/protocol/errors.ts +67 -110
- package/src/protocol/schemas.ts +173 -0
- package/src/reference.ts +449 -0
- package/src/server.ts +334 -0
- package/src/socket-server.ts +46 -147
- package/src/tool.ts +73 -0
- package/src/transport.ts +242 -0
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -399
- package/dist/cli.js.map +0 -1
- package/dist/connection-manager.d.ts +0 -48
- package/dist/connection-manager.d.ts.map +0 -1
- package/dist/connection-manager.js +0 -185
- package/dist/connection-manager.js.map +0 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -5
- package/dist/index.js.map +0 -1
- package/dist/protocol/types.d.ts +0 -132
- package/dist/protocol/types.d.ts.map +0 -1
- package/dist/protocol/types.js +0 -46
- package/dist/protocol/types.js.map +0 -1
- package/src/cli.ts +0 -449
- package/src/connection-manager.ts +0 -247
- package/src/index.ts +0 -27
- package/src/protocol/types.ts +0 -153
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtkx/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.0-rc.2",
|
|
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",
|
|
7
7
|
"gtk",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|
|
22
|
-
"url": "https://github.com/gtkx-org/gtkx.git",
|
|
22
|
+
"url": "git+https://github.com/gtkx-org/gtkx.git",
|
|
23
23
|
"directory": "packages/mcp"
|
|
24
24
|
},
|
|
25
25
|
"license": "MPL-2.0",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"type": "module",
|
|
28
28
|
"exports": {
|
|
29
29
|
"./package.json": "./package.json",
|
|
30
|
-
"
|
|
31
|
-
"types": "./dist/
|
|
32
|
-
"default": "./dist/
|
|
30
|
+
"./internal": {
|
|
31
|
+
"types": "./dist/internal.d.ts",
|
|
32
|
+
"default": "./dist/internal.js"
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"bin": {
|
|
@@ -41,13 +41,17 @@
|
|
|
41
41
|
"dist",
|
|
42
42
|
"src"
|
|
43
43
|
],
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=24"
|
|
46
|
+
},
|
|
44
47
|
"dependencies": {
|
|
45
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
46
|
-
"zod": "^4.3
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
49
|
+
"zod": "^4.4.3",
|
|
50
|
+
"@gtkx/codegen": "1.0.0-rc.2",
|
|
51
|
+
"@gtkx/utils": "1.0.0-rc.2",
|
|
52
|
+
"@gtkx/config": "1.0.0-rc.2"
|
|
47
53
|
},
|
|
48
54
|
"scripts": {
|
|
49
|
-
"
|
|
50
|
-
"test": "vitest run",
|
|
51
|
-
"typecheck": "tsc -b --emitDeclarationOnly"
|
|
55
|
+
"release": "tsx ../../scripts/release-package.ts"
|
|
52
56
|
}
|
|
53
57
|
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import {
|
|
2
|
+
appNotFoundError,
|
|
3
|
+
connectionWriteFailedError,
|
|
4
|
+
invalidRequestError,
|
|
5
|
+
methodNotFoundError,
|
|
6
|
+
noAppConnectedError,
|
|
7
|
+
type ProtocolError,
|
|
8
|
+
} 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";
|
|
17
|
+
|
|
18
|
+
type AppRegisteredEvent = CustomEvent<AppInfo>;
|
|
19
|
+
type AppUnregisteredEvent = CustomEvent<string>;
|
|
20
|
+
|
|
21
|
+
type RegisteredApp = {
|
|
22
|
+
info: AppInfo;
|
|
23
|
+
connection: ProtocolConnection;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
27
|
+
|
|
28
|
+
function appRegisteredEvent(info: AppInfo): AppRegisteredEvent {
|
|
29
|
+
return new CustomEvent("appRegistered", { detail: info });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function appUnregisteredEvent(applicationId: string): AppUnregisteredEvent {
|
|
33
|
+
return new CustomEvent("appUnregistered", { detail: applicationId });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class AppRouter extends EventTarget {
|
|
37
|
+
private static defaultWaitTimeout = 10_000;
|
|
38
|
+
|
|
39
|
+
private apps: Map<string, RegisteredApp> = new Map();
|
|
40
|
+
|
|
41
|
+
private connectionToApp: Map<string, string> = new Map();
|
|
42
|
+
|
|
43
|
+
private requestTimeout: number;
|
|
44
|
+
|
|
45
|
+
private connections: AppConnections;
|
|
46
|
+
|
|
47
|
+
constructor(connections: AppConnections, options: { requestTimeout?: number } = {}) {
|
|
48
|
+
super();
|
|
49
|
+
this.connections = connections;
|
|
50
|
+
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
|
+
});
|
|
56
|
+
|
|
57
|
+
this.connections.addEventListener("disconnection", (event) => {
|
|
58
|
+
this.removeApp((event as ConnectionEvent).detail);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private resolveTargetApp(applicationId: string | undefined): RegisteredApp {
|
|
63
|
+
const app = applicationId ? this.apps.get(applicationId) : this.getDefaultApp();
|
|
64
|
+
|
|
65
|
+
if (app) {
|
|
66
|
+
return app;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (applicationId) {
|
|
70
|
+
throw appNotFoundError(applicationId);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
throw noAppConnectedError();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private handleRequest(connection: ProtocolConnection, request: Request): void {
|
|
77
|
+
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));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private handleRegister(connection: ProtocolConnection, request: Request): void {
|
|
87
|
+
const parseResult = RegisterParamsSchema.safeParse(request.params);
|
|
88
|
+
|
|
89
|
+
if (!parseResult.success) {
|
|
90
|
+
this.sendError(connection, request, invalidRequestError(parseResult.error.message));
|
|
91
|
+
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const params = parseResult.data;
|
|
96
|
+
|
|
97
|
+
const appInfo: AppInfo = {
|
|
98
|
+
applicationId: params.applicationId,
|
|
99
|
+
pid: params.pid,
|
|
100
|
+
...(params.projectRoot !== undefined && { projectRoot: params.projectRoot }),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
this.apps.set(params.applicationId, { info: appInfo, connection });
|
|
104
|
+
this.connectionToApp.set(connection.id, params.applicationId);
|
|
105
|
+
this.acknowledge(connection, request);
|
|
106
|
+
this.dispatchEvent(appRegisteredEvent(appInfo));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private handleUnregister(connection: ProtocolConnection, request: Request): void {
|
|
110
|
+
this.removeApp(connection);
|
|
111
|
+
this.acknowledge(connection, request);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private acknowledge(connection: ProtocolConnection, request: Request): void {
|
|
115
|
+
const response: Response = {
|
|
116
|
+
id: request.id,
|
|
117
|
+
result: { success: true },
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
this.connections.send(connection.id, response);
|
|
121
|
+
}
|
|
122
|
+
|
|
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
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private removeApp(connection: ProtocolConnection): void {
|
|
131
|
+
const applicationId = this.connectionToApp.get(connection.id);
|
|
132
|
+
this.connectionToApp.delete(connection.id);
|
|
133
|
+
|
|
134
|
+
if (applicationId === undefined) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (this.apps.get(applicationId)?.connection !== connection) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
this.apps.delete(applicationId);
|
|
143
|
+
this.dispatchEvent(appUnregisteredEvent(applicationId));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
getApps(): AppInfo[] {
|
|
147
|
+
return this.apps.values().map((app) => app.info).toArray();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
hasConnectedApps(): boolean {
|
|
151
|
+
return this.apps.size > 0;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
getDefaultApp(): RegisteredApp | undefined {
|
|
155
|
+
const first = this.apps.values().next();
|
|
156
|
+
|
|
157
|
+
return first.done ? undefined : first.value;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
getProjectRoot(): string | undefined {
|
|
161
|
+
return this.getDefaultApp()?.info.projectRoot;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
waitForApp(timeout: number = AppRouter.defaultWaitTimeout): Promise<AppInfo> {
|
|
165
|
+
const defaultApp = this.getDefaultApp();
|
|
166
|
+
|
|
167
|
+
if (defaultApp) {
|
|
168
|
+
return Promise.resolve(defaultApp.info);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return new Promise((resolve, reject) => {
|
|
172
|
+
const timeoutId = setTimeout(() => {
|
|
173
|
+
this.removeEventListener("appRegistered", onRegister);
|
|
174
|
+
|
|
175
|
+
reject(
|
|
176
|
+
new Error(
|
|
177
|
+
`Timeout waiting for app registration after ${String(timeout)}ms. ` +
|
|
178
|
+
"Make sure your GTKX app is running with 'gtkx dev'.",
|
|
179
|
+
),
|
|
180
|
+
);
|
|
181
|
+
}, timeout);
|
|
182
|
+
|
|
183
|
+
const onRegister = (event: Event): void => {
|
|
184
|
+
clearTimeout(timeoutId);
|
|
185
|
+
this.removeEventListener("appRegistered", onRegister);
|
|
186
|
+
resolve((event as AppRegisteredEvent).detail);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
this.addEventListener("appRegistered", onRegister);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async sendToApp<T>(applicationId: string | undefined, method: string, params?: unknown): Promise<T> {
|
|
194
|
+
const app = this.resolveTargetApp(applicationId);
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
return await app.connection.send<T>(method, params, this.requestTimeout);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (error instanceof ConnectionClosedError) {
|
|
200
|
+
this.removeApp(app.connection);
|
|
201
|
+
throw connectionWriteFailedError(app.info.applicationId);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export { AppRouter, appRegisteredEvent, appUnregisteredEvent, type AppRegisteredEvent, type AppUnregisteredEvent };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Socket } from "node:net";
|
|
2
|
+
import type { Message } from "./protocol/schemas.js";
|
|
3
|
+
import {
|
|
4
|
+
type AppConnections,
|
|
5
|
+
connectionDisconnectionEvent,
|
|
6
|
+
connectionErrorEvent,
|
|
7
|
+
connectionRequestEvent,
|
|
8
|
+
ProtocolConnection,
|
|
9
|
+
} from "./transport.js";
|
|
10
|
+
|
|
11
|
+
class ConnectionRegistry extends EventTarget implements AppConnections {
|
|
12
|
+
private connections: Map<string, ProtocolConnection> = new Map();
|
|
13
|
+
private sockets: Map<string, Socket> = new Map();
|
|
14
|
+
|
|
15
|
+
register(socket: Socket): ProtocolConnection {
|
|
16
|
+
const connection = ProtocolConnection.fromSocket(socket, {
|
|
17
|
+
onClose: () => {
|
|
18
|
+
this.connections.delete(connection.id);
|
|
19
|
+
this.sockets.delete(connection.id);
|
|
20
|
+
this.dispatchEvent(connectionDisconnectionEvent(connection));
|
|
21
|
+
},
|
|
22
|
+
onError: (error) => this.dispatchEvent(connectionErrorEvent(error)),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
this.connections.set(connection.id, connection);
|
|
26
|
+
this.sockets.set(connection.id, socket);
|
|
27
|
+
connection.on("request", (request) => this.dispatchEvent(connectionRequestEvent(connection, request)));
|
|
28
|
+
|
|
29
|
+
connection.on("invalid", ({ id: badId, error }) => {
|
|
30
|
+
connection.write({ id: badId, error: error.toErrorObject() });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return connection;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
send(connectionId: string, message: Message): void {
|
|
37
|
+
const connection = this.connections.get(connectionId);
|
|
38
|
+
|
|
39
|
+
if (!connection) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
connection.write(message);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
dispose(reason: string): void {
|
|
47
|
+
for (const connection of this.connections.values()) {
|
|
48
|
+
connection.rejectPending(new Error(reason));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for (const socket of this.sockets.values()) {
|
|
52
|
+
socket.destroy();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { ConnectionRegistry };
|
package/src/internal.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ErrorCode,
|
|
3
|
+
invalidRequestError,
|
|
4
|
+
methodNotFoundError,
|
|
5
|
+
ProtocolError,
|
|
6
|
+
widgetNotFoundError,
|
|
7
|
+
} from "./protocol/errors.js";
|
|
8
|
+
export {
|
|
9
|
+
DEFAULT_SOCKET_PATH,
|
|
10
|
+
type ParamsSchema,
|
|
11
|
+
type Request,
|
|
12
|
+
type SerializedWidget,
|
|
13
|
+
type ServerInitiatedMethod,
|
|
14
|
+
type ServerRequestParams,
|
|
15
|
+
ServerRequestParamsSchemas,
|
|
16
|
+
} from "./protocol/schemas.js";
|
|
17
|
+
export { ProtocolConnection } from "./transport.js";
|
package/src/protocol/errors.ts
CHANGED
|
@@ -1,128 +1,85 @@
|
|
|
1
|
-
|
|
2
|
-
* Error codes for MCP protocol errors.
|
|
3
|
-
*/
|
|
4
|
-
export enum McpErrorCode {
|
|
5
|
-
/** Internal server error */
|
|
6
|
-
INTERNAL_ERROR = 1000,
|
|
7
|
-
/** No GTKX application is connected */
|
|
8
|
-
NO_APP_CONNECTED = 1001,
|
|
9
|
-
/** Requested application ID was not found */
|
|
10
|
-
APP_NOT_FOUND = 1002,
|
|
11
|
-
/** Widget with specified ID was not found */
|
|
12
|
-
WIDGET_NOT_FOUND = 1003,
|
|
13
|
-
/** Widget cannot be interacted with */
|
|
14
|
-
WIDGET_NOT_INTERACTABLE = 1004,
|
|
15
|
-
/** Query timed out waiting for widget */
|
|
16
|
-
QUERY_TIMEOUT = 1005,
|
|
17
|
-
/** Widget is not the expected type */
|
|
18
|
-
INVALID_WIDGET_TYPE = 1006,
|
|
19
|
-
/** Screenshot capture failed */
|
|
20
|
-
SCREENSHOT_FAILED = 1007,
|
|
21
|
-
/** IPC request timed out */
|
|
22
|
-
IPC_TIMEOUT = 1008,
|
|
23
|
-
/** Failed to serialize data */
|
|
24
|
-
SERIALIZATION_ERROR = 1009,
|
|
25
|
-
/** Request format is invalid */
|
|
26
|
-
INVALID_REQUEST = 1010,
|
|
27
|
-
/** Requested method does not exist */
|
|
28
|
-
METHOD_NOT_FOUND = 1011,
|
|
29
|
-
}
|
|
1
|
+
type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
|
|
30
2
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
3
|
+
const ErrorCode = {
|
|
4
|
+
INTERNAL_ERROR: 1000,
|
|
5
|
+
NO_APP_CONNECTED: 1001,
|
|
6
|
+
APP_NOT_FOUND: 1002,
|
|
7
|
+
WIDGET_NOT_FOUND: 1003,
|
|
8
|
+
CONNECTION_WRITE_FAILED: 1004,
|
|
9
|
+
REQUEST_TIMEOUT: 1005,
|
|
10
|
+
INVALID_REQUEST: 1006,
|
|
11
|
+
METHOD_NOT_FOUND: 1007,
|
|
12
|
+
} as const;
|
|
41
13
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
this.data = data;
|
|
46
|
-
this.name = "McpError";
|
|
14
|
+
function isErrorCode(code: number): code is ErrorCode {
|
|
15
|
+
return (Object.values(ErrorCode) as number[]).includes(code);
|
|
16
|
+
}
|
|
47
17
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
18
|
+
function noAppConnectedError(): ProtocolError {
|
|
19
|
+
return new ProtocolError(
|
|
20
|
+
ErrorCode.NO_APP_CONNECTED,
|
|
21
|
+
"No GTKX application connected: start an app with 'gtkx dev' to connect",
|
|
22
|
+
{ hint: "Run 'gtkx dev' in your project directory" },
|
|
23
|
+
);
|
|
24
|
+
}
|
|
52
25
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
*
|
|
56
|
-
* @returns Object suitable for IPC response error field
|
|
57
|
-
*/
|
|
58
|
-
toIpcError(): { code: number; message: string; data?: unknown } {
|
|
59
|
-
return {
|
|
60
|
-
code: this.code,
|
|
61
|
-
message: this.message,
|
|
62
|
-
...(this.data !== undefined && { data: this.data }),
|
|
63
|
-
};
|
|
64
|
-
}
|
|
26
|
+
function appNotFoundError(applicationId: string): ProtocolError {
|
|
27
|
+
return new ProtocolError(ErrorCode.APP_NOT_FOUND, `Application '${applicationId}' not found`, { applicationId });
|
|
65
28
|
}
|
|
66
29
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
export function noAppConnectedError(): McpError {
|
|
73
|
-
return new McpError(
|
|
74
|
-
McpErrorCode.NO_APP_CONNECTED,
|
|
75
|
-
"No GTKX application connected: start an app with 'gtkx dev' to connect",
|
|
76
|
-
{ hint: "Run 'gtkx dev src/app.tsx' in your project directory" },
|
|
30
|
+
function connectionWriteFailedError(applicationId: string): ProtocolError {
|
|
31
|
+
return new ProtocolError(
|
|
32
|
+
ErrorCode.CONNECTION_WRITE_FAILED,
|
|
33
|
+
`Connection to application '${applicationId}' is no longer writable`,
|
|
34
|
+
{ applicationId },
|
|
77
35
|
);
|
|
78
36
|
}
|
|
79
37
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
*
|
|
83
|
-
* @param appId - The application ID that was not found
|
|
84
|
-
* @returns McpError with APP_NOT_FOUND code
|
|
85
|
-
*/
|
|
86
|
-
export function appNotFoundError(appId: string): McpError {
|
|
87
|
-
return new McpError(McpErrorCode.APP_NOT_FOUND, `Application '${appId}' not found`, { appId });
|
|
38
|
+
function widgetNotFoundError(widgetId: string): ProtocolError {
|
|
39
|
+
return new ProtocolError(ErrorCode.WIDGET_NOT_FOUND, `Widget '${widgetId}' not found`, { widgetId });
|
|
88
40
|
}
|
|
89
41
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
*
|
|
93
|
-
* @param widgetId - The widget ID that was not found
|
|
94
|
-
* @returns McpError with WIDGET_NOT_FOUND code
|
|
95
|
-
*/
|
|
96
|
-
export function widgetNotFoundError(widgetId: string): McpError {
|
|
97
|
-
return new McpError(McpErrorCode.WIDGET_NOT_FOUND, `Widget '${widgetId}' not found`, { widgetId });
|
|
42
|
+
function requestTimeoutError(timeout: number): ProtocolError {
|
|
43
|
+
return new ProtocolError(ErrorCode.REQUEST_TIMEOUT, `Request timed out after ${String(timeout)}ms`, { timeout });
|
|
98
44
|
}
|
|
99
45
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
*
|
|
103
|
-
* @param timeout - The timeout duration in milliseconds
|
|
104
|
-
* @returns McpError with IPC_TIMEOUT code
|
|
105
|
-
*/
|
|
106
|
-
export function ipcTimeoutError(timeout: number): McpError {
|
|
107
|
-
return new McpError(McpErrorCode.IPC_TIMEOUT, `IPC request timed out after ${timeout}ms`, { timeout });
|
|
46
|
+
function invalidRequestError(reason: string): ProtocolError {
|
|
47
|
+
return new ProtocolError(ErrorCode.INVALID_REQUEST, `Invalid request: ${reason}`, { reason });
|
|
108
48
|
}
|
|
109
49
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
*
|
|
113
|
-
* @param reason - Description of why the request is invalid
|
|
114
|
-
* @returns McpError with INVALID_REQUEST code
|
|
115
|
-
*/
|
|
116
|
-
export function invalidRequestError(reason: string): McpError {
|
|
117
|
-
return new McpError(McpErrorCode.INVALID_REQUEST, `Invalid request: ${reason}`, { reason });
|
|
50
|
+
function methodNotFoundError(method: string): ProtocolError {
|
|
51
|
+
return new ProtocolError(ErrorCode.METHOD_NOT_FOUND, `Method '${method}' not found`, { method });
|
|
118
52
|
}
|
|
119
53
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
54
|
+
class ProtocolError extends Error {
|
|
55
|
+
code: ErrorCode;
|
|
56
|
+
data?: unknown;
|
|
57
|
+
|
|
58
|
+
constructor(code: ErrorCode, message: string, data?: unknown) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.code = code;
|
|
61
|
+
this.data = data;
|
|
62
|
+
this.name = "ProtocolError";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
toErrorObject(): { code: number; message: string; data?: unknown } {
|
|
66
|
+
return {
|
|
67
|
+
code: this.code,
|
|
68
|
+
message: this.message,
|
|
69
|
+
...(this.data !== undefined && { data: this.data }),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
128
72
|
}
|
|
73
|
+
|
|
74
|
+
export {
|
|
75
|
+
ErrorCode,
|
|
76
|
+
isErrorCode,
|
|
77
|
+
noAppConnectedError,
|
|
78
|
+
appNotFoundError,
|
|
79
|
+
connectionWriteFailedError,
|
|
80
|
+
widgetNotFoundError,
|
|
81
|
+
requestTimeoutError,
|
|
82
|
+
invalidRequestError,
|
|
83
|
+
methodNotFoundError,
|
|
84
|
+
ProtocolError,
|
|
85
|
+
};
|