owenkaplinsky commited on
Commit
c0bd4ef
·
1 Parent(s): 59cfebc

Fix bug; improve prompt

Browse files
Files changed (2) hide show
  1. project/chat.py +112 -108
  2. project/src/index.js +27 -17
project/chat.py CHANGED
@@ -135,13 +135,13 @@ def delete_block(block_id):
135
  traceback.print_exc()
136
  return f"Error deleting block: {str(e)}"
137
 
138
- def create_block(block_spec, under_block_id=None, input_name=None):
139
  try:
140
  print(f"[CREATE REQUEST] Attempting to create block: {block_spec}")
141
- if under_block_id:
142
- print(f"[CREATE REQUEST] Under block ID: {under_block_id}")
143
  if input_name:
144
- print(f"[CREATE REQUEST] Into input: {input_name}")
145
 
146
  # Generate a unique request ID
147
  import uuid
@@ -151,10 +151,12 @@ def create_block(block_spec, under_block_id=None, input_name=None):
151
  if request_id in creation_results:
152
  creation_results.pop(request_id)
153
 
154
- # Add to creation queue with optional under_block_id and input_name
155
  queue_data = {"request_id": request_id, "block_spec": block_spec}
156
- if under_block_id:
157
- queue_data["under_block_id"] = under_block_id
 
 
158
  if input_name:
159
  queue_data["input_name"] = input_name
160
  creation_queue.put(queue_data)
@@ -169,11 +171,11 @@ def create_block(block_spec, under_block_id=None, input_name=None):
169
  while time.time() - start_time < timeout:
170
  if request_id in creation_results:
171
  result = creation_results.pop(request_id)
172
- print(f"[CREATE RESULT] Received result for {request_id}: success={result.get('success')}, error={result.get('error')}")
173
  if result["success"]:
174
  return f"[TOOL] Successfully created block: {result.get('block_id', 'unknown')}"
175
  else:
176
- return f"[TOOL] Failed to create block: {result.get('error', 'Unknown error')}"
 
177
  time.sleep(check_interval)
178
 
179
  print(f"[CREATE TIMEOUT] No response received for request {request_id} after {timeout} seconds")
@@ -296,7 +298,6 @@ async def unified_stream():
296
  if request_key not in sent_requests:
297
  sent_requests.add(request_key)
298
  deletion_request["type"] = "delete" # Add type identifier
299
- print(f"[SSE SEND] Sending deletion request for block: {block_id}")
300
  yield f"data: {json.dumps(deletion_request)}\n\n"
301
 
302
  # Clear from sent_requests after 10 seconds
@@ -314,7 +315,6 @@ async def unified_stream():
314
  if request_key not in sent_requests:
315
  sent_requests.add(request_key)
316
  creation_request["type"] = "create" # Add type identifier
317
- print(f"[SSE SEND] Sending creation request with ID: {request_id}")
318
  yield f"data: {json.dumps(creation_request)}\n\n"
319
 
320
  # Clear from sent_requests after 10 seconds
@@ -332,7 +332,6 @@ async def unified_stream():
332
  if request_key not in sent_requests:
333
  sent_requests.add(request_key)
334
  variable_request["type"] = "variable" # Add type identifier
335
- print(f"[SSE SEND] Sending variable creation request with ID: {request_id}")
336
  yield f"data: {json.dumps(variable_request)}\n\n"
337
 
338
  # Clear from sent_requests after 10 seconds
@@ -350,7 +349,6 @@ async def unified_stream():
350
  if request_key not in sent_requests:
351
  sent_requests.add(request_key)
352
  edit_request["type"] = "edit_mcp" # Add type identifier
353
- print(f"[SSE SEND] Sending edit MCP request with ID: {request_id}")
354
  yield f"data: {json.dumps(edit_request)}\n\n"
355
 
356
  # Clear from sent_requests after 10 seconds
@@ -387,16 +385,10 @@ async def unified_stream():
387
  async def creation_result(request: Request):
388
  data = await request.json()
389
  request_id = data.get("request_id")
390
- success = data.get("success")
391
- error = data.get("error")
392
- block_id = data.get("block_id")
393
-
394
- print(f"[CREATION RESULT RECEIVED] request_id={request_id}, success={success}, error={error}, block_id={block_id}")
395
-
396
  if request_id:
397
  # Store the result for the create_block function to retrieve
