owenkaplinsky commited on
Commit
a0c3289
·
1 Parent(s): 67dfe8d

Add MCP block in/out tool

Browse files
project/chat.py CHANGED
@@ -39,6 +39,10 @@ creation_results = {}
39
  variable_queue = queue.Queue()
40
  variable_results = {}
41
 
 
 
 
 
42
  # Global variable to store the deployed HF MCP server URL
43
  current_mcp_server_url = None
44
 
@@ -223,6 +227,53 @@ def create_variable(var_name):
223
  traceback.print_exc()
224
  return f"Error creating variable: {str(e)}"
225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  # Unified Server-Sent Events endpoint for all workspace operations
227
  @app.get("/unified_stream")
228
  async def unified_stream():
@@ -294,6 +345,24 @@ async def unified_stream():
294
  else:
295
  print(f"[SSE SKIP] Skipping duplicate request for ID: {request_id}")
296
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  else:
298
  # Send a heartbeat every 30 seconds to keep connection alive
299
  heartbeat_counter += 1
@@ -374,6 +443,24 @@ async def variable_result(request: Request):
374
 
375
  return {"received": True}
376
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
377
  def deploy_to_huggingface(space_name):
378
  """Deploy the generated MCP code to a Hugging Face Space"""
379
  global stored_hf_key
@@ -665,6 +752,38 @@ Note: Users can see tool response outputs verbatim. You don't have to repeat the
665
  "required": ["name"],
666
  }
667
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
668
  {
669
  "type": "function",
670
  "name": "deploy_to_huggingface",
@@ -885,6 +1004,13 @@ Note: Users can see tool response outputs verbatim. You don't have to repeat the
885
  tool_result = create_variable(name)
886
  result_label = "Create Var Operation"
887
 
 
 
 
 
 
 
 
888
  elif function_name == "deploy_to_huggingface":
889
  space_name = function_args.get("space_name", "")
890
  print(Fore.YELLOW + f"Agent deploying to Hugging Face Space `{space_name}`." + Style.RESET_ALL)
 
39
  variable_queue = queue.Queue()
40
  variable_results = {}
41
 
42
+ # Queue for edit MCP requests and results storage
43
+ edit_mcp_queue = queue.Queue()
44
+ edit_mcp_results = {}
45
+
46
  # Global variable to store the deployed HF MCP server URL
47
  current_mcp_server_url = None
48
 
 
227
  traceback.print_exc()
228
  return f"Error creating variable: {str(e)}"
229
 
230
+ def edit_mcp(inputs=None, outputs=None):
231
+ """Edit the inputs and outputs of the create_mcp block"""
232
+ try:
233
+ print(f"[EDIT MCP REQUEST] Attempting to edit MCP block: inputs={inputs}, outputs={outputs}")
234
+
235
+ # Generate a unique request ID
236
+ request_id = str(uuid.uuid4())
237
+
238
+ # Clear any old results for this request ID first
239
+ if request_id in edit_mcp_results:
240
+ edit_mcp_results.pop(request_id)
241
+
242
+ # Build the edit data
243
+ edit_data = {"request_id": request_id}
244
+ if inputs is not None:
245
+ edit_data["inputs"] = inputs
246
+ if outputs is not None:
247
+ edit_data["outputs"] = outputs
248
+
249
+ # Add to edit MCP queue
250
+ edit_mcp_queue.put(edit_data)
251
+ print(f"[EDIT MCP REQUEST] Added to queue with ID: {request_id}")
252
+
253
+ # Wait for result with timeout
254
+ timeout = 8 # 8 seconds timeout
255
+ start_time = time.time()
256
+ check_interval = 0.05 # Check more frequently
257
+
258
+ while time.time() - start_time < timeout:
259
+ if request_id in edit_mcp_results:
260
+ result = edit_mcp_results.pop(request_id)
261
+ print(f"[EDIT MCP RESULT] Received result for {request_id}: success={result.get('success')}, error={result.get('error')}")
262
+ if result["success"]:
263
+ return f"[TOOL] Successfully edited MCP block inputs/outputs"
264
+ else:
265
+ return f"[TOOL] Failed to edit MCP block: {result.get('error', 'Unknown error')}"
266
+ time.sleep(check_interval)
267
+
268
+ print(f"[EDIT MCP TIMEOUT] No response received for request {request_id} after {timeout} seconds")
269
+ return f"Timeout waiting for MCP edit confirmation"
270
+
271
+ except Exception as e:
272
+ print(f"[EDIT MCP ERROR] {e}")
273
+ import traceback
274
+ traceback.print_exc()
275
+ return f"Error editing MCP block: {str(e)}"
276
+
277
  # Unified Server-Sent Events endpoint for all workspace operations
278
  @app.get("/unified_stream")
279
  async def unified_stream():
 
345
  else:
346
  print(f"[SSE SKIP] Skipping duplicate request for ID: {request_id}")
347
 
348
+ # Check edit MCP queue
349
+ elif not edit_mcp_queue.empty():
350
+ edit_request = edit_mcp_queue.get_nowait()
351
+ request_id = edit_request.get("request_id")
352
+ request_key = f"edit_mcp_{request_id}"
353
+
354
+ # Avoid sending duplicate requests too quickly
355
+ if request_key not in sent_requests:
356
+ sent_requests.add(request_key)
357
+ edit_request["type"] = "edit_mcp" # Add type identifier
358
+ print(f"[SSE SEND] Sending edit MCP request with ID: {request_id}")
359
+ yield f"data: {json.dumps(edit_request)}\n\n"
360
+
361
+ # Clear from sent_requests after 10 seconds
362
+ asyncio.create_task(clear_sent_request(sent_requests, request_key, 10))
363
+ else:
364
+ print(f"[SSE SKIP] Skipping duplicate request for ID: {request_id}")
365
+
366
  else:
367
  # Send a heartbeat every 30 seconds to keep connection alive
368
  heartbeat_counter += 1
 
443
 
444
  return {"received": True}
445
 
446
+ # Endpoint to receive edit MCP results from frontend
447
+ @app.post("/edit_mcp_result")
448
+ async def edit_mcp_result(request: Request):
449
+ """Receive edit MCP results from the frontend"""
450
+ data = await request.json()
451
+ request_id = data.get("request_id")
452
+ success = data.get("success")
453
+ error = data.get("error")
454
+
455
+ print(f"[EDIT MCP RESULT RECEIVED] request_id={request_id}, success={success}, error={error}")
456
+
457
+ if request_id:
458
+ # Store the result for the edit_mcp function to retrieve
459
+ edit_mcp_results[request_id] = data
460
+ print(f"[EDIT MCP RESULT STORED] Results dict now has {len(edit_mcp_results)} items")
461
+
462
+ return {"received": True}
463
+
464
  def deploy_to_huggingface(space_name):
465
  """Deploy the generated MCP code to a Hugging Face Space"""
466
  global stored_hf_key
 
752
  "required": ["name"],
753
  }
