owenkaplinsky commited on
Commit
bce9c73
·
1 Parent(s): 0fc972f

Add mcp, gradio, and functions

Browse files
project/app.py CHANGED
@@ -2,6 +2,7 @@ from fastapi import FastAPI, Request
2
  from fastapi.middleware.cors import CORSMiddleware
3
  import gradio as gr
4
  import uvicorn
 
5
  from dotenv import load_dotenv
6
 
7
  app = FastAPI()
@@ -34,22 +35,78 @@ def execute_blockly_logic(user_inputs):
34
  return "No Blockly code available"
35
 
36
  result = ""
37
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  def capture_result(msg):
39
  nonlocal result
40
  result = msg
41
 
42
- env = {
43
- "reply": capture_result,
44
- }
45
 
46
  try:
47
- exec(latest_blockly_code, env)
48
  if "create_mcp" in env:
49
- # If create_mcp function exists, call it with the input arguments
50
- # Filter out None values and convert to list for unpacking
51
- args = [arg for arg in user_inputs if arg is not None]
52
- result = env["create_mcp"](*args)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  elif "process_input" in env:
54
  env["process_input"](user_inputs)
55
  except Exception as e:
@@ -74,8 +131,13 @@ def build_interface():
74
  txt = gr.Textbox(label=f"Input {i+1}", visible=False)
75
  input_fields.append(txt)
76
  input_group_items.append(txt)
 
 
77
 
78
- output_text = gr.Textbox(label="Output", interactive=False)
 
 
 
79
 
80
  with gr.Row():
81
  submit_btn = gr.Button("Submit")
@@ -83,14 +145,12 @@ def build_interface():
83
 
84
  def refresh_inputs():
85
  global latest_blockly_code
86
-
87
- # Parse the Python code to extract function parameters
88
  import re
89
-
90
  # Look for the create_mcp function definition
91
  pattern = r'def create_mcp\((.*?)\):'
92
  match = re.search(pattern, latest_blockly_code)
93
-
94
  params = []
95
  if match:
96
  params_str = match.group(1)
@@ -109,38 +169,54 @@ def build_interface():
109
  'name': param,
110
  'type': 'str'
111
  })
112
-
113
- # Generate visibility and label updates for each input field
 
 
 
 
 
 
 
 
 
 
 
 
114
  updates = []
115
  for i, field in enumerate(input_fields):
116
  if i < len(params):
117
- # Show this field and update its label
118
  param = params[i]
119
  updates.append(gr.update(
120
  visible=True,
121
- label=f"{param['name']} ({param['type']})"
 
122
  ))
123
  else:
124
- # Hide this field
125
- updates.append(gr.update(visible=False))
126
-
127
- return updates
128
 
129
  def process_input(*args):
130
- # Get the input values from Gradio fields
131
- return execute_blockly_logic(args)
 
 
 
 
 
132
 
133
  # When refresh is clicked, update input field visibility and labels
134
  refresh_btn.click(
135
  refresh_inputs,
136
- outputs=input_fields,
137
  queue=False
138
  )
139
 
140
  submit_btn.click(
141
  process_input,
142
  inputs=input_fields,
143
- outputs=[output_text]
144
  )
145
 
146
  return demo
 
2
  from fastapi.middleware.cors import CORSMiddleware
3
  import gradio as gr
4
  import uvicorn
5
+ import re
6
  from dotenv import load_dotenv
7
 
8
  app = FastAPI()
 
35
  return "No Blockly code available"
36
 
37
  result = ""
38
+
39
+ code_lines = latest_blockly_code.splitlines()
40
+
41
+ cleaned_lines = []
42
+ skip = False
43
+ for line in code_lines:
44
+ stripped = line.strip()
45
+
46
+ # When we reach the Interface block, start skipping until after demo.launch()
47
+ if stripped.startswith("demo = gr.Interface("):
48
+ skip = True
49
+ continue
50
+ if skip:
51
+ # Stop skipping after demo.launch(...) line
52
+ if stripped.startswith("demo.launch("):
53
+ skip = False
54
+ continue
55
+
56
+ # Keep everything else
57
+ cleaned_lines.append(line)
58
+
59
+ # Remove trailing blank lines
60
+ while cleaned_lines and not cleaned_lines[-1].strip():
61
+ cleaned_lines.pop()
62
+
63
+ code_to_run = "\n".join(cleaned_lines)
64
+
65
+ if len(code_lines) > 7:
66
+ code_to_run = "\n".join(code_lines[:-7])
67
+ else:
68
+ code_to_run = "\n".join(code_lines)
69
+
70
  def capture_result(msg):