398
  creation_results[request_id] = data
399
- print(f"[CREATION RESULT STORED] Results dict now has {len(creation_results)} items")
400
 
401
  return {"received": True}
402
 
@@ -443,12 +435,9 @@ async def edit_mcp_result(request: Request):
443
  success = data.get("success")
444
  error = data.get("error")
445
 
446
- print(f"[EDIT MCP RESULT RECEIVED] request_id={request_id}, success={success}, error={error}")
447
-
448
  if request_id:
449
  # Store the result for the edit_mcp function to retrieve
450
  edit_mcp_results[request_id] = data
451
- print(f"[EDIT MCP RESULT STORED] Results dict now has {len(edit_mcp_results)} items")
452
 
453
  return {"received": True}
454
 
@@ -564,17 +553,16 @@ def create_gradio_interface():
564
  # Hardcoded system prompt
565
 
566
  SYSTEM_PROMPT = f"""You are an AI assistant that helps users build **MCP servers** using Blockly blocks.
567
- MCP lets AI systems define tools with specific inputs and outputs that any LLM can call.
568
 
569
  You'll receive the workspace state in this format:
570
  `blockId | block_name(inputs(input_name: value))`
571
 
572
- **Special cases:**
 
 
 
573
  - `create_mcp` and `func_def` use `blockId | block_name(inputs(input_name: type), outputs(output_name: value))`
574
  - Indentation or nesting shows logic hierarchy (like loops or conditionals).
575
-
576
- Note that the `blockId` before the pipe `|` is each block's unique identifier. The ID can have a | in it. But,
577
- the real separator will always have a space before and after it.
578
 
579
  ---
580
 
@@ -613,52 +601,53 @@ def create_gradio_interface():
613
 
614
  {blocks_context}
615
 
616
- You can create new blocks by specifying the block type and its inputs, if any.
617
- You cannot create a `create_mcp` block, but you may edit its inputs using the dedicated tool.
618
-
619
- ### Placing Blocks in MCP Inputs
620
- You can place blocks directly into the MCP block's inputs using the `input` parameter:
621
- - Use `input: "X0"`, `input: "X1"`, etc. to place a block into an input slot
622
- - Use `input: "R0"`, `input: "R1"`, etc. to place a block into an output slot
623
- - **This feature can ONLY be used with the MCP block** - you cannot place blocks into inputs of other blocks this way
624
- - The block will replace whatever is currently in that input
625
-
626
- Example: Create a text block and put it in the MCP's first input:
627
- `text(inputs(TEXT: "hello"))` with `input: "X0"`
628
 
629
- There are two kinds of nesting:
630
-
631
- 1. **Statement-level nesting (main-level blocks)**
632
- These include loops, conditionals, and other blocks that contain statements.
633
- - Create the outer block first.
634
- - Then create the inner block using the `under` parameter.
635
-
636
- **A statement-level block must always have an explicit placement.**
637
- - If it is top-level, state that it is top-level.
638
- - If it belongs inside another block, include the `under` parameter with the parent block ID.
639
- - Never create a statement-level block without placement.
640
- - If the parent block ID is not yet available, wait and do not create the child.
641
-
642
- 2. **Value-level nesting (output blocks)**
643
- These produce values and must be nested inside another block's input.
644
- They must be fully nested in a *single* create call.
645
- Example:
646
- `math_arithmetic(inputs(A: math_number(inputs(NUM: 1)), B: math_number(inputs(NUM: 1))))`
647
-
648
- Rules for all value blocks:
649
- - No raw strings, numbers, booleans, or values.
650
- - Strings must use a `text` block.
651
- - Numbers must use `math_number`.
652
- - Any value input must contain a block, never a raw value.
653
-
654
- For blocks with infinite inputs (...N), inputs may be omitted.
655
 
656
- You cannot put a new value-outputting block into an existing input after creation; if a value structure is needed, build the entire nested structure in the same call.
657
 
658
- For stackable (top/bottom connection) blocks:
659
- - Create one
660
- - Wait for the ID
661
- - Then create the next block to connect underneath using `under`.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662
 
663
  You may NEVER create functions. You must only use code inside the MCP block itself.
664
 
@@ -681,40 +670,52 @@ def create_gradio_interface():
681
 
682
  ---
683
 
684
- Users can see tool responses verbatim. Responses do not need to repeat tool output.
685
 
686
- ---
687
 
688
- ## REQUIRED PLANNING PHASE BEFORE ANY TOOL CALL
689
 
690
- Before creating or deleting any blocks, always begin with a *Planning Phase*:
691
 
692
- 1. **Analyze the user's request and outline the full logic needed.**
693
- - Identify required inputs
694
- - Identify required outputs
695
- - Identify intermediate computations
696
- - Identify loops, conditionals, and sub-logic
697
 
698
- 2. **Produce a step-by-step construction plan** from outermost structures to nested value blocks.
699
- Include:
700
- - Required block types
701
- - Which blocks are top-level
702
- - Which blocks must be nested
703
- - Which must be created via single-call value nesting
704
- - The order of tool calls
705
- - Where each block will be placed
706
 
707
- 3. Create a pseudocode plan for exactly how you will implement it.
 
 
708
 
709
- 4. Create a block-creation sequence that builds the complete structure in a single pass, starting with all outer blocks, then adding inner statement blocks, and finally generating fully nested value blocks, without revising or inserting blocks later.
 
 
 
710
 
711
- 5. Perform the actions. Do not ask for approval or permission.
712
 
713
- If a user request arrives without an existing plan, enter the Planning Phase first.
 
 
 
714
 
715
  ---
716
 
717
- Tool responses appear under the assistant role, but they are not part of your own words. Never treat tool output as something you said, and never fabricate or echo fake tool outputs.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
718
  """
