agent-desk-mcp 1.0.4 → 1.1.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.
Files changed (3) hide show
  1. package/dist/index.js +80 -32
  2. package/package.json +4 -2
  3. package/src/index.ts +85 -33
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({
@@ -157,41 +160,84 @@ Returns: User's answers concatenated with newlines, or timeout message.`,
157
160
  id: q.id || `Q${i + 1}`,
158
161
  }));
159
162
  await this.apiClient.addAgent(sessionId, agent_summary, context, processedQuestions, timeout_seconds);
160
- // Poll for answer
161
- const pollInterval = 1000; // 1 second
162
- const startTime = Date.now();
163
- while (true) {
164
- // Check timeout
165
- if (timeout_seconds && (Date.now() - startTime) >= timeout_seconds * 1000) {
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);
166
172
  console.error(`[${new Date().toISOString()}] TIMEOUT handleAskUser: ${sessionId}`);
167
- return {
168
- content: [
169
- {
170
- type: "text",
171
- text: "User did not respond within the timeout period.",
172
- },
173
- ],
174
- };
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
+ }
175
229
  }
176
- // Check for answer
177
- const agent = await this.apiClient.getAgent(sessionId);
178
- if (agent && agent.status === "answered") {
179
- console.error(`[${new Date().toISOString()}] ANSWERED handleAskUser: ${sessionId}`);
180
- const answers = agent.questions
181
- .filter((q) => q.answer)
182
- .map((q) => `${q.id}: ${q.answer}`);
183
- return {
184
- content: [
185
- {
186
- type: "text",
187
- text: answers.join("\n"),
188
- },
189
- ],
190
- };
230
+ catch (error) {
231
+ console.error("Error processing WebSocket message:", error);
191
232
  }
192
- console.error(`[${new Date().toISOString()}] POLLING handleAskUser: ${sessionId}`);
193
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
194
- }
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
+ });
195
241
  }
196
242
  async run() {
197
243
  this.apiClient = new APIClient(this.apiUrl);
@@ -202,6 +248,8 @@ Returns: User's answers concatenated with newlines, or timeout message.`,
202
248
  console.error("Please ensure the API Server is running.");
203
249
  process.exit(1);
204
250
  }
251
+ // Setup WebSocket connection
252
+ this.setupWebSocket();
205
253
  const transport = new StdioServerTransport();
206
254
  await this.server.connect(transport);
207
255
  console.error("Agent Desk MCP Server started");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-desk-mcp",
3
- "version": "1.0.4",
3
+ "version": "1.1.1",
4
4
  "description": "MCP server for multi-agent interaction with users",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -21,10 +21,12 @@
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
23
  "@modelcontextprotocol/sdk": "^1.0.0",
24
- "axios": "^1.7.0"
24
+ "axios": "^1.7.0",
25
+ "ws": "^8.18.0"
25
26
  },
26
27
  "devDependencies": {
27
28
  "@types/node": "^20.0.0",
29
+ "@types/ws": "^8.5.13",
28
30
  "typescript": "^5.0.0"
29
31
  },
30
32
  "engines": {
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;
@@ -215,44 +218,90 @@ Returns: User's answers concatenated with newlines, or timeout message.`,
215
218
  timeout_seconds
216
219
  );
217
220
 
218
- // Poll for answer
219
- const pollInterval = 1000; // 1 second
220
- const startTime = Date.now();
221
+ // Wait for answer via WebSocket push
222
+ const answerPromise = new Promise<string>((resolve) => {
223
+ this.pendingRequests.set(sessionId, resolve);
224
+ });
221
225
 
222
- while (true) {
223
- // Check timeout
224
- if (timeout_seconds && (Date.now() - startTime) >= timeout_seconds * 1000) {
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);
225
231
  console.error(`[${new Date().toISOString()}] TIMEOUT handleAskUser: ${sessionId}`);
226
- return {
227
- content: [
228
- {
229
- type: "text",
230
- text: "User did not respond within the timeout period.",
231
- },
232
- ],
233
- };
234
- }
232
+ }, timeout_seconds * 1000);
233
+ }
235
234
 
236
- // Check for answer
237
- const agent = await this.apiClient!.getAgent(sessionId);
238
- if (agent && agent.status === "answered") {
239
- console.error(`[${new Date().toISOString()}] ANSWERED handleAskUser: ${sessionId}`);
240
- const answers = agent.questions
241
- .filter((q) => q.answer)
242
- .map((q) => `${q.id}: ${q.answer}`);
243
- return {
244
- content: [
245
- {
246
- type: "text",
247
- text: answers.join("\n"),
248
- },
249
- ],
250
- };
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);
251
294
  }
295
+ });
252
296
 
253
- console.error(`[${new Date().toISOString()}] POLLING handleAskUser: ${sessionId}`);
254
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
255
- }
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
+ });
256
305
  }
257
306
 
258
307
  async run(): Promise<void> {
@@ -266,6 +315,9 @@ Returns: User's answers concatenated with newlines, or timeout message.`,
266
315
  process.exit(1);
267
316
  }
268
317
 
318
+ // Setup WebSocket connection
319
+ this.setupWebSocket();
320
+
269
321
  const transport = new StdioServerTransport();
270
322
  await this.server.connect(transport);
271
323