754
  },
755
+ {
756
+ "type": "function",
757
+ "name": "edit_mcp",
758
+ "description": "Edit the inputs and outputs of the create_mcp block.",
759
+ "parameters": {
760
+ "type": "object",
761
+ "properties": {
762
+ "inputs": {
763
+ "type": "array",
764
+ "items": {
765
+ "type": "object",
766
+ "properties": {
767
+ "name": { "type": "string" },
768
+ "type": { "type": "string", "enum": ["string", "integer", "list"] }
769
+ },
770
+ "required": ["name", "type"]
771
+ }
772
+ },
773
+ "outputs": {
774
+ "type": "array",
775
+ "items": {
776
+ "type": "object",
777
+ "properties": {
778
+ "name": { "type": "string" },
779
+ "type": { "type": "string", "enum": ["string", "integer", "list"] }
780
+ },
781
+ "required": ["name", "type"]
782
+ }
783
+ }
784
+ }
785
+ }
786
+ },
787
  {
788
  "type": "function",
789
  "name": "deploy_to_huggingface",
 
1004
  tool_result = create_variable(name)
1005
  result_label = "Create Var Operation"
1006
 
1007
+ elif function_name == "edit_mcp":
1008
+ inputs = function_args.get("inputs", None)
1009
+ outputs = function_args.get("outputs", None)
1010
+ print(Fore.YELLOW + f"Agent editing MCP block: inputs={inputs}, outputs={outputs}." + Style.RESET_ALL)
1011
+ tool_result = edit_mcp(inputs, outputs)
1012
+ result_label = "Edit MCP Operation"
1013
+
1014
  elif function_name == "deploy_to_huggingface":
1015
  space_name = function_args.get("space_name", "")
1016
  print(Fore.YELLOW + f"Agent deploying to Hugging Face Space `{space_name}`." + Style.RESET_ALL)
project/src/index.js CHANGED
@@ -243,6 +243,8 @@ const setupUnifiedStream = () => {
243
  requestKey = `create_${data.request_id}`;
244
  } else if (data.type === 'variable') {
245
  requestKey = `variable_${data.request_id}`;
 
 
246
  }
247
 
248
  // Skip if we've already processed this request
@@ -256,8 +258,91 @@ const setupUnifiedStream = () => {
256
  setTimeout(() => processedRequests.delete(requestKey), 10000);
257
  }
258
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  // Handle deletion requests
260
- if (data.type === 'delete' && data.block_id) {
261
  console.log('[SSE] Received deletion request for block:', data.block_id);
262
 
263
  // Try to delete the block
 
243
  requestKey = `create_${data.request_id}`;
244
  } else if (data.type === 'variable') {
245
  requestKey = `variable_${data.request_id}`;
246
+ } else if (data.type === 'edit_mcp') {
247
+ requestKey = `edit_mcp_${data.request_id}`;
248
  }
249
 
250
  // Skip if we've already processed this request
 
258
  setTimeout(() => processedRequests.delete(requestKey), 10000);
259
  }