719
 
720
  tools = [
@@ -744,13 +745,18 @@ def create_gradio_interface():
744
  "type": "string",
745
  "description": "The create block command using the custom DSL format.",
746
  },
747
- "under": {
 
 
 
 
748
  "type": "string",
749
- "description": "The ID of the block that you want to place this under.",
 
750
  },
751
- "input": {
752
  "type": "string",
753
- "description": "The input name of the MCP block to place this block inside (e.g., 'X0', 'R0'). Can only be used with the MCP block.",
754
  },
755
  },
756
  "required": ["command"],
@@ -850,9 +856,6 @@ def create_gradio_interface():
850
  input_items.append({"role": "user", "content": human})
851
  input_items.append({"role": "assistant", "content": ai})
852
 
853
- # Debug
854
- print(f"[DEBUG] Context received: {context if context else 'No context available'}")
855
-
856
  # Build instructions
857
  instructions = SYSTEM_PROMPT
858
  if context:
@@ -1009,15 +1012,16 @@ def create_gradio_interface():
1009
 
1010
  elif function_name == "create_block":
1011
  command = function_args.get("command", "")
1012
- under_block_id = function_args.get("under", None)
1013
- input_name = function_args.get("input", None)
1014
- if under_block_id is None and input_name is None:
 
1015
  print(Fore.YELLOW + f"Agent created block with command `{command}`." + Style.RESET_ALL)
1016
- elif under_block_id:
1017
- print(Fore.YELLOW + f"Agent created block with command `{command}`, under block ID `{under_block_id}`." + Style.RESET_ALL)
1018
- elif input_name:
1019
- print(Fore.YELLOW + f"Agent created block with command `{command}`, into input `{input_name}`." + Style.RESET_ALL)
1020
- tool_result = create_block(command, under_block_id, input_name)
1021
  result_label = "Create Operation"
1022
 
1023
  elif function_name == "create_variable":
 
135
  traceback.print_exc()
136
  return f"Error deleting block: {str(e)}"
137
 
138
+ def create_block(block_spec, blockID=None, placement_type=None, input_name=None):
139
  try:
140
  print(f"[CREATE REQUEST] Attempting to create block: {block_spec}")
141
+ if blockID:
142
+ print(f"[CREATE REQUEST] Placement type: {placement_type}, block ID: {blockID}")
143
  if input_name:
144
+ print(f"[CREATE REQUEST] Input name: {input_name}")
145
 
146
  # Generate a unique request ID
147
  import uuid
 
151
  if request_id in creation_results:
152
  creation_results.pop(request_id)
153
 
154
+ # Add to creation queue with optional blockID, placement_type, and input_name
155
  queue_data = {"request_id": request_id, "block_spec": block_spec}
156
+ if blockID:
157
+ queue_data["blockID"] = blockID
158
+ if placement_type:
159
+ queue_data["placement_type"] = placement_type
160
  if input_name:
