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.
Files changed (3) hide show
  1. package/dist/index.js +82 -30
  2. package/package.json +1 -1
  3. 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
- // Poll for answer
160
- const pollInterval = 1000; // 1 second
161
- const startTime = Date.now();
162
- while (true) {
163
- // Check timeout
164
- if (timeout_seconds && (Date.now() - startTime) >= timeout_seconds * 1000) {
165
- return {
166
- content: [
167
- {
168
- type: "text",
169
- text: "User did not respond within the timeout period.",
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
- // Check for answer
175
- const agent = await this.apiClient.getAgent(sessionId);
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
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-desk-mcp",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "MCP server for multi-agent interaction with users",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
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
- // Poll for answer
218
- const pollInterval = 1000; // 1 second
219
- 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
+ });
220
225
 
221
- while (true) {
222
- // Check timeout
223
- if (timeout_seconds && (Date.now() - startTime) >= timeout_seconds * 1000) {
224
- return {
225
- content: [
226
- {
227
- type: "text",
228
- text: "User did not respond within the timeout period.",
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
- // Check for answer
235
- const agent = await this.apiClient!.getAgent(sessionId);
236
- if (agent && agent.status === "answered") {
237
- const answers = agent.questions
238
- .filter((q) => q.answer)
239
- .map((q) => `${q.id}: ${q.answer}`);
240
- return {
241
- content: [
242
- {
243
- type: "text",
244
- text: answers.join("\n"),
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
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
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