agent-desk-mcp 1.0.3 → 1.1.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.
- package/dist/index.js +82 -30
- package/package.json +1 -1
- package/src/index.ts +87 -31
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
4
|
import axios from "axios";
|
|
5
|
+
import { WebSocket } from "ws";
|
|
5
6
|
class APIClient {
|
|
6
7
|
client;
|
|
7
8
|
constructor(baseUrl) {
|
|
@@ -44,6 +45,8 @@ class AgentDeskServer {
|
|
|
44
45
|
server;
|
|
45
46
|
apiClient = null;
|
|
46
47
|
apiUrl;
|
|
48
|
+
ws = null;
|
|
49
|
+
pendingRequests = new Map();
|
|
47
50
|
constructor(apiUrl) {
|
|
48
51
|
this.apiUrl = apiUrl;
|
|
49
52
|
this.server = new Server({
|
|
@@ -150,44 +153,91 @@ Returns: User's answers concatenated with newlines, or timeout message.`,
|
|
|
150
153
|
async handleAskUser(args) {
|
|
151
154
|
const { agent_summary, context, questions, timeout_seconds, } = args;
|
|
152
155
|
const sessionId = crypto.randomUUID();
|
|
156
|
+
console.error(`[${new Date().toISOString()}] START handleAskUser: ${sessionId} - ${agent_summary}`);
|
|
153
157
|
// Ensure questions have IDs
|
|
154
158
|
const processedQuestions = questions.map((q, i) => ({
|
|
155
159
|
...q,
|
|
156
160
|
id: q.id || `Q${i + 1}`,
|
|
157
161
|
}));
|
|
158
162
|
await this.apiClient.addAgent(sessionId, agent_summary, context, processedQuestions, timeout_seconds);
|
|
159
|
-
//
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
163
|
+
// Wait for answer via WebSocket push
|
|
164
|
+
const answerPromise = new Promise((resolve) => {
|
|
165
|
+
this.pendingRequests.set(sessionId, resolve);
|
|
166
|
+
});
|
|
167
|
+
// Set up timeout if specified
|
|
168
|
+
let timeoutHandle = null;
|
|
169
|
+
if (timeout_seconds) {
|
|
170
|
+
timeoutHandle = setTimeout(() => {
|
|
171
|
+
this.pendingRequests.delete(sessionId);
|
|
172
|
+
console.error(`[${new Date().toISOString()}] TIMEOUT handleAskUser: ${sessionId}`);
|
|
173
|
+
}, timeout_seconds * 1000);
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const answer = await answerPromise;
|
|
177
|
+
if (timeoutHandle)
|
|
178
|
+
clearTimeout(timeoutHandle);
|
|
179
|
+
console.error(`[${new Date().toISOString()}] ANSWERED handleAskUser: ${sessionId}`);
|
|
180
|
+
return {
|
|
181
|
+
content: [
|
|
182
|
+
{
|
|
183
|
+
type: "text",
|
|
184
|
+
text: answer,
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
if (timeoutHandle)
|
|
191
|
+
clearTimeout(timeoutHandle);
|
|
192
|
+
return {
|
|
193
|
+
content: [
|
|
194
|
+
{
|
|
195
|
+
type: "text",
|
|
196
|
+
text: "User did not respond within the timeout period.",
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
setupWebSocket() {
|
|
203
|
+
const wsUrl = this.apiUrl.replace(/^http/, "ws") + "/ws";
|
|
204
|
+
console.error(`Connecting to WebSocket: ${wsUrl}`);
|
|
205
|
+
this.ws = new WebSocket(wsUrl);
|
|
206
|
+
this.ws.on("open", () => {
|
|
207
|
+
console.error("WebSocket connected");
|
|
208
|
+
});
|
|
209
|
+
this.ws.on("message", (data) => {
|
|
210
|
+
try {
|
|
211
|
+
const message = JSON.parse(data.toString());
|
|
212
|
+
console.error(`WebSocket message received: ${JSON.stringify(message)}`);
|
|
213
|
+
if (message.type === "agent_answered") {
|
|
214
|
+
const sessionId = message.session_id;
|
|
215
|
+
const resolver = this.pendingRequests.get(sessionId);
|
|
216
|
+
if (resolver) {
|
|
217
|
+
// Fetch the full answer from API
|
|
218
|
+
this.apiClient.getAgent(sessionId).then((agent) => {
|
|
219
|
+
if (agent && agent.status === "answered") {
|
|
220
|
+
const answers = agent.questions
|
|
221
|
+
.filter((q) => q.answer)
|
|
222
|
+
.map((q) => `${q.id}: ${q.answer}`);
|
|
223
|
+
resolver(answers.join("\n"));
|
|
224
|
+
this.pendingRequests.delete(sessionId);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
173
229
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (agent && agent.status === "answered") {
|
|
177
|
-
const answers = agent.questions
|
|
178
|
-
.filter((q) => q.answer)
|
|
179
|
-
.map((q) => `${q.id}: ${q.answer}`);
|
|
180
|
-
return {
|
|
181
|
-
content: [
|
|
182
|
-
{
|
|
183
|
-
type: "text",
|
|
184
|
-
text: answers.join("\n"),
|
|
185
|
-
},
|
|
186
|
-
],
|
|
187
|
-
};
|
|
230
|
+
catch (error) {
|
|
231
|
+
console.error("Error processing WebSocket message:", error);
|
|
188
232
|
}
|
|
189
|
-
|
|
190
|
-
|
|
233
|
+
});
|
|
234
|
+
this.ws.on("error", (error) => {
|
|
235
|
+
console.error("WebSocket error:", error);
|
|
236
|
+
});
|
|
237
|
+
this.ws.on("close", () => {
|
|
238
|
+
console.error("WebSocket closed, reconnecting in 5s...");
|
|
239
|
+
setTimeout(() => this.setupWebSocket(), 5000);
|
|
240
|
+
});
|
|
191
241
|
}
|
|
192
242
|
async run() {
|
|
193
243
|
this.apiClient = new APIClient(this.apiUrl);
|
|
@@ -198,6 +248,8 @@ Returns: User's answers concatenated with newlines, or timeout message.`,
|
|
|
198
248
|
console.error("Please ensure the API Server is running.");
|
|
199
249
|
process.exit(1);
|
|
200
250
|
}
|
|
251
|
+
// Setup WebSocket connection
|
|
252
|
+
this.setupWebSocket();
|
|
201
253
|
const transport = new StdioServerTransport();
|
|
202
254
|
await this.server.connect(transport);
|
|
203
255
|
console.error("Agent Desk MCP Server started");
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
ServerCapabilities,
|
|
7
7
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
8
8
|
import axios, { AxiosInstance } from "axios";
|
|
9
|
+
import { WebSocket } from "ws";
|
|
9
10
|
|
|
10
11
|
interface Question {
|
|
11
12
|
id: string;
|
|
@@ -73,6 +74,8 @@ class AgentDeskServer {
|
|
|
73
74
|
private server: Server;
|
|
74
75
|
private apiClient: APIClient | null = null;
|
|
75
76
|
private apiUrl: string;
|
|
77
|
+
private ws: WebSocket | null = null;
|
|
78
|
+
private pendingRequests: Map<string, (answer: string) => void> = new Map();
|
|
76
79
|
|
|
77
80
|
constructor(apiUrl: string) {
|
|
78
81
|
this.apiUrl = apiUrl;
|
|
@@ -199,6 +202,7 @@ Returns: User's answers concatenated with newlines, or timeout message.`,
|
|
|
199
202
|
};
|
|
200
203
|
|
|
201
204
|
const sessionId = crypto.randomUUID();
|
|
205
|
+
console.error(`[${new Date().toISOString()}] START handleAskUser: ${sessionId} - ${agent_summary}`);
|
|
202
206
|
|
|
203
207
|
// Ensure questions have IDs
|
|
204
208
|
const processedQuestions = questions.map((q, i) => ({
|
|
@@ -214,41 +218,90 @@ Returns: User's answers concatenated with newlines, or timeout message.`,
|
|
|
214
218
|
timeout_seconds
|
|
215
219
|
);
|
|
216
220
|
|
|
217
|
-
//
|
|
218
|
-
const
|
|
219
|
-
|
|
221
|
+
// Wait for answer via WebSocket push
|
|
222
|
+
const answerPromise = new Promise<string>((resolve) => {
|
|
223
|
+
this.pendingRequests.set(sessionId, resolve);
|
|
224
|
+
});
|
|
220
225
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
},
|
|
230
|
-
],
|
|
231
|
-
};
|
|
232
|
-
}
|
|
226
|
+
// Set up timeout if specified
|
|
227
|
+
let timeoutHandle: NodeJS.Timeout | null = null;
|
|
228
|
+
if (timeout_seconds) {
|
|
229
|
+
timeoutHandle = setTimeout(() => {
|
|
230
|
+
this.pendingRequests.delete(sessionId);
|
|
231
|
+
console.error(`[${new Date().toISOString()}] TIMEOUT handleAskUser: ${sessionId}`);
|
|
232
|
+
}, timeout_seconds * 1000);
|
|
233
|
+
}
|
|
233
234
|
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
235
|
+
try {
|
|
236
|
+
const answer = await answerPromise;
|
|
237
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
238
|
+
console.error(`[${new Date().toISOString()}] ANSWERED handleAskUser: ${sessionId}`);
|
|
239
|
+
return {
|
|
240
|
+
content: [
|
|
241
|
+
{
|
|
242
|
+
type: "text",
|
|
243
|
+
text: answer,
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
};
|
|
247
|
+
} catch (error) {
|
|
248
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
249
|
+
return {
|
|
250
|
+
content: [
|
|
251
|
+
{
|
|
252
|
+
type: "text",
|
|
253
|
+
text: "User did not respond within the timeout period.",
|
|
254
|
+
},
|
|
255
|
+
],
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
private setupWebSocket(): void {
|
|
261
|
+
const wsUrl = this.apiUrl.replace(/^http/, "ws") + "/ws";
|
|
262
|
+
console.error(`Connecting to WebSocket: ${wsUrl}`);
|
|
263
|
+
|
|
264
|
+
this.ws = new WebSocket(wsUrl);
|
|
265
|
+
|
|
266
|
+
this.ws.on("open", () => {
|
|
267
|
+
console.error("WebSocket connected");
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
this.ws.on("message", (data: Buffer) => {
|
|
271
|
+
try {
|
|
272
|
+
const message = JSON.parse(data.toString());
|
|
273
|
+
console.error(`WebSocket message received: ${JSON.stringify(message)}`);
|
|
274
|
+
|
|
275
|
+
if (message.type === "agent_answered") {
|
|
276
|
+
const sessionId = message.session_id;
|
|
277
|
+
const resolver = this.pendingRequests.get(sessionId);
|
|
278
|
+
|
|
279
|
+
if (resolver) {
|
|
280
|
+
// Fetch the full answer from API
|
|
281
|
+
this.apiClient!.getAgent(sessionId).then((agent) => {
|
|
282
|
+
if (agent && agent.status === "answered") {
|
|
283
|
+
const answers = agent.questions
|
|
284
|
+
.filter((q) => q.answer)
|
|
285
|
+
.map((q) => `${q.id}: ${q.answer}`);
|
|
286
|
+
resolver(answers.join("\n"));
|
|
287
|
+
this.pendingRequests.delete(sessionId);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.error("Error processing WebSocket message:", error);
|
|
248
294
|
}
|
|
295
|
+
});
|
|
249
296
|
|
|
250
|
-
|
|
251
|
-
|
|
297
|
+
this.ws.on("error", (error) => {
|
|
298
|
+
console.error("WebSocket error:", error);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
this.ws.on("close", () => {
|
|
302
|
+
console.error("WebSocket closed, reconnecting in 5s...");
|
|
303
|
+
setTimeout(() => this.setupWebSocket(), 5000);
|
|
304
|
+
});
|
|
252
305
|
}
|
|
253
306
|
|
|
254
307
|
async run(): Promise<void> {
|
|
@@ -262,6 +315,9 @@ Returns: User's answers concatenated with newlines, or timeout message.`,
|
|
|
262
315
|
process.exit(1);
|
|
263
316
|
}
|
|
264
317
|
|
|
318
|
+
// Setup WebSocket connection
|
|
319
|
+
this.setupWebSocket();
|
|
320
|
+
|
|
265
321
|
const transport = new StdioServerTransport();
|
|
266
322
|
await this.server.connect(transport);
|
|
267
323
|
|