161
  queue_data["input_name"] = input_name
162
  creation_queue.put(queue_data)
 
171
  while time.time() - start_time < timeout:
172
  if request_id in creation_results:
173
  result = creation_results.pop(request_id)
 
174
  if result["success"]:
175
  return f"[TOOL] Successfully created block: {result.get('block_id', 'unknown')}"
176
  else:
177
+ error_msg = result.get('error') or 'Unknown error'
178
+ return f"[TOOL] Failed to create block: {error_msg}"
179
  time.sleep(check_interval)
180
 
181
  print(f"[CREATE TIMEOUT] No response received for request {request_id} after {timeout} seconds")
 
298
  if request_key not in sent_requests:
299
  sent_requests.add(request_key)
300
  deletion_request["type"] = "delete" # Add type identifier
 
301
  yield f"data: {json.dumps(deletion_request)}\n\n"
302
 
303
  # Clear from sent_requests after 10 seconds
 
315
  if request_key not in sent_requests:
316
  sent_requests.add(request_key)
317
  creation_request["type"] = "create" # Add type identifier
 
318
  yield f"data: {json.dumps(creation_request)}\n\n"
319
 
320
  # Clear from sent_requests after 10 seconds
 
332
  if request_key not in sent_requests:
333
  sent_requests.add(request_key)
334
  variable_request["type"] = "variable" # Add type identifier
 
335
  yield f"data: {json.dumps(variable_request)}\n\n"
336
 
337
  # Clear from sent_requests after 10 seconds
 
349
  if request_key not in sent_requests:
350
  sent_requests.add(request_key)
351
  edit_request["type"] = "edit_mcp" # Add type identifier
 
352
  yield f"data: {json.dumps(edit_request)}\n\n"
353
 
354
  # Clear from sent_requests after 10 seconds
 
385
  async def creation_result(request: Request):
386
  data = await request.json()
387
  request_id = data.get("request_id")
388
+
 
 
 
 
 
389
  if request_id:
390
  # Store the result for the create_block function to retrieve
391
  creation_results[request_id] = data
 
392
 
393
  return {"received": True}
394
 
 
435
  success = data.get("success")
436
  error = data.get("error")
437
 
 
 
438
  if request_id:
439
  # Store the result for the edit_mcp function to retrieve
440
  edit_mcp_results[request_id] = data
 
441
 
442
  return {"received": True}
443
 
 
553
  # Hardcoded system prompt
554
 
