#dependances : stdlib uniquement (json, re, urllib). Aucun reseau requis.
import json, re, os
from collections import Counter
from urllib.request import Request, urlopen
from urllib.error import URLError
# --- Chargement du catalogue --------------------------------------------
# Deux sources, dans cet ordre :
# 1. Un endpoint live (optionnel) declare dans .env.
# 2. Le catalogue synthetique Maison Valmont embarque (defaut).
#
# On execute sur le defaut : aucun .env dans le depot, donc aucune donnee
# d'installation reelle n'entre dans les sorties committees.
def charger_catalogue():
url = os.environ.get("MCP_TOOL_CATALOG_URL", "").strip()
if url:
token = os.environ.get("MCP_TOOL_TOKEN", "").strip()
headers = {"Accept": "application/json"}
if token:
headers["Authorization"] = f"Bearer {token}"
try:
req = Request(url, headers=headers)
with urlopen(req, timeout=10) as r:
data = json.loads(r.read().decode("utf-8"))
print(f"Catalogue charge depuis {url} : {len(data)} outils.")
return normaliser(data)
except (URLError, ValueError, OSError) as e:
print(f"Endpoint live injoignable ({e}). Bascule sur le fixture.")
print(f"Catalogue synthetique Maison Valmont : {len(CATALOGUE_VALMONT)} outils.")
return normaliser(CATALOGUE_VALMONT)
def normaliser(outils_bruts):
"""Unifie le format : {name, description, params (dict name->type)}."""
out = []
for o in outils_bruts:
schema = o.get("inputSchema", {}) or {}
props = schema.get("properties", {}) or {}
params = {p: meta.get("type", "?") for p, meta in props.items()}
out.append({
"name": o.get("name", "?"),
"description": o.get("description", ""),
"params": params,
})
return out
CATALOGUE_VALMONT = [
# --- Couche plateforme : CRUD generique (CMS + boutique) ---
{"name": "cms_create_post", "description": "Creer un article.",
"inputSchema": {"type": "object", "properties": {
"title": {"type": "string"}, "content": {"type": "string"},
"status": {"type": "string"}, "author_id": {"type": "integer"},
"slug": {"type": "string"}, "excerpt": {"type": "string"},
"date": {"type": "string"}, "categories": {"type": "array"},
"tags": {"type": "array"}}}},
{"name": "cms_update_post", "description": "Modifier un article.",
"inputSchema": {"type": "object", "properties": {
"post_id": {"type": "integer"}, "title": {"type": "string"},
"content": {"type": "string"}, "status": {"type": "string"},
"slug": {"type": "string"}}}},
{"name": "cms_get_post", "description": "Lire un article.",
"inputSchema": {"type": "object", "properties": {"post_id": {"type": "integer"}}}},
{"name": "cms_list_posts", "description": "Lister les articles.",
"inputSchema": {"type": "object", "properties": {
"status": {"type": "string"}, "author_id": {"type": "integer"},
"limit": {"type": "integer"}, "offset": {"type": "integer"},
"order": {"type": "string"}}}},
{"name": "cms_delete_post", "description": "Supprimer un article.",
"inputSchema": {"type": "object", "properties": {
"post_id": {"type": "integer"}, "force": {"type": "boolean"}}}},
{"name": "cms_list_users", "description": "Lister les comptes.",
"inputSchema": {"type": "object", "properties": {
"role": {"type": "string"}, "limit": {"type": "integer"},
"offset": {"type": "integer"}}}},
{"name": "cms_get_user", "description": "Lire un compte.",
"inputSchema": {"type": "object", "properties": {"user_id": {"type": "integer"}}}},
{"name": "cms_list_media", "description": "Lister les medias.",
"inputSchema": {"type": "object", "properties": {
"limit": {"type": "integer"}, "offset": {"type": "integer"},
"mime_type": {"type": "string"}}}},
{"name": "store_create_product", "description": "Creer un produit.",
"inputSchema": {"type": "object", "properties": {
"name": {"type": "string"}, "sku": {"type": "string"},
"price": {"type": "number"}, "stock": {"type": "integer"},
"description": {"type": "string"}, "categories": {"type": "array"},
"weight": {"type": "number"}}}},
{"name": "store_update_product", "description": "Modifier un produit.",
"inputSchema": {"type": "object", "properties": {
"product_id": {"type": "integer"}, "name": {"type": "string"},
"price": {"type": "number"}, "stock": {"type": "integer"}}}},
{"name": "store_list_orders", "description": "Lister les commandes.",
"inputSchema": {"type": "object", "properties": {
"status": {"type": "string"}, "customer_id": {"type": "integer"},
"limit": {"type": "integer"}, "offset": {"type": "integer"}}}},
{"name": "store_get_order", "description": "Lire une commande.",
"inputSchema": {"type": "object", "properties": {"order_id": {"type": "integer"}}}},
# --- Couche metier : verbes du workflow editorial ---
{"name": "valmont_submit_manuscript", "description": "Deposer un manuscrit.",
"inputSchema": {"type": "object", "properties": {
"author_id": {"type": "integer"}, "title": {"type": "string"},
"genre": {"type": "string"}, "synopsis": {"type": "string"},
"manuscript_file": {"type": "string"}}}},
{"name": "valmont_assign_reviewer", "description": "Assigner un relecteur.",
"inputSchema": {"type": "object", "properties": {
"manuscript_id": {"type": "integer"}, "reviewer_id": {"type": "integer"}}}},
{"name": "valmont_submit_reading_report", "description": "Deposer un rapport de lecture.",
"inputSchema": {"type": "object", "properties": {
"manuscript_id": {"type": "integer"}, "reviewer_id": {"type": "integer"},
"decision": {"type": "string", "enum": ["favorable", "reserve", "defavorable"]},
"comment": {"type": "string"}}}},
{"name": "valmont_transition_manuscript", "description": "Faire passer un manuscrit de statut.",
"inputSchema": {"type": "object", "properties": {
"manuscript_id": {"type": "integer"}, "from_status": {"type": "string"},
"to_status": {"type": "string"}}}},
{"name": "valmont_get_pending_decisions", "description": "Decisions en attente du comite.",
"inputSchema": {"type": "object", "properties": {"committee_id": {"type": "integer"}}}},
{"name": "valmont_get_manuscripts", "description": "Lister les manuscrits.",
"inputSchema": {"type": "object", "properties": {
"status": {"type": "string"}, "author_id": {"type": "integer"},
"limit": {"type": "integer"}, "offset": {"type": "integer"}}}},
{"name": "valmont_request_prereading", "description": "Demander une prelecture assistee.",
"inputSchema": {"type": "object", "properties": {
"manuscript_id": {"type": "integer"}, "environment": {"type": "string"}}}},
{"name": "valmont_notify_author", "description": "Notifier l'autrice d'un evenement.",
"inputSchema": {"type": "object", "properties": {
"manuscript_id": {"type": "integer"}, "event": {"type": "string"}}}},
{"name": "valmont_query_catalog", "description": "Interroger le catalogue indexe.",
"inputSchema": {"type": "object", "properties": {
"query": {"type": "string"}, "environment": {"type": "string"},
"k": {"type": "integer"}}}},
{"name": "valmont_get_processing_status", "description": "Etat du pipeline d'un manuscrit.",
"inputSchema": {"type": "object", "properties": {
"manuscript_id": {"type": "integer"},
"pipeline_stage": {"type": "string",
"enum": ["extraction", "chunking", "indexing", "prereading"]}}}},
]
OUTILS = charger_catalogue()
print(f"Total charge : {len(OUTILS)} outils.")