Ali Hmaou commited on
Commit
f1e41b8
·
1 Parent(s): 5deec59

Version 1.9RC

Browse files
app.py CHANGED
@@ -1,5 +1,9 @@
1
  import os
2
  import sys
 
 
 
 
3
 
4
  # Ajoute le dossier courant au path pour pouvoir importer src
5
  sys.path.append(os.path.dirname(__file__))
 
1
  import os
2
  import sys
3
+ from dotenv import load_dotenv
4
+
5
+ # Charge les variables d'environnement depuis le fichier .env
6
+ load_dotenv()
7
 
8
  # Ajoute le dossier courant au path pour pouvoir importer src
9
  sys.path.append(os.path.dirname(__file__))
src/core/builder/proposal_generator.py CHANGED
@@ -23,7 +23,12 @@ class ProposalGenerator:
23
 
24
  print(f"🤖 Appel LLM avec Modèle: {model}, Provider: {provider}")
25
 
26
- client = InferenceClient(model=model, token=self.token, provider=provider)
 
 
 
 
 
27
 
28
  messages = [
29
  {
 
23
 
24
  print(f"🤖 Appel LLM avec Modèle: {model}, Provider: {provider}")
25
 
26
+ # Use current environment variable if available (supports UI updates), otherwise fallback to init token
27
+ current_token = os.environ.get("HF_TOKEN", self.token)
28
+
29
+ client = InferenceClient(model=model, token=current_token, provider=provider)
30
+
31
+ # print(self.token) # Avoid printing token in logs
32
 
33
  messages = [
34
  {
src/mcp_server/server.py CHANGED
@@ -15,14 +15,11 @@ from src.core.builder.proposal_generator import proposal_generator
15
  # Modèles simplifiés et performants pour le code
16
  COMMON_MODELS = [
17
  "openai/gpt-oss-120b",
18
- "moonshotai/Kimi-K2-Instruct-0905",
19
- "Qwen/Qwen3-Coder-30B-A3B-Instruct",
20
- "Qwen/Qwen2.5-Coder-32B-Instruct", # Backup éprouvé
21
  ]
22
 
23
  PROVIDER_MODELS = {
24
  "together": COMMON_MODELS,
25
- "sambanova": COMMON_MODELS,
26
  "hyperbolic": COMMON_MODELS,
27
  "None": COMMON_MODELS,
28
  # Fallback pour les autres
@@ -34,7 +31,9 @@ PROVIDER_MODELS = {
34
 
35
  def step_1_initialisation_and_proposal(project_name, description, model_id, provider_id):
36
  """
37
- STEP 1: Starts a new tool project and uses AI to propose code.
 
 
38
 
39
  This is the entry point for creating a new MCP tool. It returns a draft_id and a code proposal based on the description.
40
 
@@ -42,7 +41,7 @@ def step_1_initialisation_and_proposal(project_name, description, model_id, prov
42
  project_name: The technical name of the tool (e.g., 'weather-fetcher').
43
  description: A natural language description of what the tool should do, or a raw Swagger/OpenAPI JSON specification.
44
  model_id: The LLM model to use for code generation (default: Qwen/Qwen2.5-Coder-32B-Instruct).
45
- provider_id: The inference provider to use. Options: 'together', 'fal-ai', 'replicate', 'sambanova', 'hyperbolic', or None (auto).
46
  """
47
  # 1. Initialisation du projet (type 'adhoc' par défaut)
48
  init_result = tools.init_project(project_name, description, type="adhoc")
@@ -117,13 +116,62 @@ def step_3_deployment(draft_id):
117
  # Simplification: Toujours public, toujours new (écrase/crée), nom du space = nom du projet
118
  result = tools.deploy_to_space(draft_id, visibility="public", space_target="new", target_space_name=None)
119
 
 
 
 
 
 
120
  if "error" not in result:
121
- gr.Info(f"Déploiement réussi ! URL : {result.get('url')}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  else:
123
  gr.Info(f"Échec du déploiement : {result.get('error')}")
 
124
 
125
- # Retourne une string JSON pour compatibilité avec gr.Code
126
- return json.dumps(result, indent=2)
 
 
 
 
 
127
 
128
  # Récupération des handlers du playground
129
  reload_tools_handler, chat_response_handler = get_playground_ui_handlers()
@@ -135,7 +183,7 @@ def step_0_configuration(hf_user: str = None, hf_token: str = None, default_spac
135
  """
136
  STEP 0: Configures the Meta-MCP server environment.
137
 
138
- This step is optional but recommended to set up the Hugging Face environment.
139
 
140
  Args:
141
  hf_user: The Hugging Face username or organization (namespace) where Spaces will be deployed.
@@ -220,8 +268,8 @@ def util_get_tool_code(space_name: str, tool_name: str):
220
 
221
  # --- Construction de l'interface ---
222
 
223
- with gr.Blocks(title="Meta-MCP Fractal") as demo:
224
- gr.Markdown("# 🏭 Méta-MCP Fractal Factory")
225
  gr.Markdown("Ce serveur permet de créer et déployer d'autres serveurs MCP sur Hugging Face Spaces.")
226
 
227
  with gr.Tab("0. Setup & How-to"):
@@ -286,7 +334,7 @@ with gr.Blocks(title="Meta-MCP Fractal") as demo:
286
  with gr.Tab("1. Initialisation"):
287
  gr.Markdown("Commencez par initialiser un nouveau projet.")
288
 
289
- project_name = gr.Textbox(label="Nom du projet (ex: strawberry-counter, ratp-api-client)")
290
 
291
  project_desc = gr.Textbox(
292
  label="Description de l'outil ou Spécification (Swagger/OpenAPI JSON)",
@@ -317,20 +365,30 @@ with gr.Blocks(title="Meta-MCP Fractal") as demo:
317
 
318
  provider_id.change(update_models, inputs=[provider_id], outputs=[model_id])
319
 
320
- btn_init = gr.Button("Initialiser le projet & Générer le code (IA)")
321
  out_init = gr.JSON(label="Résultat (Copiez le draft_id)")
322
 
323
 
324
  with gr.Tab("2. Précision de la logique"):
325
  gr.Markdown("Vérifiez et précisez le code Python et l'interface de votre outil.")
326
- with gr.Row():
327
- draft_id_logic = gr.Textbox(label="Draft ID")
328
- python_code = gr.Code(language="python", label="Code Python (ex: def count_r(word): ...)")
329
 
330
  with gr.Row():
331
- # MODIFICATION: Utilisation de gr.Code au lieu de gr.JSON pour une meilleure compatibilité type string
332
- inputs_dict = gr.Code(language="json", label="Inputs (JSON)", value='{"word": "text"}')
333
- with gr.Column():
 
 
 
 
 
 
 
 
 
 
334
  output_desc = gr.Textbox(label="Description de la sortie")
335
  output_component_ui = gr.Dropdown(
336
  label="Type de sortie (Composant Gradio)",
@@ -338,11 +396,8 @@ with gr.Blocks(title="Meta-MCP Fractal") as demo:
338
  value="text",
339
  interactive=True
340
  )
341
-
342
- # MODIFICATION: Utilisation de gr.Code
343
- requirements_box = gr.Code(language="json", label="Requirements (JSON List)", value='[]')
344
 
345
- btn_logic = gr.Button("Générer le code")
346
  out_logic = gr.JSON(label="Résultat")
347
 
348
  btn_logic.click(
@@ -382,44 +437,27 @@ with gr.Blocks(title="Meta-MCP Fractal") as demo:
382
  """
383
 
384
  btn_deploy = gr.Button("Déployer sur Spaces", variant="primary")
385
- out_deploy = gr.Code(language="json", label="Résultat du déploiement")
 
 
 
 
 
 
 
 
 
 
 
386
 
387
  # Mise à jour du résumé quand le draft_id change
388
  draft_id_deploy.change(update_deployment_summary, inputs=[draft_id_deploy], outputs=[deployment_summary])
389
 
390
  # Fonction pour extraire l'URL MCP directe et préremplir le playground
391
- def auto_fill_playground(deploy_result_str: str):
392
- if not deploy_result_str:
393
- return gr.update()
394
-
395
- try:
396
- deploy_result = json.loads(deploy_result_str)
397
- except:
398
- return gr.update()
399
-
400
- if "url" not in deploy_result:
401
  return gr.update()
402
-
403
- # L'URL retournée est de la forme https://huggingface.co/spaces/USER/SPACE
404
- # On veut https://USER-SPACE.hf.space/gradio_api/mcp/
405
- hf_url = deploy_result["url"]
406
-
407
- try:
408
- # Extraction user et space
409
- if "huggingface.co/spaces/" in hf_url:
410
- parts = hf_url.split("huggingface.co/spaces/")
411
- if len(parts) > 1:
412
- path = parts[1].strip("/")
413
- if "/" in path:
414
- user, space = path.split("/", 1)
415
- # Format direct url : https://user-space.hf.space
416
- direct_url = f"https://{user}-{space}.hf.space/gradio_api/mcp/"
417
- return direct_url
418
- except:
419
- pass
420
-
421
- # Fallback si parsing échoue
422
- return hf_url
423
 
424
  # Câblage global des événements (une fois tous les composants définis)
425
  # 1. Init -> Remplissage auto de l'onglet 2 (Logic) et copie de l'ID vers onglet 3 (Deploy)
@@ -480,11 +518,11 @@ with gr.Blocks(title="Meta-MCP Fractal") as demo:
480
  btn_deploy.click(
481
  step_3_deployment,
482
  inputs=[draft_id_deploy],
483
- outputs=out_deploy,
484
  api_name="step_3_deployment"
485
  ).then(
486
  fn=auto_fill_playground,
487
- inputs=[out_deploy],
488
  outputs=[mcp_url_input]
489
  )
490
 
 
15
  # Modèles simplifiés et performants pour le code
16
  COMMON_MODELS = [
17
  "openai/gpt-oss-120b",
18
+ "moonshotai/Kimi-K2-Instruct-0905"
 
 
19
  ]
20
 
21
  PROVIDER_MODELS = {
22
  "together": COMMON_MODELS,
 
23
  "hyperbolic": COMMON_MODELS,
24
  "None": COMMON_MODELS,
25
  # Fallback pour les autres
 
31
 
32
  def step_1_initialisation_and_proposal(project_name, description, model_id, provider_id):
33
  """
34
+ STEP 1: Starts a new tool project and uses AI to propose draft code.
35
+
36
+ Call this AFTER `step_0...`. It initializes the project and sets the optional HF_TOKEN.
37
 
38
  This is the entry point for creating a new MCP tool. It returns a draft_id and a code proposal based on the description.
39
 
 
41
  project_name: The technical name of the tool (e.g., 'weather-fetcher').
42
  description: A natural language description of what the tool should do, or a raw Swagger/OpenAPI JSON specification.
43
  model_id: The LLM model to use for code generation (default: Qwen/Qwen2.5-Coder-32B-Instruct).
44
+ provider_id: The inference provider to use. Options: 'together', 'hyperbolic'.
45
  """
46
  # 1. Initialisation du projet (type 'adhoc' par défaut)
47
  init_result = tools.init_project(project_name, description, type="adhoc")
 
116
  # Simplification: Toujours public, toujours new (écrase/crée), nom du space = nom du projet
117
  result = tools.deploy_to_space(draft_id, visibility="public", space_target="new", target_space_name=None)
118
 
119
+ status_msg = ""
120
+ space_url_val = ""
121
+ mcp_url_val = ""
122
+ claude_config_val = ""
123
+
124
  if "error" not in result:
125
+ space_url_val = result.get('url', '')
126
+ gr.Info(f"Déploiement réussi ! URL : {space_url_val}")
127
+
128
+ status_msg = "### 🚀 Déploiement réussi !"
129
+
130
+ # Construction de l'URL MCP
131
+ mcp_url_val = space_url_val
132
+ tool_name = "my-tool"
133
+
134
+ try:
135
+ if "huggingface.co/spaces/" in space_url_val:
136
+ parts = space_url_val.split("huggingface.co/spaces/")
137
+ if len(parts) > 1:
138
+ path = parts[1].strip("/")
139
+ if "/" in path:
140
+ user, space = path.split("/", 1)
141
+ tool_name = space
142
+ # Format direct url : https://user-space.hf.space
143
+ # Note: pour mcp-remote on utilise le endpoint /gradio_api/mcp/
144
+ mcp_url_val = f"https://{user}-{space}.hf.space/gradio_api/mcp/"
145
+ except Exception:
146
+ pass
147
+
148
+ # Construction de la config Claude
149
+ config_dict = {
150
+ "mcpServers": {
151
+ tool_name: {
152
+ "command": "npx",
153
+ "args": [
154
+ "mcp-remote",
155
+ mcp_url_val,
156
+ "--transport",
157
+ "streamable-http"
158
+ ]
159
+ }
160
+ }
161
+ }
162
+ claude_config_val = json.dumps(config_dict, indent=2)
163
+
164
  else:
165
  gr.Info(f"Échec du déploiement : {result.get('error')}")
166
+ status_msg = f"### ❌ Échec du déploiement\n\nErreur : {result.get('error')}"
167
 
168
+ # Retourne :
169
+ # 1. JSON result (pour out_deploy)
170
+ # 2. Markdown status
171
+ # 3. Space URL
172
+ # 4. MCP URL
173
+ # 5. Claude Config Code
174
+ return json.dumps(result, indent=2), status_msg, space_url_val, mcp_url_val, claude_config_val
175
 
176
  # Récupération des handlers du playground
177
  reload_tools_handler, chat_response_handler = get_playground_ui_handlers()
 
183
  """
184
  STEP 0: Configures the Meta-MCP server environment.
185
 
186
+ This step is needed to set up the Hugging Face environment.
187
 
188
  Args:
189
  hf_user: The Hugging Face username or organization (namespace) where Spaces will be deployed.
 
268
 
269
  # --- Construction de l'interface ---
270
 
271
+ with gr.Blocks(title="MCEPTION") as demo:
272
+ gr.Markdown("# 🏭 MCEPTION is the MCP of your MCPs")
273
  gr.Markdown("Ce serveur permet de créer et déployer d'autres serveurs MCP sur Hugging Face Spaces.")
274
 
275
  with gr.Tab("0. Setup & How-to"):
 
334
  with gr.Tab("1. Initialisation"):
335
  gr.Markdown("Commencez par initialiser un nouveau projet.")
336
 
337
+ project_name = gr.Textbox(label="ex: Nom du projet (ex: strawberry-counter, town-weather)...")
338
 
339
  project_desc = gr.Textbox(
340
  label="Description de l'outil ou Spécification (Swagger/OpenAPI JSON)",
 
365
 
366
  provider_id.change(update_models, inputs=[provider_id], outputs=[model_id])
367
 
368
+ btn_init = gr.Button("Initialiser le projet & proposer le code (IA)")
369
  out_init = gr.JSON(label="Résultat (Copiez le draft_id)")
370
 
371
 
372
  with gr.Tab("2. Précision de la logique"):
373
  gr.Markdown("Vérifiez et précisez le code Python et l'interface de votre outil.")
374
+
375
+ # Afficher le rappel du draft_id en lecture seule pour assurer la propagation
376
+ draft_id_logic = gr.Textbox(label="Draft ID", interactive=False)
377
 
378
  with gr.Row():
379
+ # Colonne de gauche : Code
380
+ with gr.Column(scale=2):
381
+ python_code = gr.Code(language="python", label="Code Python (ex: def count_r(word): ...)")
382
+
383
+ # Colonne de droite : Requirements, Inputs, Outputs
384
+ with gr.Column(scale=1):
385
+ # 1. Requirements
386
+ requirements_box = gr.Code(language="json", label="Requirements (JSON List)", value='[]')
387
+
388
+ # 2. Inputs
389
+ inputs_dict = gr.Code(language="json", label="Inputs (JSON)", value='{"word": "text"}')
390
+
391
+ # 3. Outputs
392
  output_desc = gr.Textbox(label="Description de la sortie")
393
  output_component_ui = gr.Dropdown(
394
  label="Type de sortie (Composant Gradio)",
 
396
  value="text",
397
  interactive=True
398
  )
 
 
 
399
 
400
+ btn_logic = gr.Button("Valider le code")
401
  out_logic = gr.JSON(label="Résultat")
402
 
403
  btn_logic.click(
 
437
  """
438
 
439
  btn_deploy = gr.Button("Déployer sur Spaces", variant="primary")
440
+
441
+ out_status = gr.Markdown("")
442
+
443
+ with gr.Row():
444
+ # Utilisation de gr.Code car gr.Textbox(show_copy_button=True) n'est pas supporté dans cette version de Gradio
445
+ out_space_url = gr.Code(language=None, label="URL du Space Hugging Face", interactive=False, lines=1)
446
+ out_mcp_url = gr.Code(language=None, label="URL du Point d'accès MCP", interactive=False, lines=1)
447
+
448
+ out_claude_config = gr.Code(language="json", label="Configuration Claude Desktop (à ajouter à claude_desktop_config.json)")
449
+
450
+ with gr.Accordion("Détails JSON (Debug)", open=False):
451
+ out_deploy = gr.Code(language="json", label="Résultat Brut")
452
 
453
  # Mise à jour du résumé quand le draft_id change
454
  draft_id_deploy.change(update_deployment_summary, inputs=[draft_id_deploy], outputs=[deployment_summary])
455
 
456
  # Fonction pour extraire l'URL MCP directe et préremplir le playground
457
+ def auto_fill_playground(mcp_url_val: str):
458
+ if not mcp_url_val:
 
 
 
 
 
 
 
 
459
  return gr.update()
460
+ return mcp_url_val
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
461
 
462
  # Câblage global des événements (une fois tous les composants définis)
463
  # 1. Init -> Remplissage auto de l'onglet 2 (Logic) et copie de l'ID vers onglet 3 (Deploy)
 
518
  btn_deploy.click(
519
  step_3_deployment,
520
  inputs=[draft_id_deploy],
521
+ outputs=[out_deploy, out_status, out_space_url, out_mcp_url, out_claude_config],
522
  api_name="step_3_deployment"
523
  ).then(
524
  fn=auto_fill_playground,
525
+ inputs=[out_mcp_url],
526
  outputs=[mcp_url_input]
527
  )
528