555
  SYSTEM_PROMPT = f"""You are an AI assistant that helps users build **MCP servers** using Blockly blocks.
 
556
 
557
  You'll receive the workspace state in this format:
558
  `blockId | block_name(inputs(input_name: value))`
559
 
560
+ Block ID parsing: Block IDs are everything before ` | ` (space-pipe-space). IDs are always complex/long strings.
561
+ Example: `?fHZRh^|us|9bECO![$= | text(inputs(TEXT: "hello"))`, ID is `?fHZRh^|us|9bECO![$=`
562
+
563
+ Special cases:
564
  - `create_mcp` and `func_def` use `blockId | block_name(inputs(input_name: type), outputs(output_name: value))`
565
  - Indentation or nesting shows logic hierarchy (like loops or conditionals).
 
 
 
566
 
567
  ---
568
 
 
601
 
602
  {blocks_context}
603
 
604
+ You cannot create a `create_mcp` block, but you may edit its inputs using the `edit_mcp` tool.
 
 
 
 
 
 
 
 
 
 
 
605
 
606
+ ### Block Types: Statement vs Value
607
+ **Statement blocks** are containers that hold other blocks inside them. They can have blocks above or below them:
608
+ - Loops (repeat, while, for)
609
+ - Conditionals (if/else)
610
+ - Any block that wraps other blocks
611
+
612
+ **Value blocks** produce a value and plug into another block's input:
613
+ - Math blocks (math_number, math_arithmetic)
614
+ - Text blocks (text, text_join)
615
+ - Logic blocks (logic_compare, logic_operation)
616
+ - Any block that outputs a result for something else to use
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
617
 
618
+ ### How to Place Blocks
619
 
620
+ **Placement types** - use `blockID` and `type` parameters:
621
+
622
+ - `type: "under"` - For statement blocks inside containers. Create the container first, then create statement blocks using the container's ID.
623
+ Example: Create a loop first, then use `blockID: loopID, type: "under"` to place code inside it.
624
+ Also for stackable blocks: create one, get its ID, then create the next one with the previous block's ID and `type: "under"`.
625
+
626
+ - `type: "input"` - ONLY for value blocks placed in MCP output slots. Provide `input_name` with the output slot name (R0, R1, R2, etc).
627
+ Example: `text(inputs(TEXT: "hello"))` with `type: "input", input_name: "R0"` places the text block in the MCP's first output slot.
628
+
629
+ **Value block nesting** - For value blocks inside other blocks: nest them directly in the create_block command (do not use `blockID` or `type`).
630
+ Example: `math_arithmetic(inputs(A: math_number(inputs(NUM: 5)), B: math_number(inputs(NUM: 3))))`
631
+
632
+ **CRITICAL for value block expressions**: You MUST build the entire nested structure in ONE create_block call. You cannot:
633
+ - Create blocks in stages
634
+ - Create intermediate blocks first and connect them later
635
+ - Break a value expression into multiple separate create_block calls
636
+ This is impossible. Value blocks can only be built by nesting them inside the create_block command.
637
+ Example: `math_arithmetic(inputs(A: math_number(inputs(NUM: 1)), B: math_arithmetic(inputs(A: math_number(inputs(NUM: 2)), B: math_number(inputs(NUM: 3))))))`
638
+
639
+ ### Input Rules
640
+ Every block must have `inputs()`. For blocks that grow like `make_json` or `text_join`, add as many inputs as needed:
641
+ - `text_join(inputs(ADD0: text(inputs(TEXT: "hello")), ADD1: text(inputs(TEXT: "world"))))`
642
+ The blocks list will have comments saying something like "you can make as many N as you want" if this is allowed for that block.
643
+
644
+ **String values must always be quoted with double quotes.** All configuration values (like operation names, parameter values, etc.) that are strings must be `"quoted"`:
645
+ - WRONG: `math_single(inputs(OP: ROOT, NUM: value)`
646
+ - CORRECT: `math_single(inputs(OP: "ROOT", NUM: value)`
647
+
648
+ For value inputs, never use raw values. Always wrap in a block:
649
+ - WRONG: `math_arithmetic(inputs(A: 5, B: "hi"))`
650
+ - RIGHT: `math_arithmetic(inputs(A: math_number(inputs(NUM: 5)), B: text(inputs(TEXT: "hi"))))`
651
 
652
  You may NEVER create functions. You must only use code inside the MCP block itself.
653
 
 
670
 
671
  ---
672
 
673
+ ## VALUE BLOCK CONSTRUCTION: ABSOLUTE RULE
674
 
675
+ **Value blocks and value expressions must be built entirely in a SINGLE create_block call.**
676
 
677
+ This is not negotiable. There is no alternative method. There is no workaround.
678
 
679
+ When you need to create value blocks (math blocks, text blocks, logic blocks, comparison blocks, or any block that produces a value for another block to use), you must nest all of them together in one create_block call. All child blocks, all nested blocks, all sub-expressions must be included in that single call.
680
 
681
+ **For any value expression, you have exactly ONE option: build it all in a single create_block call with all children nested inside.**
 
 
 
 
682
 
683
+ You cannot call create_block multiple times for one value expression. You cannot create intermediate blocks and connect them later. These are not possible.
 
 
 
 
 
 
 
684
 
685
+ **Correct:**
686
+ `text_join(inputs(ADD0: text(inputs(TEXT: "a")), ADD1: text(inputs(TEXT: "b")), ADD2: text(inputs(TEXT: "c"))))`
687
+ This is ONE call with all blocks nested in it.
688
 
689
+ **Prohibited:**
690
+ - `create_block(text(...))` then `create_block(text(...))` then `create_block(text(...))`: You cannot do this
691
+ - Create one block, get its ID, then try to place other blocks in its inputs later: You cannot do this
692
+ - Break a math expression across multiple calls: You cannot do this
693
 
694
+ **Statement blocks are different.** Loops and conditionals require sequential calls because you need the container's ID first to place code inside it. But value blocks are not containers. They are atomic. Build the entire structure in one call or not at all.
695
 
696
+ ---
697
+
698
+ Tool responses appear under the assistant role, but they are not part of your own words. Never treat tool output as
699
+ something you said, and never fabricate or echo fake tool outputs.
700
 
701
  ---
702
 
703
+ ## REQUIRED PLANNING PHASE BEFORE ANY TOOL CALL
704
+
705
+ Before creating or deleting any blocks, always begin with a *Planning Phase*:
706
+
707
+ 0. Acknowledge the `VALUE BLOCK CONSTRUCTION: ABSOLUTE RULE` section and how you are prohibited from doing multi step calls for value blocks, and must do it in one create block call.
708
+
709
+ 1. **Analyze the user's request.** Identify all required inputs, outputs, intermediate steps, loops, and conditionals.
710
+
711
+ 2. **Write pseudocode showing the complete flow** using readable syntax like function calls and control structures.
712
+ This is for *your own* understanding: work out the logic *before* translating to blocks.
713
+ Example: `for item in items: result = process(item); output = combine(result)`
714
+
715
+ 3. **Create a build order**: which blocks are top-level, which nest inside others, which are value expressions that must be built in one call.
716
+
717
+ 4. Perform the actions in order without asking for approval or asking to wait for intermediate results.
718
+
719
  """
