gitx.do 0.0.1
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/LICENSE +21 -0
- package/README.md +156 -0
- package/dist/durable-object/object-store.d.ts +113 -0
- package/dist/durable-object/object-store.d.ts.map +1 -0
- package/dist/durable-object/object-store.js +387 -0
- package/dist/durable-object/object-store.js.map +1 -0
- package/dist/durable-object/schema.d.ts +17 -0
- package/dist/durable-object/schema.d.ts.map +1 -0
- package/dist/durable-object/schema.js +43 -0
- package/dist/durable-object/schema.js.map +1 -0
- package/dist/durable-object/wal.d.ts +111 -0
- package/dist/durable-object/wal.d.ts.map +1 -0
- package/dist/durable-object/wal.js +200 -0
- package/dist/durable-object/wal.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +101 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/adapter.d.ts +231 -0
- package/dist/mcp/adapter.d.ts.map +1 -0
- package/dist/mcp/adapter.js +502 -0
- package/dist/mcp/adapter.js.map +1 -0
- package/dist/mcp/sandbox.d.ts +261 -0
- package/dist/mcp/sandbox.d.ts.map +1 -0
- package/dist/mcp/sandbox.js +983 -0
- package/dist/mcp/sandbox.js.map +1 -0
- package/dist/mcp/sdk-adapter.d.ts +413 -0
- package/dist/mcp/sdk-adapter.d.ts.map +1 -0
- package/dist/mcp/sdk-adapter.js +672 -0
- package/dist/mcp/sdk-adapter.js.map +1 -0
- package/dist/mcp/tools.d.ts +133 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +1604 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/ops/blame.d.ts +148 -0
- package/dist/ops/blame.d.ts.map +1 -0
- package/dist/ops/blame.js +754 -0
- package/dist/ops/blame.js.map +1 -0
- package/dist/ops/branch.d.ts +215 -0
- package/dist/ops/branch.d.ts.map +1 -0
- package/dist/ops/branch.js +608 -0
- package/dist/ops/branch.js.map +1 -0
- package/dist/ops/commit-traversal.d.ts +209 -0
- package/dist/ops/commit-traversal.d.ts.map +1 -0
- package/dist/ops/commit-traversal.js +755 -0
- package/dist/ops/commit-traversal.js.map +1 -0
- package/dist/ops/commit.d.ts +221 -0
- package/dist/ops/commit.d.ts.map +1 -0
- package/dist/ops/commit.js +606 -0
- package/dist/ops/commit.js.map +1 -0
- package/dist/ops/merge-base.d.ts +223 -0
- package/dist/ops/merge-base.d.ts.map +1 -0
- package/dist/ops/merge-base.js +581 -0
- package/dist/ops/merge-base.js.map +1 -0
- package/dist/ops/merge.d.ts +385 -0
- package/dist/ops/merge.d.ts.map +1 -0
- package/dist/ops/merge.js +1203 -0
- package/dist/ops/merge.js.map +1 -0
- package/dist/ops/tag.d.ts +182 -0
- package/dist/ops/tag.d.ts.map +1 -0
- package/dist/ops/tag.js +608 -0
- package/dist/ops/tag.js.map +1 -0
- package/dist/ops/tree-builder.d.ts +82 -0
- package/dist/ops/tree-builder.d.ts.map +1 -0
- package/dist/ops/tree-builder.js +246 -0
- package/dist/ops/tree-builder.js.map +1 -0
- package/dist/ops/tree-diff.d.ts +243 -0
- package/dist/ops/tree-diff.d.ts.map +1 -0
- package/dist/ops/tree-diff.js +657 -0
- package/dist/ops/tree-diff.js.map +1 -0
- package/dist/pack/delta.d.ts +68 -0
- package/dist/pack/delta.d.ts.map +1 -0
- package/dist/pack/delta.js +343 -0
- package/dist/pack/delta.js.map +1 -0
- package/dist/pack/format.d.ts +84 -0
- package/dist/pack/format.d.ts.map +1 -0
- package/dist/pack/format.js +261 -0
- package/dist/pack/format.js.map +1 -0
- package/dist/pack/full-generation.d.ts +327 -0
- package/dist/pack/full-generation.d.ts.map +1 -0
- package/dist/pack/full-generation.js +1159 -0
- package/dist/pack/full-generation.js.map +1 -0
- package/dist/pack/generation.d.ts +118 -0
- package/dist/pack/generation.d.ts.map +1 -0
- package/dist/pack/generation.js +459 -0
- package/dist/pack/generation.js.map +1 -0
- package/dist/pack/index.d.ts +181 -0
- package/dist/pack/index.d.ts.map +1 -0
- package/dist/pack/index.js +552 -0
- package/dist/pack/index.js.map +1 -0
- package/dist/refs/branch.d.ts +224 -0
- package/dist/refs/branch.d.ts.map +1 -0
- package/dist/refs/branch.js +170 -0
- package/dist/refs/branch.js.map +1 -0
- package/dist/refs/storage.d.ts +208 -0
- package/dist/refs/storage.d.ts.map +1 -0
- package/dist/refs/storage.js +421 -0
- package/dist/refs/storage.js.map +1 -0
- package/dist/refs/tag.d.ts +230 -0
- package/dist/refs/tag.d.ts.map +1 -0
- package/dist/refs/tag.js +188 -0
- package/dist/refs/tag.js.map +1 -0
- package/dist/storage/lru-cache.d.ts +188 -0
- package/dist/storage/lru-cache.d.ts.map +1 -0
- package/dist/storage/lru-cache.js +410 -0
- package/dist/storage/lru-cache.js.map +1 -0
- package/dist/storage/object-index.d.ts +140 -0
- package/dist/storage/object-index.d.ts.map +1 -0
- package/dist/storage/object-index.js +166 -0
- package/dist/storage/object-index.js.map +1 -0
- package/dist/storage/r2-pack.d.ts +394 -0
- package/dist/storage/r2-pack.d.ts.map +1 -0
- package/dist/storage/r2-pack.js +1062 -0
- package/dist/storage/r2-pack.js.map +1 -0
- package/dist/tiered/cdc-pipeline.d.ts +316 -0
- package/dist/tiered/cdc-pipeline.d.ts.map +1 -0
- package/dist/tiered/cdc-pipeline.js +771 -0
- package/dist/tiered/cdc-pipeline.js.map +1 -0
- package/dist/tiered/migration.d.ts +242 -0
- package/dist/tiered/migration.d.ts.map +1 -0
- package/dist/tiered/migration.js +592 -0
- package/dist/tiered/migration.js.map +1 -0
- package/dist/tiered/parquet-writer.d.ts +248 -0
- package/dist/tiered/parquet-writer.d.ts.map +1 -0
- package/dist/tiered/parquet-writer.js +555 -0
- package/dist/tiered/parquet-writer.js.map +1 -0
- package/dist/tiered/read-path.d.ts +141 -0
- package/dist/tiered/read-path.d.ts.map +1 -0
- package/dist/tiered/read-path.js +204 -0
- package/dist/tiered/read-path.js.map +1 -0
- package/dist/types/objects.d.ts +53 -0
- package/dist/types/objects.d.ts.map +1 -0
- package/dist/types/objects.js +291 -0
- package/dist/types/objects.js.map +1 -0
- package/dist/types/storage.d.ts +117 -0
- package/dist/types/storage.d.ts.map +1 -0
- package/dist/types/storage.js +8 -0
- package/dist/types/storage.js.map +1 -0
- package/dist/utils/hash.d.ts +31 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +60 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/sha1.d.ts +26 -0
- package/dist/utils/sha1.d.ts.map +1 -0
- package/dist/utils/sha1.js +127 -0
- package/dist/utils/sha1.js.map +1 -0
- package/dist/wire/capabilities.d.ts +236 -0
- package/dist/wire/capabilities.d.ts.map +1 -0
- package/dist/wire/capabilities.js +437 -0
- package/dist/wire/capabilities.js.map +1 -0
- package/dist/wire/pkt-line.d.ts +67 -0
- package/dist/wire/pkt-line.d.ts.map +1 -0
- package/dist/wire/pkt-line.js +145 -0
- package/dist/wire/pkt-line.js.map +1 -0
- package/dist/wire/receive-pack.d.ts +302 -0
- package/dist/wire/receive-pack.d.ts.map +1 -0
- package/dist/wire/receive-pack.js +885 -0
- package/dist/wire/receive-pack.js.map +1 -0
- package/dist/wire/smart-http.d.ts +321 -0
- package/dist/wire/smart-http.d.ts.map +1 -0
- package/dist/wire/smart-http.js +654 -0
- package/dist/wire/smart-http.js.map +1 -0
- package/dist/wire/upload-pack.d.ts +333 -0
- package/dist/wire/upload-pack.d.ts.map +1 -0
- package/dist/wire/upload-pack.js +850 -0
- package/dist/wire/upload-pack.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP SDK Adapter
|
|
3
|
+
*
|
|
4
|
+
* This module provides a full-featured adapter for the MCP SDK,
|
|
5
|
+
* including SDK initialization, tool registration, request/response
|
|
6
|
+
* handling, error propagation, and connection lifecycle management.
|
|
7
|
+
*/
|
|
8
|
+
import { gitTools } from './tools';
|
|
9
|
+
/**
|
|
10
|
+
* MCP SDK Error codes - JSON-RPC 2.0 standard codes and MCP-specific codes
|
|
11
|
+
*/
|
|
12
|
+
export var MCPSDKErrorCode;
|
|
13
|
+
(function (MCPSDKErrorCode) {
|
|
14
|
+
// JSON-RPC standard error codes
|
|
15
|
+
MCPSDKErrorCode[MCPSDKErrorCode["PARSE_ERROR"] = -32700] = "PARSE_ERROR";
|
|
16
|
+
MCPSDKErrorCode[MCPSDKErrorCode["INVALID_REQUEST"] = -32600] = "INVALID_REQUEST";
|
|
17
|
+
MCPSDKErrorCode[MCPSDKErrorCode["METHOD_NOT_FOUND"] = -32601] = "METHOD_NOT_FOUND";
|
|
18
|
+
MCPSDKErrorCode[MCPSDKErrorCode["INVALID_PARAMS"] = -32602] = "INVALID_PARAMS";
|
|
19
|
+
MCPSDKErrorCode[MCPSDKErrorCode["INTERNAL_ERROR"] = -32603] = "INTERNAL_ERROR";
|
|
20
|
+
// MCP-specific error codes
|
|
21
|
+
MCPSDKErrorCode[MCPSDKErrorCode["TOOL_NOT_FOUND"] = -32001] = "TOOL_NOT_FOUND";
|
|
22
|
+
MCPSDKErrorCode[MCPSDKErrorCode["RESOURCE_NOT_FOUND"] = -32002] = "RESOURCE_NOT_FOUND";
|
|
23
|
+
MCPSDKErrorCode[MCPSDKErrorCode["PROMPT_NOT_FOUND"] = -32003] = "PROMPT_NOT_FOUND";
|
|
24
|
+
MCPSDKErrorCode[MCPSDKErrorCode["CAPABILITY_NOT_SUPPORTED"] = -32004] = "CAPABILITY_NOT_SUPPORTED";
|
|
25
|
+
})(MCPSDKErrorCode || (MCPSDKErrorCode = {}));
|
|
26
|
+
/**
|
|
27
|
+
* MCP SDK Error class
|
|
28
|
+
*/
|
|
29
|
+
export class MCPSDKError extends Error {
|
|
30
|
+
code;
|
|
31
|
+
data;
|
|
32
|
+
constructor(code, message, data) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.code = code;
|
|
35
|
+
this.data = data;
|
|
36
|
+
this.name = 'MCPSDKError';
|
|
37
|
+
}
|
|
38
|
+
toJSONRPC() {
|
|
39
|
+
const result = {
|
|
40
|
+
code: this.code,
|
|
41
|
+
message: this.message,
|
|
42
|
+
};
|
|
43
|
+
if (this.data !== undefined) {
|
|
44
|
+
result.data = this.data;
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* MCP SDK Adapter class
|
|
51
|
+
*/
|
|
52
|
+
export class MCPSDKAdapter {
|
|
53
|
+
config;
|
|
54
|
+
connectionState = 'disconnected';
|
|
55
|
+
tools = new Map();
|
|
56
|
+
toolIdCounter = 0;
|
|
57
|
+
session = null;
|
|
58
|
+
stateChangeListeners = [];
|
|
59
|
+
connectedListeners = [];
|
|
60
|
+
disconnectedListeners = [];
|
|
61
|
+
notificationListeners = new Map();
|
|
62
|
+
progressListeners = [];
|
|
63
|
+
errorListeners = [];
|
|
64
|
+
pongListeners = [];
|
|
65
|
+
connectionTimeoutListeners = [];
|
|
66
|
+
pendingRequests = new Map();
|
|
67
|
+
currentRequestId = 0;
|
|
68
|
+
transport = null;
|
|
69
|
+
clientResponsive = true;
|
|
70
|
+
pingTimeoutId = null;
|
|
71
|
+
cleanupOnShutdown = false;
|
|
72
|
+
constructor(config) {
|
|
73
|
+
// Validate configuration
|
|
74
|
+
if (config?.name !== undefined && config.name === '') {
|
|
75
|
+
throw new Error('Configuration error: name is required and cannot be empty');
|
|
76
|
+
}
|
|
77
|
+
this.config = {
|
|
78
|
+
name: config?.name || 'gitx.do',
|
|
79
|
+
version: config?.version || '0.0.1',
|
|
80
|
+
vendor: config?.vendor || 'gitx.do',
|
|
81
|
+
transports: config?.transports || ['stdio'],
|
|
82
|
+
protocolVersion: config?.protocolVersion || '2024-11-05',
|
|
83
|
+
capabilities: config?.capabilities || {},
|
|
84
|
+
logger: config?.logger,
|
|
85
|
+
mode: config?.mode || 'development',
|
|
86
|
+
pingInterval: config?.pingInterval,
|
|
87
|
+
pingTimeout: config?.pingTimeout,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get the adapter configuration
|
|
92
|
+
*/
|
|
93
|
+
getConfig() {
|
|
94
|
+
return { ...this.config };
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get supported transports
|
|
98
|
+
*/
|
|
99
|
+
getSupportedTransports() {
|
|
100
|
+
return [...(this.config.transports || ['stdio'])];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get protocol version
|
|
104
|
+
*/
|
|
105
|
+
getProtocolVersion() {
|
|
106
|
+
return this.config.protocolVersion || '2024-11-05';
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get SDK version
|
|
110
|
+
*/
|
|
111
|
+
getSDKVersion() {
|
|
112
|
+
return '1.0.0';
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get capabilities
|
|
116
|
+
*/
|
|
117
|
+
getCapabilities() {
|
|
118
|
+
return { ...this.config.capabilities };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get connection state
|
|
122
|
+
*/
|
|
123
|
+
getConnectionState() {
|
|
124
|
+
return this.connectionState;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Set connection state and notify listeners
|
|
128
|
+
*/
|
|
129
|
+
setConnectionState(state) {
|
|
130
|
+
this.connectionState = state;
|
|
131
|
+
for (const listener of this.stateChangeListeners) {
|
|
132
|
+
listener(state);
|
|
133
|
+
}
|
|
134
|
+
if (state === 'connected') {
|
|
135
|
+
for (const listener of this.connectedListeners) {
|
|
136
|
+
listener();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
else if (state === 'disconnected') {
|
|
140
|
+
for (const listener of this.disconnectedListeners) {
|
|
141
|
+
listener();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Register a state change listener
|
|
147
|
+
*/
|
|
148
|
+
onStateChange(listener) {
|
|
149
|
+
this.stateChangeListeners.push(listener);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Register a connected listener
|
|
153
|
+
*/
|
|
154
|
+
onConnected(listener) {
|
|
155
|
+
this.connectedListeners.push(listener);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Register a disconnected listener
|
|
159
|
+
*/
|
|
160
|
+
onDisconnected(listener) {
|
|
161
|
+
this.disconnectedListeners.push(listener);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Register a notification listener
|
|
165
|
+
*/
|
|
166
|
+
onNotification(type, listener) {
|
|
167
|
+
const listeners = this.notificationListeners.get(type) || [];
|
|
168
|
+
listeners.push(listener);
|
|
169
|
+
this.notificationListeners.set(type, listeners);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Emit a notification
|
|
173
|
+
*/
|
|
174
|
+
emitNotification(type) {
|
|
175
|
+
const listeners = this.notificationListeners.get(type) || [];
|
|
176
|
+
for (const listener of listeners) {
|
|
177
|
+
listener();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Register a progress listener
|
|
182
|
+
*/
|
|
183
|
+
onProgress(listener) {
|
|
184
|
+
this.progressListeners.push(listener);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Register an error listener
|
|
188
|
+
*/
|
|
189
|
+
onError(listener) {
|
|
190
|
+
this.errorListeners.push(listener);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Register a pong listener
|
|
194
|
+
*/
|
|
195
|
+
onPong(listener) {
|
|
196
|
+
this.pongListeners.push(listener);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Register a connection timeout listener
|
|
200
|
+
*/
|
|
201
|
+
onConnectionTimeout(listener) {
|
|
202
|
+
this.connectionTimeoutListeners.push(listener);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Start the adapter
|
|
206
|
+
*/
|
|
207
|
+
async start() {
|
|
208
|
+
if (this.connectionState !== 'disconnected') {
|
|
209
|
+
throw new Error('Adapter is already started or running');
|
|
210
|
+
}
|
|
211
|
+
this.setConnectionState('initializing');
|
|
212
|
+
// Simulate initialization
|
|
213
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
214
|
+
this.setConnectionState('connected');
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Connect with a transport
|
|
218
|
+
*/
|
|
219
|
+
async connect(transport) {
|
|
220
|
+
this.transport = transport;
|
|
221
|
+
if (this.connectionState === 'disconnected') {
|
|
222
|
+
await this.start();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Shutdown the adapter
|
|
227
|
+
*/
|
|
228
|
+
async shutdown(options) {
|
|
229
|
+
const cleanup = options?.cleanup ?? false;
|
|
230
|
+
this.cleanupOnShutdown = cleanup;
|
|
231
|
+
if (options?.graceful && options?.timeout) {
|
|
232
|
+
// Wait for pending requests with timeout
|
|
233
|
+
const timeoutPromise = new Promise((resolve) => setTimeout(resolve, options.timeout));
|
|
234
|
+
await Promise.race([this.waitForPendingRequests(), timeoutPromise]);
|
|
235
|
+
}
|
|
236
|
+
if (this.pingTimeoutId) {
|
|
237
|
+
clearTimeout(this.pingTimeoutId);
|
|
238
|
+
this.pingTimeoutId = null;
|
|
239
|
+
}
|
|
240
|
+
if (cleanup) {
|
|
241
|
+
this.tools.clear();
|
|
242
|
+
}
|
|
243
|
+
this.transport = null;
|
|
244
|
+
this.session = null;
|
|
245
|
+
this.setConnectionState('disconnected');
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Wait for all pending requests to complete
|
|
249
|
+
*/
|
|
250
|
+
async waitForPendingRequests() {
|
|
251
|
+
while (this.pendingRequests.size > 0) {
|
|
252
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Handle client initialization
|
|
257
|
+
*/
|
|
258
|
+
async handleClientInitialize(request) {
|
|
259
|
+
// Validate protocol version
|
|
260
|
+
const supportedVersions = ['2024-11-05'];
|
|
261
|
+
if (!supportedVersions.includes(request.protocolVersion)) {
|
|
262
|
+
throw new MCPSDKError(MCPSDKErrorCode.INVALID_PARAMS, `Incompatible protocol version: ${request.protocolVersion}. Supported versions: ${supportedVersions.join(', ')}`);
|
|
263
|
+
}
|
|
264
|
+
// Create session
|
|
265
|
+
this.session = {
|
|
266
|
+
id: `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
267
|
+
clientInfo: request.clientInfo,
|
|
268
|
+
clientCapabilities: request.capabilities,
|
|
269
|
+
};
|
|
270
|
+
return {
|
|
271
|
+
serverInfo: {
|
|
272
|
+
name: this.config.name,
|
|
273
|
+
version: this.config.version,
|
|
274
|
+
},
|
|
275
|
+
capabilities: this.config.capabilities || {},
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Get current session
|
|
280
|
+
*/
|
|
281
|
+
getSession() {
|
|
282
|
+
return this.session;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Register a tool
|
|
286
|
+
*/
|
|
287
|
+
registerTool(registration) {
|
|
288
|
+
// Validate schema type
|
|
289
|
+
if (registration.inputSchema.type !== 'object' &&
|
|
290
|
+
registration.inputSchema.type !== 'string' &&
|
|
291
|
+
registration.inputSchema.type !== 'number' &&
|
|
292
|
+
registration.inputSchema.type !== 'boolean' &&
|
|
293
|
+
registration.inputSchema.type !== 'array') {
|
|
294
|
+
throw new Error(`Invalid schema type: ${registration.inputSchema.type}. Expected valid JSON Schema type.`);
|
|
295
|
+
}
|
|
296
|
+
if (this.tools.has(registration.name)) {
|
|
297
|
+
throw new Error(`Tool '${registration.name}' already exists (duplicate)`);
|
|
298
|
+
}
|
|
299
|
+
const internalTool = {
|
|
300
|
+
...registration,
|
|
301
|
+
id: `tool-${++this.toolIdCounter}`,
|
|
302
|
+
};
|
|
303
|
+
this.tools.set(registration.name, internalTool);
|
|
304
|
+
this.emitNotification('tools/list_changed');
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Register multiple tools
|
|
308
|
+
*/
|
|
309
|
+
registerTools(registrations) {
|
|
310
|
+
for (const registration of registrations) {
|
|
311
|
+
// Don't emit notification for each tool
|
|
312
|
+
if (registration.inputSchema.type !== 'object' &&
|
|
313
|
+
registration.inputSchema.type !== 'string' &&
|
|
314
|
+
registration.inputSchema.type !== 'number' &&
|
|
315
|
+
registration.inputSchema.type !== 'boolean' &&
|
|
316
|
+
registration.inputSchema.type !== 'array') {
|
|
317
|
+
throw new Error(`Invalid schema type: ${registration.inputSchema.type}. Expected valid JSON Schema type.`);
|
|
318
|
+
}
|
|
319
|
+
if (this.tools.has(registration.name)) {
|
|
320
|
+
throw new Error(`Tool '${registration.name}' already exists (duplicate)`);
|
|
321
|
+
}
|
|
322
|
+
const internalTool = {
|
|
323
|
+
...registration,
|
|
324
|
+
id: `tool-${++this.toolIdCounter}`,
|
|
325
|
+
};
|
|
326
|
+
this.tools.set(registration.name, internalTool);
|
|
327
|
+
}
|
|
328
|
+
this.emitNotification('tools/list_changed');
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Unregister a tool
|
|
332
|
+
*/
|
|
333
|
+
unregisterTool(name) {
|
|
334
|
+
this.tools.delete(name);
|
|
335
|
+
this.emitNotification('tools/list_changed');
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Get a tool by name
|
|
339
|
+
*/
|
|
340
|
+
getTool(name) {
|
|
341
|
+
const tool = this.tools.get(name);
|
|
342
|
+
if (!tool)
|
|
343
|
+
return undefined;
|
|
344
|
+
return {
|
|
345
|
+
id: tool.id,
|
|
346
|
+
name: tool.name,
|
|
347
|
+
description: tool.description,
|
|
348
|
+
inputSchema: tool.inputSchema,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* List all tools
|
|
353
|
+
*/
|
|
354
|
+
listTools() {
|
|
355
|
+
const result = [];
|
|
356
|
+
for (const tool of this.tools.values()) {
|
|
357
|
+
result.push({
|
|
358
|
+
id: tool.id,
|
|
359
|
+
name: tool.name,
|
|
360
|
+
description: tool.description,
|
|
361
|
+
inputSchema: tool.inputSchema,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Register gitdo tools
|
|
368
|
+
*/
|
|
369
|
+
registerGitdoTools() {
|
|
370
|
+
for (const tool of gitTools) {
|
|
371
|
+
if (!this.tools.has(tool.name)) {
|
|
372
|
+
this.registerTool({
|
|
373
|
+
name: tool.name,
|
|
374
|
+
description: tool.description,
|
|
375
|
+
inputSchema: tool.inputSchema,
|
|
376
|
+
handler: async (params) => tool.handler(params),
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Handle tools/list request
|
|
383
|
+
*/
|
|
384
|
+
async handleToolsList(options) {
|
|
385
|
+
const allTools = this.listTools();
|
|
386
|
+
const pageSize = 10;
|
|
387
|
+
// Parse cursor
|
|
388
|
+
let startIndex = 0;
|
|
389
|
+
if (options?.cursor) {
|
|
390
|
+
startIndex = parseInt(options.cursor, 10);
|
|
391
|
+
}
|
|
392
|
+
const endIndex = startIndex + pageSize;
|
|
393
|
+
const pageTools = allTools.slice(startIndex, endIndex);
|
|
394
|
+
const result = {
|
|
395
|
+
tools: pageTools.map((t) => ({
|
|
396
|
+
name: t.name,
|
|
397
|
+
description: t.description,
|
|
398
|
+
inputSchema: t.inputSchema,
|
|
399
|
+
})),
|
|
400
|
+
};
|
|
401
|
+
if (endIndex < allTools.length) {
|
|
402
|
+
result.nextCursor = String(endIndex);
|
|
403
|
+
}
|
|
404
|
+
return result;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Handle tools/call request
|
|
408
|
+
*/
|
|
409
|
+
handleToolsCall(request) {
|
|
410
|
+
// Generate requestId upfront for consistent tracking
|
|
411
|
+
const requestId = `req-${++this.currentRequestId}`;
|
|
412
|
+
// Helper to create a rejected promise with requestId attached
|
|
413
|
+
const createRejectedPromise = (error) => {
|
|
414
|
+
const promise = Promise.reject(error);
|
|
415
|
+
promise.requestId = requestId;
|
|
416
|
+
return promise;
|
|
417
|
+
};
|
|
418
|
+
const tool = this.tools.get(request.name);
|
|
419
|
+
if (!tool) {
|
|
420
|
+
return createRejectedPromise(new MCPSDKError(MCPSDKErrorCode.TOOL_NOT_FOUND, `Tool '${request.name}' not found (nonexistent)`));
|
|
421
|
+
}
|
|
422
|
+
// Validate required parameters
|
|
423
|
+
const schema = tool.inputSchema;
|
|
424
|
+
if (schema.required) {
|
|
425
|
+
for (const requiredParam of schema.required) {
|
|
426
|
+
if (!(requiredParam in request.arguments) ||
|
|
427
|
+
request.arguments[requiredParam] === undefined) {
|
|
428
|
+
return createRejectedPromise(new MCPSDKError(MCPSDKErrorCode.INVALID_PARAMS, `Missing required parameter: ${requiredParam}`));
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
// Create request tracking
|
|
433
|
+
this.pendingRequests.set(requestId, { cancelled: false });
|
|
434
|
+
// Create context
|
|
435
|
+
const context = {
|
|
436
|
+
reportProgress: async (progress, total) => {
|
|
437
|
+
for (const listener of this.progressListeners) {
|
|
438
|
+
listener({ progress, total });
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
isCancelled: () => {
|
|
442
|
+
const req = this.pendingRequests.get(requestId);
|
|
443
|
+
return req?.cancelled ?? false;
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
const executeHandler = async () => {
|
|
447
|
+
try {
|
|
448
|
+
const result = await tool.handler(request.arguments, context);
|
|
449
|
+
this.pendingRequests.delete(requestId);
|
|
450
|
+
return { ...result, requestId };
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
this.pendingRequests.delete(requestId);
|
|
454
|
+
// Log error if logger configured
|
|
455
|
+
if (this.config.logger?.error) {
|
|
456
|
+
this.config.logger.error('Tool execution error:', error instanceof Error ? error.message : String(error));
|
|
457
|
+
}
|
|
458
|
+
// Format error message based on mode
|
|
459
|
+
let errorText = error instanceof Error ? error.message : String(error);
|
|
460
|
+
if (this.config.mode === 'development' && error instanceof Error && error.stack) {
|
|
461
|
+
errorText = error.stack;
|
|
462
|
+
}
|
|
463
|
+
return {
|
|
464
|
+
content: [{ type: 'text', text: errorText }],
|
|
465
|
+
isError: true,
|
|
466
|
+
requestId,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
// Create the promise and attach the requestId property
|
|
471
|
+
const promise = executeHandler();
|
|
472
|
+
promise.requestId = requestId;
|
|
473
|
+
return promise;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Cancel a request
|
|
477
|
+
*/
|
|
478
|
+
cancelRequest(requestId) {
|
|
479
|
+
if (requestId) {
|
|
480
|
+
const req = this.pendingRequests.get(requestId);
|
|
481
|
+
if (req) {
|
|
482
|
+
req.cancelled = true;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Handle raw JSON-RPC message
|
|
488
|
+
*/
|
|
489
|
+
async handleMessage(message) {
|
|
490
|
+
let parsed;
|
|
491
|
+
try {
|
|
492
|
+
parsed = JSON.parse(message);
|
|
493
|
+
}
|
|
494
|
+
catch {
|
|
495
|
+
return JSON.stringify({
|
|
496
|
+
jsonrpc: '2.0',
|
|
497
|
+
id: null,
|
|
498
|
+
error: {
|
|
499
|
+
code: MCPSDKErrorCode.PARSE_ERROR,
|
|
500
|
+
message: 'Parse error: Invalid JSON',
|
|
501
|
+
},
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
// Handle batch requests
|
|
505
|
+
if (Array.isArray(parsed)) {
|
|
506
|
+
const responses = await Promise.all(parsed.map((req) => this.handleSingleMessage(req)));
|
|
507
|
+
return JSON.stringify(responses);
|
|
508
|
+
}
|
|
509
|
+
const response = await this.handleSingleMessage(parsed);
|
|
510
|
+
return JSON.stringify(response);
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Handle a single JSON-RPC message
|
|
514
|
+
*/
|
|
515
|
+
async handleSingleMessage(request) {
|
|
516
|
+
const req = request;
|
|
517
|
+
const id = req.id ?? null;
|
|
518
|
+
if (req.jsonrpc !== '2.0') {
|
|
519
|
+
return {
|
|
520
|
+
jsonrpc: '2.0',
|
|
521
|
+
id,
|
|
522
|
+
error: {
|
|
523
|
+
code: MCPSDKErrorCode.INVALID_REQUEST,
|
|
524
|
+
message: 'Invalid Request: missing or invalid jsonrpc version',
|
|
525
|
+
},
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
try {
|
|
529
|
+
switch (req.method) {
|
|
530
|
+
case 'tools/list': {
|
|
531
|
+
const result = await this.handleToolsList(req.params);
|
|
532
|
+
return { jsonrpc: '2.0', id, result };
|
|
533
|
+
}
|
|
534
|
+
case 'tools/call': {
|
|
535
|
+
const result = await this.handleToolsCall(req.params);
|
|
536
|
+
return { jsonrpc: '2.0', id, result };
|
|
537
|
+
}
|
|
538
|
+
default:
|
|
539
|
+
return {
|
|
540
|
+
jsonrpc: '2.0',
|
|
541
|
+
id,
|
|
542
|
+
error: {
|
|
543
|
+
code: MCPSDKErrorCode.METHOD_NOT_FOUND,
|
|
544
|
+
message: `Method not found: ${req.method}`,
|
|
545
|
+
},
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
catch (error) {
|
|
550
|
+
if (error instanceof MCPSDKError) {
|
|
551
|
+
return {
|
|
552
|
+
jsonrpc: '2.0',
|
|
553
|
+
id,
|
|
554
|
+
error: error.toJSONRPC(),
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
return {
|
|
558
|
+
jsonrpc: '2.0',
|
|
559
|
+
id,
|
|
560
|
+
error: {
|
|
561
|
+
code: MCPSDKErrorCode.INTERNAL_ERROR,
|
|
562
|
+
message: error instanceof Error ? error.message : 'Internal error',
|
|
563
|
+
},
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Simulate a pending request (for testing)
|
|
569
|
+
*/
|
|
570
|
+
simulatePendingRequest() {
|
|
571
|
+
const requestId = `sim-req-${++this.currentRequestId}`;
|
|
572
|
+
this.pendingRequests.set(requestId, { cancelled: false });
|
|
573
|
+
return {
|
|
574
|
+
complete: async () => {
|
|
575
|
+
this.pendingRequests.delete(requestId);
|
|
576
|
+
},
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Simulate an internal error (for testing)
|
|
581
|
+
*/
|
|
582
|
+
simulateInternalError(error) {
|
|
583
|
+
const mcpError = new MCPSDKError(MCPSDKErrorCode.INTERNAL_ERROR, error.message);
|
|
584
|
+
for (const listener of this.errorListeners) {
|
|
585
|
+
listener(mcpError);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Send ping
|
|
590
|
+
*/
|
|
591
|
+
sendPing() {
|
|
592
|
+
// Simulate ping/pong
|
|
593
|
+
setTimeout(() => {
|
|
594
|
+
if (this.clientResponsive) {
|
|
595
|
+
for (const listener of this.pongListeners) {
|
|
596
|
+
listener();
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}, 10);
|
|
600
|
+
// Set timeout for pong response
|
|
601
|
+
if (this.config.pingTimeout) {
|
|
602
|
+
this.pingTimeoutId = setTimeout(() => {
|
|
603
|
+
if (!this.clientResponsive) {
|
|
604
|
+
for (const listener of this.connectionTimeoutListeners) {
|
|
605
|
+
listener();
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}, this.config.pingTimeout);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Simulate client becoming unresponsive (for testing)
|
|
613
|
+
*/
|
|
614
|
+
simulateClientUnresponsive() {
|
|
615
|
+
this.clientResponsive = false;
|
|
616
|
+
// Trigger a ping to start the timeout
|
|
617
|
+
this.sendPing();
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Transport factory
|
|
622
|
+
*/
|
|
623
|
+
export const MCPSDKTransport = {
|
|
624
|
+
createStdio(_options) {
|
|
625
|
+
return {
|
|
626
|
+
type: 'stdio',
|
|
627
|
+
isConnected: () => true,
|
|
628
|
+
send: () => { },
|
|
629
|
+
receive: async () => '',
|
|
630
|
+
close: () => { },
|
|
631
|
+
};
|
|
632
|
+
},
|
|
633
|
+
createSSE(_options) {
|
|
634
|
+
let connected = false;
|
|
635
|
+
return {
|
|
636
|
+
type: 'sse',
|
|
637
|
+
isConnected: () => connected,
|
|
638
|
+
send: () => { },
|
|
639
|
+
receive: async () => '',
|
|
640
|
+
close: () => {
|
|
641
|
+
connected = false;
|
|
642
|
+
},
|
|
643
|
+
handleRequest: async (_request) => {
|
|
644
|
+
connected = true;
|
|
645
|
+
return { status: 200, headers: {} };
|
|
646
|
+
},
|
|
647
|
+
};
|
|
648
|
+
},
|
|
649
|
+
createHTTP(_options) {
|
|
650
|
+
return {
|
|
651
|
+
type: 'http',
|
|
652
|
+
isConnected: () => true,
|
|
653
|
+
send: () => { },
|
|
654
|
+
receive: async () => '',
|
|
655
|
+
close: () => { },
|
|
656
|
+
handleRequest: async (_request) => {
|
|
657
|
+
return {
|
|
658
|
+
status: 200,
|
|
659
|
+
headers: { 'Content-Type': 'application/json' },
|
|
660
|
+
body: JSON.stringify({ jsonrpc: '2.0', result: {} }),
|
|
661
|
+
};
|
|
662
|
+
},
|
|
663
|
+
};
|
|
664
|
+
},
|
|
665
|
+
};
|
|
666
|
+
/**
|
|
667
|
+
* Factory function to create an MCP SDK adapter
|
|
668
|
+
*/
|
|
669
|
+
export function createMCPSDKAdapter(config) {
|
|
670
|
+
return new MCPSDKAdapter(config);
|
|
671
|
+
}
|
|
672
|
+
//# sourceMappingURL=sdk-adapter.js.map
|