71
  nonlocal result
72
  result = msg
73
 
74
+ env = {"reply": capture_result}
 
 
75
 
76
  try:
77
+ exec(code_to_run, env)
78
  if "create_mcp" in env:
79
+ import inspect
80
+ sig = inspect.signature(env["create_mcp"])
81
+ params = list(sig.parameters.values())
82
+
83
+ typed_args = []
84
+ for i, arg in enumerate(user_inputs):
85
+ if i >= len(params):
86
+ break
87
+ if arg is None or arg == "":
88
+ typed_args.append(None)
89
+ continue
90
+
91
+ anno = params[i].annotation
92
+ try:
93
+ # Convert using the declared type hint
94
+ if anno == int:
95
+ typed_args.append(int(arg))
96
+ elif anno == float:
97
+ typed_args.append(float(arg))
98
+ elif anno == bool:
99
+ typed_args.append(str(arg).lower() in ("true", "1"))
100
+ elif anno == str or anno == inspect._empty:
101
+ typed_args.append(str(arg))
102
+ else:
103
+ # Unknown or complex type — leave as-is
104
+ typed_args.append(arg)
105
+ except Exception:
106
+ # If conversion fails, pass the raw input
107
+ typed_args.append(arg)
108
+
109
+ result = env["create_mcp"](*typed_args)
110
  elif "process_input" in env:
111
  env["process_input"](user_inputs)
112
  except Exception as e:
 
131
  txt = gr.Textbox(label=f"Input {i+1}", visible=False)
132
  input_fields.append(txt)
133
  input_group_items.append(txt)
134
+
135
+ output_fields = []
136
 
137
+ with gr.Accordion("MCP Outputs", open=True):
138
+ for i in range(10):
139
+ out = gr.Textbox(label=f"Output {i+1}", visible=False, interactive=False)
140
+ output_fields.append(out)
141
 
142
  with gr.Row():
143
  submit_btn = gr.Button("Submit")
 
145
 
146
  def refresh_inputs():
147
  global latest_blockly_code
 
 
148
  import re
149
+
150
  # Look for the create_mcp function definition
151
  pattern = r'def create_mcp\((.*?)\):'
152
  match = re.search(pattern, latest_blockly_code)
153
+
154
  params = []
155
  if match:
156
  params_str = match.group(1)
 
169
  'name': param,
170
  'type': 'str'
171
  })
172
+
173
+ # Detect output count (out_amt = N)
174
+ out_amt_match = re.search(r'out_amt\s*=\s*(\d+)', latest_blockly_code)
175
+ out_amt = int(out_amt_match.group(1)) if out_amt_match else 0
176
+
177
+ # Update visibility + clear output fields
178
+ output_updates = []
179
+ for i, field in enumerate(output_fields):
180
+ if i < out_amt:
181
+ output_updates.append(gr.update(visible=True, label=f"Output {i+1}", value=""))
182
+ else:
183
+ output_updates.append(gr.update(visible=False, value=""))
184
+
185
+ # Update visibility + clear input fields
186
  updates = []
187
  for i, field in enumerate(input_fields):
188
  if i < len(params):
 
189
  param = params[i]
190
  updates.append(gr.update(
191
  visible=True,
192
+ label=f"{param['name']} ({param['type']})",
193
+ value=""
194
  ))
195
  else:
196
+ updates.append(gr.update(visible=False, value=""))
197
+
198
+ return updates + output_updates
 
199
 
200
  def process_input(*args):
201
+ result = execute_blockly_logic(args)
202
+
203
+ # If the result is a tuple or list, pad it to length 10
204
+ if isinstance(result, (tuple, list)):
205
+ return list(result) + [""] * (10 - len(result))
206
+ # If it's a single value, repeat it in the first slot and pad the rest
207
+ return [result] + [""] * 9
208
 
209
  # When refresh is clicked, update input field visibility and labels
210
  refresh_btn.click(
211
  refresh_inputs,
212
+ outputs=input_fields + output_fields,
213
  queue=False
214
  )
215
 
216
  submit_btn.click(
217
  process_input,
218
  inputs=input_fields,
219
+ outputs=output_fields
220
  )
221
 
222
  return demo