720
 
721
  tools = [
 
745
  "type": "string",
746
  "description": "The create block command using the custom DSL format.",
747
  },
748
+ "blockID": {
749
+ "type": "string",
750
+ "description": "The ID of the target block for placement.",
751
+ },
752
+ "type": {
753
  "type": "string",
754
+ "enum": ["under", "input"],
755
+ "description": "Placement type. 'under' for statement blocks inside containers. 'input' only for value blocks in MCP outputs.",
756
  },
757
+ "input_name": {
758
  "type": "string",
759
+ "description": "Specific MCP input slot name when type is 'input'. Use 'RN', where N is the number of the output slot you want to put something into.",
760
  },
761
  },
762
  "required": ["command"],
 
856
  input_items.append({"role": "user", "content": human})
857
  input_items.append({"role": "assistant", "content": ai})
858
 
 
 
 
859
  # Build instructions
860
  instructions = SYSTEM_PROMPT
861
  if context:
 
1012
 
1013
  elif function_name == "create_block":
1014
  command = function_args.get("command", "")
1015
+ blockID = function_args.get("blockID", None)
1016
+ placement_type = function_args.get("type", None)
1017
+ input_name = function_args.get("input_name", None)
1018
+ if blockID is None:
1019
  print(Fore.YELLOW + f"Agent created block with command `{command}`." + Style.RESET_ALL)
1020
+ else:
1021
+ print(Fore.YELLOW + f"Agent created block with command `{command}`, type: {placement_type}, blockID: `{blockID}`." + Style.RESET_ALL)
1022
+ if input_name:
1023
+ print(Fore.YELLOW + f" Input name: {input_name}" + Style.RESET_ALL)
1024
+ tool_result = create_block(command, blockID, placement_type, input_name)
1025
  result_label = "Create Operation"
1026
 
1027
  elif function_name == "create_variable":