260
 
261
+ // Handle edit MCP requests
262
+ if (data.type === 'edit_mcp' && data.request_id) {
263
+ console.log('[SSE] Received edit MCP request:', data);
264
+
265
+ let success = false;
266
+ let error = null;
267
+
268
+ try {
269
+ // Find the create_mcp block
270
+ const mcpBlocks = ws.getBlocksByType('create_mcp');
271
+ const mcpBlock = mcpBlocks[0];
272
+
273
+ if (!mcpBlock) {
274
+ throw new Error('No create_mcp block found in workspace');
275
+ }
276
+
277
+ // Disable events to prevent infinite loops
278
+ Blockly.Events.disable();
279
+
280
+ try {
281
+ // Create a container block for the mutator
282
+ const containerBlock = ws.newBlock('container');
283
+ containerBlock.initSvg();
284
+
285
+ // Build inputs if provided
286
+ if (data.inputs && Array.isArray(data.inputs)) {
287
+ let connection = containerBlock.getInput('STACK').connection;
288
+ for (let idx = 0; idx < data.inputs.length; idx++) {
289
+ const input = data.inputs[idx];
290
+ const itemBlock = ws.newBlock('container_input');
291
+ itemBlock.initSvg();
292
+ itemBlock.setFieldValue(input.type || 'string', 'TYPE');
293
+ itemBlock.setFieldValue(input.name || '', 'NAME');
294
+ connection.connect(itemBlock.previousConnection);
295
+ connection = itemBlock.nextConnection;
296
+ }
297
+ }
298
+
299
+ // Build outputs if provided
300
+ if (data.outputs && Array.isArray(data.outputs)) {
301
+ let connection2 = containerBlock.getInput('STACK2').connection;
302
+ for (let idx = 0; idx < data.outputs.length; idx++) {
303
+ const output = data.outputs[idx];
304
+ const itemBlock = ws.newBlock('container_output');
305
+ itemBlock.initSvg();
306
+ itemBlock.setFieldValue(output.type || 'string', 'TYPE');
307
+ itemBlock.setFieldValue(output.name || 'output', 'NAME');
308
+ connection2.connect(itemBlock.previousConnection);
309
+ connection2 = itemBlock.nextConnection;
310
+ }
311
+ }
312
+
313
+ // Apply changes using the compose method
314
+ mcpBlock.compose(containerBlock);
315
+
316
+ // Clean up
317
+ containerBlock.dispose();
318
+ success = true;
319
+ console.log('[SSE] Successfully edited MCP block');
320
+ } finally {
321
+ Blockly.Events.enable();
322
+ }
323
+ } catch (e) {
324
+ error = e.toString();
325
+ console.error('[SSE] Error editing MCP block:', e);
326
+ }
327
+
328
+ // Send result back to backend immediately
329
+ console.log('[SSE] Sending edit MCP result:', { request_id: data.request_id, success, error });
330
+ fetch('/edit_mcp_result', {
331
+ method: 'POST',
332
+ headers: { 'Content-Type': 'application/json' },
333
+ body: JSON.stringify({
334
+ request_id: data.request_id,
335
+ success: success,
336
+ error: error
337
+ })
338
+ }).then(response => {
339
+ console.log('[SSE] Edit MCP result sent successfully');
340
+ }).catch(err => {
341
+ console.error('[SSE] Error sending edit MCP result:', err);
342
+ });
343
+ }
344
  // Handle deletion requests
345
+ else if (data.type === 'delete' && data.block_id) {
346
  console.log('[SSE] Received deletion request for block:', data.block_id);
347
 
348
  // Try to delete the block
project/unified_server.py CHANGED
@@ -50,6 +50,10 @@ async def deletion_result_route(request: Request):
50
  async def variable_result_route(request: Request):
51
  return await chat.variable_result(request)
52
 
 
 
 
 
53
 
54
  # === test.py API endpoints ===
55
  @app.post("/update_code")
 
50
  async def variable_result_route(request: Request):
51
  return await chat.variable_result(request)
52
 
53
+ @app.post("/edit_mcp_result")
54
+ async def edit_mcp_result_route(request: Request):
55
+ return await chat.edit_mcp_result(request)
56
+
57
 
58
  # === test.py API endpoints ===
59
  @app.post("/update_code")