project/src/blocks/text.js CHANGED
@@ -43,6 +43,9 @@ Blockly.Extensions.registerMutator(
43
  this.inputNames_ = [];
44
  this.inputTypes_ = [];
45
  this.inputRefBlocks_ = new Map();
 
 
 
46
  this.initialized_ = true;
47
  // Mark all reference blocks with their owner for later identification
48
  this._ownerBlockId = this.id;
@@ -57,6 +60,9 @@ Blockly.Extensions.registerMutator(
57
  this.inputCount_ = this.inputCount_ || 0;
58
  this.inputNames_ = this.inputNames_ || [];
59
  this.inputTypes_ = this.inputTypes_ || [];
 
 
 
60
 
61
  // Restore dynamically added input items
62
  for (let i = 0; i < this.inputCount_; i++) {
@@ -76,6 +82,20 @@ Blockly.Extensions.registerMutator(
76
  connection = itemBlock.nextConnection;
77
  }
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  return containerBlock;
80
  },
81
 
@@ -85,7 +105,9 @@ Blockly.Extensions.registerMutator(
85
  if (!this.initialized_) this.initialize();
86
 
87
  const oldNames = [...(this.inputNames_ || [])];
 
88
  const connections = [];
 
89
  let itemBlock = containerBlock.getInputTargetBlock('STACK');
90
 
91
  // Collect all child connections from mutator stack
@@ -94,10 +116,34 @@ Blockly.Extensions.registerMutator(
94
  itemBlock = itemBlock.nextConnection && itemBlock.nextConnection.targetBlock();
95
  }
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  const newCount = connections.length;
 
98
  this.inputCount_ = newCount;
 
99
  this.inputNames_ = this.inputNames_ || [];
100
  this.inputTypes_ = this.inputTypes_ || [];
 
 
101
 
102
  // Rebuild the new list of input names and types
103
  let idx = 0;
@@ -111,6 +157,16 @@ Blockly.Extensions.registerMutator(
111
  idx++;
112
  }
113
 
 
 
 
 
 
 
 
 
 
 
114
  // Dispose of removed input reference blocks when inputs shrink
115
  if (newCount < oldNames.length) {
116
  for (let i = newCount; i < oldNames.length; i++) {
@@ -182,9 +238,11 @@ Blockly.Extensions.registerMutator(
182
  // Remove all dynamic and temporary inputs before reconstruction
183
  let i = 0;
184
  while (this.getInput('X' + i)) this.removeInput('X' + i++);
 
 
185
  let t = 0;
186
  while (this.getInput('T' + t)) this.removeInput('T' + t++);
187
- ['INPUTS_TEXT', 'TOOLS_TEXT'].forEach(name => {
188
  if (this.getInput(name)) this.removeInput(name);
189
  });
190
 
@@ -239,6 +297,39 @@ Blockly.Extensions.registerMutator(
239
  }
240
  }
241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  this.workspace.render();
243
  } finally {
244
  Blockly.Events.enable();
@@ -250,6 +341,9 @@ Blockly.Extensions.registerMutator(
250
  inputCount: this.inputCount_,
251
  inputNames: this.inputNames_,
252
  inputTypes: this.inputTypes_,
 
 
 
253
  toolCount: this.toolCount_ || 0
254
  };
255
  },
@@ -258,6 +352,9 @@ Blockly.Extensions.registerMutator(
258
  this.inputCount_ = state.inputCount;
259
  this.inputNames_ = state.inputNames || [];
260
  this.inputTypes_ = state.inputTypes || [];
 
 
 
261
  this.toolCount_ = state.toolCount || 0;
262
  }
263
  },
@@ -268,10 +365,12 @@ Blockly.Extensions.registerMutator(
268
  // Base block definitions
269
  const container = {
270
  type: "container",
271
- message0: "inputs %1 %2",
272
  args0: [
273
  { type: "input_dummy", name: "title" },
274
  { type: "input_statement", name: "STACK" },
 
 
275
  ],
276
  colour: 160,
277
  inputsInline: false
@@ -279,7 +378,27 @@ const container = {
279
 
280
  const container_input = {
281
  type: "container_input",
282
- message0: "input %1 %2",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  args0: [
284
  {
285
  type: "field_dropdown",
@@ -320,11 +439,10 @@ const llm_call = {
320
 
321
  const create_mcp = {
322
  type: "create_mcp",
323
- message0: "create MCP %1 %2 and return %3",
324
  args0: [
325
  { type: "input_dummy" },
326
  { type: "input_statement", name: "BODY" },
327
- { type: "input_value", name: "RETURN" },
328
  ],
329
  colour: 160,
330
  inputsInline: true,
@@ -336,12 +454,11 @@ const create_mcp = {
336
 
337
  const tool_def = {
338
  type: "tool_def",
339
- message0: "function %1 %2 %3 and return %4",
340
  args0: [
341
  { type: "field_input", name: "NAME", text: "newFunction" },
342
  { type: "input_dummy" },
343
  { type: "input_statement", name: "BODY" },
344
- { type: "input_value", name: "RETURN" },
345
  ],
346
  colour: 160,
347
  inputsInline: true,
@@ -390,18 +507,35 @@ function generateUniqueToolName(workspace, excludeBlock) {
390
  return name;
391
  }
392
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
  // Register tool_def block separately to include custom init logic
394
  Blockly.Blocks['tool_def'] = {
395
  init: function () {
396
  this.jsonInit(tool_def);
397
  // Apply extensions
398
  Blockly.Extensions.apply('test_cleanup_extension', this, false);
 
 
 
 
399
  }
400
  };
401
 
402
  export const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([
403
- create_mcp,
404
  container,
405
  container_input,
 
406
  llm_call,
407
  ]);
 
43
  this.inputNames_ = [];
44
  this.inputTypes_ = [];
45
  this.inputRefBlocks_ = new Map();
46
+ this.outputCount_ = 0;
47
+ this.outputNames_ = [];
48
+ this.outputTypes_ = [];
49
  this.initialized_ = true;
50
  // Mark all reference blocks with their owner for later identification
51
  this._ownerBlockId = this.id;
 
60
  this.inputCount_ = this.inputCount_ || 0;
61
  this.inputNames_ = this.inputNames_ || [];
62
  this.inputTypes_ = this.inputTypes_ || [];
63
+ this.outputCount_ = this.outputCount_ || 0;
64
+ this.outputNames_ = this.outputNames_ || [];
65
+ this.outputTypes_ = this.outputTypes_ || [];
66
 
67
  // Restore dynamically added input items
68
  for (let i = 0; i < this.inputCount_; i++) {
 
82
  connection = itemBlock.nextConnection;
83
  }
84
 
85
+ // Restore dynamically added output items
86
+ let connection2 = containerBlock.getInput('STACK2').connection;
87
+ for (let i = 0; i < this.outputCount_; i++) {
88
+ const itemBlock = workspace.newBlock('container_output');
89
+ itemBlock.initSvg();
90
+ const typeVal = this.outputTypes_[i] || 'string';
91
+ const nameVal = this.outputNames_[i] || typeVal;
92
+ itemBlock.setFieldValue(typeVal, 'TYPE');
93
+ itemBlock.setFieldValue(nameVal, 'NAME');
94
+
95
+ connection2.connect(itemBlock.previousConnection);
96
+ connection2 = itemBlock.nextConnection;
97
+ }
98
+
99
  return containerBlock;
100
  },
101
 
 
105
  if (!this.initialized_) this.initialize();
106
 
107
  const oldNames = [...(this.inputNames_ || [])];
108
+ const oldOutputNames = [...(this.outputNames_ || [])];
109
  const connections = [];
110
+ const returnConnections = [];
111
  let itemBlock = containerBlock.getInputTargetBlock('STACK');
112
 
113
  // Collect all child connections from mutator stack
 
116
  itemBlock = itemBlock.nextConnection && itemBlock.nextConnection.targetBlock();
117
  }
118
 
119
+ // Save existing return connections before removing them
120
+ let rIdx = 0;
121
+ while (this.getInput('R' + rIdx)) {
122
+ const returnInput = this.getInput('R' + rIdx);
123
+ if (returnInput && returnInput.connection && returnInput.connection.targetConnection) {
124
+ returnConnections.push(returnInput.connection.targetConnection);
125
+ } else {
126
+ returnConnections.push(null);
127
+ }
128
+ rIdx++;
129
+ }
130
+
131
+ // Collect output specifications from STACK2
132
+ const outputSpecs = [];
133
+ let outputBlock = containerBlock.getInputTargetBlock('STACK2');
134
+ while (outputBlock) {
135
+ outputSpecs.push(outputBlock);
136
+ outputBlock = outputBlock.nextConnection && outputBlock.nextConnection.targetBlock();
137
+ }
138
+
139
  const newCount = connections.length;
140
+ const newOutputCount = outputSpecs.length;
141
  this.inputCount_ = newCount;
142
+ this.outputCount_ = newOutputCount;
143
  this.inputNames_ = this.inputNames_ || [];
144
  this.inputTypes_ = this.inputTypes_ || [];
145
+ this.outputNames_ = this.outputNames_ || [];
146
+ this.outputTypes_ = this.outputTypes_ || [];
147
 
148
  // Rebuild the new list of input names and types
149
  let idx = 0;
 
157
  idx++;
158
  }
159
 
160
+ // Rebuild the new list of output names and types
161
+ let oidx = 0;
162
+ const newOutputNames = [];
163
+ for (const outBlock of outputSpecs) {
164
+ this.outputTypes_[oidx] = outBlock.getFieldValue('TYPE') || 'string';
165
+ this.outputNames_[oidx] = outBlock.getFieldValue('NAME') || 'output' + oidx;
166
+ newOutputNames.push(this.outputNames_[oidx]);
167
+ oidx++;
168
+ }
169
+
170
  // Dispose of removed input reference blocks when inputs shrink
171
  if (newCount < oldNames.length) {
172
  for (let i = newCount; i < oldNames.length; i++) {
 
238
  // Remove all dynamic and temporary inputs before reconstruction
239
  let i = 0;
240
  while (this.getInput('X' + i)) this.removeInput('X' + i++);
241
+ let r = 0;
242
+ while (this.getInput('R' + r)) this.removeInput('R' + r++);
243
  let t = 0;
244
  while (this.getInput('T' + t)) this.removeInput('T' + t++);
245
+ ['INPUTS_TEXT', 'RETURNS_TEXT', 'TOOLS_TEXT'].forEach(name => {
246
  if (this.getInput(name)) this.removeInput(name);
247
  });
248
 
 
297
  }
298
  }
299
 
300
+ // Handle return inputs based on outputs
301
+ if (newOutputCount > 0) {
302
+ // Remove the default RETURN input if it exists
303
+ if (this.getInput('RETURN')) {
304
+ this.removeInput('RETURN');
305
+ }
306
+
307
+ // Add the "and return" label
308
+ const returnsText = this.appendDummyInput('RETURNS_TEXT');
309
+ returnsText.appendField('and return');
310
+
311
+ // Add each return value input slot
312
+ for (let j = 0; j < newOutputCount; j++) {
313
+ const type = this.outputTypes_[j] || 'string';
314
+ const name = this.outputNames_[j] || ('output' + j);
315
+ let check = null;
316
+ if (type === 'integer') check = 'Number';
317
+ if (type === 'string') check = 'String';
318
+
319
+ const returnInput = this.appendValueInput('R' + j);
320
+ if (check) returnInput.setCheck(check);
321
+ returnInput.appendField(type);
322
+ returnInput.appendField('"' + name + '":');
323
+
324
+ // Reconnect previous connection if it exists
325
+ if (returnConnections[j]) {
326
+ try {
327
+ returnInput.connection.connect(returnConnections[j]);
328
+ } catch { }
329
+ }
330
+ }
331
+ }
332
+
333
  this.workspace.render();
334
  } finally {
335
  Blockly.Events.enable();
 
341
  inputCount: this.inputCount_,
342
  inputNames: this.inputNames_,
343
  inputTypes: this.inputTypes_,
344
+ outputCount: this.outputCount_,
345
+ outputNames: this.outputNames_,
346
+ outputTypes: this.outputTypes_,
347
  toolCount: this.toolCount_ || 0
348
  };
349
  },
 
352
  this.inputCount_ = state.inputCount;
353
  this.inputNames_ = state.inputNames || [];
354
  this.inputTypes_ = state.inputTypes || [];
355
+ this.outputCount_ = state.outputCount || 0;
356
+ this.outputNames_ = state.outputNames || [];
357
+ this.outputTypes_ = state.outputTypes || [];
358
  this.toolCount_ = state.toolCount || 0;
359
  }
360
  },
 
365
  // Base block definitions
366
  const container = {
367
  type: "container",
368
+ message0: "inputs %1 %2 outputs %3 %4",
369
  args0: [
370
  { type: "input_dummy", name: "title" },
371
  { type: "input_statement", name: "STACK" },
372
+ { type: "input_dummy", name: "title2" },
373
+ { type: "input_statement", name: "STACK2" },
374
  ],
375
  colour: 160,
376
  inputsInline: false
 
378
 
379
  const container_input = {
380
  type: "container_input",
381
+ message0: "%1 %2",
382
+ args0: [
383
+ {
384
+ type: "field_dropdown",
385
+ name: "TYPE",
386
+ options: [
387
+ ["String", "string"],
388
+ ["Integer", "integer"],
389
+ ["List", "list"],
390
+ ]
391
+ },
392
+ { type: "field_input", name: "NAME" },
393
+ ],
394
+ previousStatement: null,
395
+ nextStatement: null,
396
+ colour: 210,
397
+ };
398
+
399
+ const container_output = {
400
+ type: "container_output",
401
+ message0: "%1 %2",
402
  args0: [
403
  {
404
  type: "field_dropdown",
 
439
 
440
  const create_mcp = {
441
  type: "create_mcp",
442
+ message0: "create MCP %1 %2",
443
  args0: [
444
  { type: "input_dummy" },
445
  { type: "input_statement", name: "BODY" },
 
446
  ],
447
  colour: 160,
448
  inputsInline: true,
 
454
 
455
  const tool_def = {
456
  type: "tool_def",
457
+ message0: "function %1 %2 %3",
458
  args0: [
459
  { type: "field_input", name: "NAME", text: "newFunction" },
460
  { type: "input_dummy" },
461
  { type: "input_statement", name: "BODY" },
 
462
  ],
463
  colour: 160,
464
  inputsInline: true,
 
507
  return name;
508
  }
509
 
510
+ // Register create_mcp block separately to include custom init logic
511
+ Blockly.Blocks['create_mcp'] = {
512
+ init: function () {
513
+ this.jsonInit(create_mcp);
514
+ // Apply extensions
515
+ Blockly.Extensions.apply('test_cleanup_extension', this, false);
516
+ // Initialize mutator state
517
+ if (this.initialize) {
518
+ this.initialize();
519
+ }
520
+ }
521
+ };
522
+
523
  // Register tool_def block separately to include custom init logic
524
  Blockly.Blocks['tool_def'] = {
525
  init: function () {
526
  this.jsonInit(tool_def);
527
  // Apply extensions
528
  Blockly.Extensions.apply('test_cleanup_extension', this, false);
529
+ // Initialize mutator state
530
+ if (this.initialize) {
531
+ this.initialize();
532
+ }
533
  }
534
  };
535
 
536
  export const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([
 
537
  container,
538
  container_input,
539
+ container_output,
540
  llm_call,
541
  ]);
project/src/generators/python.js CHANGED
@@ -32,44 +32,107 @@ forBlock['create_mcp'] = function (block, generator) {
32
  i++;
33
  }
34
 
35
- // Gather any tool definitions connected to this block
36
- let toolDefs = [];
37
- let t = 0;
38
- while (block.getInput('T' + t)) {
39
- const toolCode = generator.valueToCode(block, 'T' + t, Order.NONE);
40
- if (toolCode) {
41
- toolDefs.push(toolCode);
42
- }
43
- t++;
44
- }
45
-
46
- // Main function body and return value
47
  let body = generator.statementToCode(block, 'BODY');
48
- let returnValue = generator.valueToCode(block, 'RETURN', Order.ATOMIC);
49
 
50
- // Replace placeholder args (arg0, arg1...) with actual names in return statement
51
- if (returnValue && block.inputNames_) {
52
- for (let j = 0; j < block.inputNames_.length; j++) {
53
- const paramName = block.inputNames_[j];
54
- returnValue = returnValue.replace(new RegExp(`arg${j}\\b`, 'g'), paramName);
55
- }
56
- }
57
 
58
- let returnStatement = returnValue ? ` return ${returnValue}\n` : ' return\n';
59
- let code = '';
 
 
 
 
 
 
 
 
 
 
60
 
61
- // Tool definitions come before main function
62
- if (toolDefs.length > 0) {
63
- code += toolDefs.join('\n') + '\n\n';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  }
 
65
 
66
  // Create the main function definition
67
  if (typedInputs.length > 0) {
68
- code += `def create_mcp(${typedInputs.join(', ')}):\n${body}${returnStatement}\n`;
69
  } else {
70
- code += `def create_mcp():\n${body}${returnStatement}`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  }
72
 
 
 
 
 
 
 
 
 
 
 
 
73
  return code;
74
  };
75
 
@@ -105,24 +168,76 @@ forBlock['tool_def'] = function (block, generator) {
105
  }
106
 
107
  let body = generator.statementToCode(block, 'BODY');
108
- let returnValue = generator.valueToCode(block, 'RETURN', Order.ATOMIC);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
- // Ensure return expression uses correct parameter names
111
- if (returnValue && block.inputNames_) {
112
- for (let j = 0; j < block.inputNames_.length; j++) {
113
- const paramName = block.inputNames_[j];
114
- returnValue = returnValue.replace(new RegExp(`arg${j}\\b`, 'g'), paramName);
 
 
115
  }
116
- }
117
 
118
- let returnStatement = returnValue ? ` return ${returnValue}\n` : ' return\n';
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
  // Construct the function definition
121
  let code;
122
  if (typedInputs.length > 0) {
123
  code = `def ${name}(${typedInputs.join(', ')}):\n${body}${returnStatement}`;
124
  } else {
125
- code = `def ${name}():\n${body}${returnStatement}`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  }
127
 
128
  // Return function definition as a string value (not executed immediately)
 
32
  i++;
33
  }
34
 
35
+ // Main function body and return value(s)
 
 
 
 
 
 
 
 
 
 
 
36
  let body = generator.statementToCode(block, 'BODY');
37
+ let returnStatement = '';
38
 
39
+ const returnValues = [];
 
 
 
 
 
 
40
 
41
+ // Check if we have outputs defined and the R inputs exist
42
+ if (block.outputCount_ && block.outputCount_ > 0 && block.getInput('R0')) {
43
+ for (let r = 0; r < block.outputCount_; r++) {
44
+ let returnValue = generator.valueToCode(block, 'R' + r, Order.ATOMIC);
45
+
46
+ // Replace placeholder args with actual names
47
+ if (returnValue && block.inputNames_) {
48
+ for (let j = 0; j < block.inputNames_.length; j++) {
49
+ const paramName = block.inputNames_[j];
50
+ returnValue = returnValue.replace(new RegExp(`arg${j}\\b`, 'g'), paramName);
51
+ }
52
+ }
53
 
54
+ // Type-cast numeric returns to ensure proper Gradio compatibility
55
+ const outputType = block.outputTypes_[r] || 'string';
56
+ if (outputType === 'integer' && returnValue) {
57
+ returnValue = `int(${returnValue})`;
58
+ }
59
+
60
+ returnValues.push(returnValue || 'None');
61
+ }
62
+
63
+ if (returnValues.length === 1) {
64
+ returnStatement = ` return ${returnValues[0]}\n`;
65
+ } else {
66
+ returnStatement = ` return (${returnValues.join(', ')})\n`;
67
+ }
68
+ } else {
69
+ // No outputs defined, return empty dict for MCP
70
+ returnStatement = ' return {}\n';
71
  }
72
+ let code = '';
73
 
74
  // Create the main function definition
75
  if (typedInputs.length > 0) {
76
+ code += `def create_mcp(${typedInputs.join(', ')}):\n out_amt = ${returnValues.length}\n\n${body}${returnStatement}\n`;
77
  } else {
78
+ code += `def create_mcp():\n out_amt = ${returnValues.length}\n\n${body || ''}${returnStatement}`;
79
+ }
80
+
81
+ // Map Python types to Gradio components for inputs
82
+ const gradioInputs = [];
83
+ if (block.inputTypes_) {
84
+ for (let k = 0; k < block.inputTypes_.length; k++) {
85
+ const type = block.inputTypes_[k];
86
+ switch (type) {
87
+ case 'integer':
88
+ gradioInputs.push('gr.Number()');
89
+ break;
90
+ case 'string':
91
+ gradioInputs.push('gr.Textbox()');
92
+ break;
93
+ case 'list':
94
+ gradioInputs.push('gr.Dataframe()');
95
+ break;
96
+ default:
97
+ gradioInputs.push('gr.Textbox()');
98
+ }
99
+ }
100
+ }
101
+
102
+ // Map Python types to Gradio components for outputs
103
+ const gradioOutputs = [];
104
+ // Only add outputs if they actually exist in the block (R0, R1, etc.)
105
+ if (block.outputTypes_ && block.outputCount_ > 0 && block.getInput('R0')) {
106
+ // Use outputCount_ to ensure we only process actual outputs
107
+ for (let k = 0; k < block.outputCount_; k++) {
108
+ const type = block.outputTypes_[k];
109
+ switch (type) {
110
+ case 'integer':
111
+ gradioOutputs.push('gr.Number()');
112
+ break;
113
+ case 'string':
114
+ gradioOutputs.push('gr.Textbox()');
115
+ break;
116
+ case 'list':
117
+ gradioOutputs.push('gr.Dataframe()');
118
+ break;
119
+ default:
120
+ gradioOutputs.push('gr.Textbox()');
121
+ }
122
+ }
123
  }
124
 
125
+ // Create Gradio Interface with dynamic I/O
126
+ // Always include outputs parameter, use empty list if no outputs
127
+ code += `\ndemo = gr.Interface(
128
+ fn=create_mcp,
129
+ inputs=[${gradioInputs.join(', ')}],
130
+ outputs=[${gradioOutputs.join(', ')}],
131
+ )
132
+
133
+ demo.launch(mcp_server=True)
134
+ `;
135
+
136
  return code;
137
  };
138
 
 
168
  }
169
 
170
  let body = generator.statementToCode(block, 'BODY');
171
+ let returnStatement = '';
172
+
173
+ // Check if we have outputs defined and the R inputs exist
174
+ if (block.outputCount_ && block.outputCount_ > 0 && block.getInput('R0')) {
175
+ const returnValues = [];
176
+ for (let r = 0; r < block.outputCount_; r++) {
177
+ let returnValue = generator.valueToCode(block, 'R' + r, Order.ATOMIC);
178
+
179
+ // Replace placeholder args with actual names
180
+ if (returnValue && block.inputNames_) {
181
+ for (let j = 0; j < block.inputNames_.length; j++) {
182
+ const paramName = block.inputNames_[j];
183
+ returnValue = returnValue.replace(new RegExp(`arg${j}\\b`, 'g'), paramName);
184
+ }
185
+ }
186
 
187
+ // Type-cast numeric returns to ensure proper Gradio compatibility
188
+ const outputType = block.outputTypes_[r] || 'string';
189
+ if (outputType === 'integer' && returnValue) {
190
+ returnValue = `int(${returnValue})`;
191
+ }
192
+
193
+ returnValues.push(returnValue || 'None');
194
  }
 
195
 
196
+ if (returnValues.length === 1) {
197
+ returnStatement = ` return ${returnValues[0]}\n`;
198
+ } else {
199
+ returnStatement = ` return (${returnValues.join(', ')})\n`;
200
+ }
201
+ } else {
202
+ // No outputs defined, add pass only if body is empty
203
+ if (!body || body.trim() === '') {
204
+ returnStatement = ' pass\n';
205
+ } else {
206
+ returnStatement = '';
207
+ }
208
+ }
209
 
210
  // Construct the function definition
211
  let code;
212
  if (typedInputs.length > 0) {
213
  code = `def ${name}(${typedInputs.join(', ')}):\n${body}${returnStatement}`;
214
  } else {
215
+ code = `def ${name}():\n${body || ' pass\n'}${returnStatement}`;
216
+ }
217
+
218
+ // Add output type hints as comments if outputs are defined
219
+ if (block.outputTypes_ && block.outputTypes_.length > 0) {
220
+ let outputTypes = [];
221
+ for (let k = 0; k < block.outputTypes_.length; k++) {
222
+ const type = block.outputTypes_[k];
223
+ const outName = block.outputNames_[k] || ('output' + k);
224
+ let pyType;
225
+ switch (type) {
226
+ case 'integer':
227
+ pyType = 'int';
228
+ break;
229
+ case 'string':
230
+ pyType = 'str';
231
+ break;
232
+ case 'list':
233
+ pyType = 'list';
234
+ break;
235
+ default:
236
+ pyType = 'Any';
237
+ }
238
+ outputTypes.push(`${outName}: ${pyType}`);
239
+ }
240
+ code = code.slice(0, -1) + ` # Returns: ${outputTypes.join(', ')}\n`;
241
  }
242
 
243
  // Return function definition as a string value (not executed immediately)
project/src/index.js CHANGED
@@ -78,6 +78,8 @@ const updateCode = () => {
78
  code = call + code;
79
  }
80
 
 
 
81
  if (codeEl) {
82
  codeEl.textContent = code;
83
  }
 
78
  code = call + code;
79
  }
80
 
81
+ code = "import gradio as gr\n\n" + code
82
+
83
  if (codeEl) {
84
  codeEl.textContent = code;
85
  }