project/src/index.js CHANGED
@@ -657,14 +657,19 @@ const setupUnifiedStream = () => {
657
 
658
  if (newBlock) {
659
  blockId = newBlock.id;
 
660
 
661
- // If input_name is specified, place the block into that MCP input directly
662
- if (data.input_name) {
 
 
663
  const mcpBlock = ws.getBlocksByType('create_mcp')[0];
664
  if (mcpBlock) {
665
- const input = mcpBlock.getInput(data.input_name);
 
 
666
  if (input && input.connection) {
667
- console.log('[SSE CREATE] Placing block into MCP input:', data.input_name);
668
  // Disconnect any existing block
669
  const existingBlock = input.connection.targetBlock();
670
  if (existingBlock) {
@@ -673,25 +678,27 @@ const setupUnifiedStream = () => {
673
  // Connect the new block
674
  if (newBlock.outputConnection) {
675
  input.connection.connect(newBlock.outputConnection);
676
- console.log('[SSE CREATE] Successfully placed block into input:', data.input_name);
677
  } else {
678
- error = `Block has no output connection to connect to MCP input ${data.input_name}`;
679
  console.error('[SSE CREATE]', error);
680
  }
681
  } else {
682
- error = `MCP input not found: ${data.input_name}`;
 
 
683
  console.error('[SSE CREATE]', error);
684
  }
685
  } else {
686
- error = 'No MCP block found to place this block into';
687
  console.error('[SSE CREATE]', error);
688
  }
689
  }
690
- // If under_block_id is specified, attach the new block under the parent
691
- else if (data.under_block_id) {
692
- const parentBlock = ws.getBlockById(data.under_block_id);
693
  if (parentBlock) {
694
- console.log('[SSE CREATE] Attaching to parent block:', data.under_block_id);
695
 
696
  // Find an appropriate input to connect to
697
  // Try common statement inputs first
@@ -744,15 +751,18 @@ const setupUnifiedStream = () => {
744
  }
745
 
746
  if (!connected) {
747
- console.warn('[SSE CREATE] Could not find suitable connection point on parent block');
 
748
  }
749
  } else {
750
- console.warn('[SSE CREATE] Parent block not found:', data.under_block_id);
 
751
  }
752
  }
753
 
754
- success = true;
755
- console.log('[SSE CREATE] Successfully created block with children:', blockId, newBlock.type);
 
756
  } else {
757
  throw new Error(`Failed to create block from specification`);
758
  }
@@ -950,7 +960,7 @@ const updateCode = () => {
950
  code = "import numbers\n\n" + code;
951
  }
952
 
953
- code = "import gradio as gr\n\n" + code
954
 
955
  if (codeEl) {
956
  codeEl.textContent = code;
 
657
 
658
  if (newBlock) {
659
  blockId = newBlock.id;
660
+ success = true; // Block was created successfully
661
 
662
+ // Handle placement based on placement_type
663
+ if (data.placement_type === 'input') {
664
+ // Place into MCP block's output slot
665
+ // For type: 'input', find the first MCP block and use input_name for the slot
666
  const mcpBlock = ws.getBlocksByType('create_mcp')[0];
667
  if (mcpBlock) {
668
+ // input_name specifies which output slot (e.g., "R0", "R1")
669
+ const inputSlot = data.input_name;
670
+ const input = mcpBlock.getInput(inputSlot);
671
  if (input && input.connection) {
672
+ console.log('[SSE CREATE] Placing block into MCP output slot:', inputSlot);
673
  // Disconnect any existing block
674
  const existingBlock = input.connection.targetBlock();
675
  if (existingBlock) {
 
678
  // Connect the new block
679
  if (newBlock.outputConnection) {
680
  input.connection.connect(newBlock.outputConnection);
681
+ console.log('[SSE CREATE] Successfully placed block into slot:', inputSlot);
682
  } else {
683
+ error = `Block has no output connection to connect to MCP slot ${inputSlot}`;
684
  console.error('[SSE CREATE]', error);
685
  }
686
  } else {
687
+ // Try to get all available inputs on the MCP block for debugging
688
+ const availableInputs = mcpBlock.inputList.map(inp => inp.name).join(', ');
689
+ error = `Output slot '${inputSlot}' not found. Available inputs: ${availableInputs}`;
690
  console.error('[SSE CREATE]', error);
691
  }
692
  } else {
693
+ error = `No MCP block found in workspace`;
694
  console.error('[SSE CREATE]', error);
695
  }
696
  }
697
+ // If placement_type is 'under', attach the new block under the parent
698
+ else if (data.placement_type === 'under') {
699
+ const parentBlock = ws.getBlockById(data.blockID);
700
  if (parentBlock) {
701
+ console.log('[SSE CREATE] Attaching to parent block:', data.blockID);
702
 
703
  // Find an appropriate input to connect to
704
  // Try common statement inputs first
 
751
  }
752
 
753
  if (!connected) {
754
+ error = `Could not find suitable connection point on parent block`;
755
+ console.warn('[SSE CREATE]', error);
756
  }
757
  } else {
758
+ error = `Parent block not found: ${data.blockID}`;
759
+ console.warn('[SSE CREATE]', error);
760
  }
761
  }
762
 
763
+ if (success) {
764
+ console.log('[SSE CREATE] Successfully created block with children:', blockId, newBlock.type);
765
+ }
766
  } else {
767
  throw new Error(`Failed to create block from specification`);
768
  }
 
960
  code = "import numbers\n\n" + code;
961
  }
962
 
963
+ code = "import gradio as gr\nimport math\n\n" + code
964
 
965
  if (codeEl) {
966
  codeEl.textContent = code;