diff --git a/README.md b/README.md index 31e39e1..73d024a 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Les checks dependants du mirror specs ou des runtimes MCP restent des commandes Options utiles: - `--suite stable` pour le chemin repo-local supporte par defaut -- `--suite mcp` pour les tests MCP locaux (`notion`, `github-dispatch`, `nexar`) +- `--suite mcp` pour les tests MCP locaux (`knowledge-base`, `github-dispatch`, `nexar`) - `--suite all` pour enchainer les deux - `--bootstrap` pour creer le venv cible avant de lancer les tests - `--list` pour afficher exactement les commandes couvertes diff --git a/ai-agentic-embedded-base/specs/README.md b/ai-agentic-embedded-base/specs/README.md index 2372839..4940c0e 100644 --- a/ai-agentic-embedded-base/specs/README.md +++ b/ai-agentic-embedded-base/specs/README.md @@ -13,9 +13,11 @@ Le fichier `constraints.yaml` est la **source de verite** des contraintes non-fo Specs complementaires: - `github_mcp_conversion_spec.md`: prep de conversion de `workflow_dispatch` vers une surface MCP future. +- `cad_modeling_tasks.md`: backlog canonique `FreeCAD/OpenSCAD` pour la stack CAD locale hors MCP. - `kicad_mcp_scope_spec.md`: perimetre fonctionnel, hors scope et criteres d'acceptation du MCP KiCad supporte. +- `mcp_agentics_target_backlog.md`: backlog cible 2026 -> 2028 pour `MCP`, `agentics`, `A2A`, avec ownership par repo. - `mcp_tasks.md`: backlog canonique des actions MCP locales, partage entre runtime, doc et gouvernance. -- `notion_mcp_conversion_spec.md`: prep de conversion du bridge Notion actuel vers une surface MCP future. +- `knowledge_base_mcp_spec.md`: spec canonique du bridge et du MCP knowledge base (`memos` / `docmost`). - `zeroclaw_dual_hw_orchestration_spec.md`: architecture d'orchestration ZeroClaw multi-repo + double materiel. - `zeroclaw_dual_hw_todo.md`: backlog operationnel court terme pour autonomie controlee. diff --git a/ai-agentic-embedded-base/specs/knowledge_base_mcp_spec.md b/ai-agentic-embedded-base/specs/knowledge_base_mcp_spec.md new file mode 100644 index 0000000..6fdd79c --- /dev/null +++ b/ai-agentic-embedded-base/specs/knowledge_base_mcp_spec.md @@ -0,0 +1,61 @@ +# Spec MCP Knowledge Base + +Last updated: 2026-03-08 + +## Objectif + +Documenter l'implementation MCP `knowledge-base` a partir du bridge knowledge base actuel, avec uniquement `memos` et `docmost` dans la pile supportee. + +## Etat actuel + +- backend reel dans `mascarade/core/mascarade/integrations/knowledge_base.py` +- bridge HTTP canonique dans `mascarade/api/src/routes/knowledgeBase.ts` +- UI consommatrice dans `mascarade/web/src/pages/KnowledgeBrowser.tsx` et `crazy_life/src/pages/KnowledgeBrowser.tsx` +- providers supportes: + - `memos` + - `docmost` + +## Surface MCP implemente + +Serveur MCP `knowledge-base` local avec les outils: + +- `search_pages` +- `read_page` +- `append_to_page` +- `create_page` + +## Mapping depuis l'existant + +- `GET /api/knowledge-base/search` -> `search_pages` +- `GET /api/knowledge-base/pages/:pageId` -> `read_page` +- `POST /api/knowledge-base/pages/:pageId/append` -> `append_to_page` +- `POST /api/knowledge-base/pages` -> `create_page` + +## Contraintes + +- garder `stdio` comme transport par defaut +- ne pas exposer un MCP reseau en v1 +- garder le bridge HTTP canonique tant que les clients UI le consomment +- launcher operateur: `tools/run_knowledge_base_mcp.sh` +- erreurs explicites si le provider actif n'est pas configure + +## Providers supportes + +- `memos` + - prerequis: `MEMOS_BASE_URL`, `MEMOS_ACCESS_TOKEN` +- `docmost` + - prerequis: `DOCMOST_BASE_URL`, `DOCMOST_EMAIL`, `DOCMOST_PASSWORD` + +## Hors scope v1 + +- ajout d'un troisieme provider non qualifie +- suppression du bridge HTTP canonique +- edition riche avancee ou blocs proprietaires +- sync bidirectionnelle UI <-> MCP hors actions explicites + +## Validation minimale + +- handshake `initialize -> tools/list` vert +- `search_pages` et `read_page` smokees sur le provider actif +- erreur structuree et non ambigue si le provider actif n'est pas configure +- documentation operateur et matrice de support mises a jour diff --git a/ai-agentic-embedded-base/specs/mcp_tasks.md b/ai-agentic-embedded-base/specs/mcp_tasks.md index 5a313b9..0d23f9b 100644 --- a/ai-agentic-embedded-base/specs/mcp_tasks.md +++ b/ai-agentic-embedded-base/specs/mcp_tasks.md @@ -1,6 +1,6 @@ # Tasks MCP local -Last updated: 2026-03-07 +Last updated: 2026-03-08 Backlog MCP canonique pour `Kill_LIFE`. @@ -17,6 +17,14 @@ Format: ## État courant +- Etat agrege courant: + - `kicad`, `validate-specs` et `huggingface` sont `ready` + - `knowledge-base` est `ready` sur le provider actif `memos`, avec smoke live valide + - `github-dispatch` est `ready`, avec smoke live valide via token GitHub persiste + - le backlog MCP restant est limite a deux blocages specialises constates sur cette machine: + - `K-012`: `pcbnew` absent sur l'hote, donc validation host-native impossible ici + - `K-014`: `NEXAR_TOKEN` absent, donc `nexar_api` reste en mode demo + - [x] K-001 — Rendre `validate-specs` réel - AC: `mcp.json` ne référence plus de chemin absent. @@ -55,6 +63,9 @@ Format: - [ ] K-012 — Rejouer la validation host-native sur une machine avec `pcbnew` - AC: le smoke passe aussi sur le chemin hote, pas seulement via le fallback conteneur. - Helper pret: `python3 tools/hw/kicad_host_mcp_smoke.py --json --quick` degrade proprement si `pcbnew` est absent. + - Derniere verification: `2026-03-08` sur cette machine -> `blocked by host environment` + - Evidence: `python3 tools/hw/kicad_host_mcp_smoke.py --json --quick` + - Resultat: `{"status":"degraded","host_pcbnew_import":"missing","error":"pcbnew not importable on host runtime"}` - [x] K-013 — Décider du statut final des micro-serveurs `kicad_kic_ai` - AC: `component_database`, `kicad_tools` et `nexar_api` sont explicitement promus en surfaces auxiliaires supportées. @@ -62,18 +73,22 @@ Format: - [ ] K-014 — Valider le mode live de `nexar_api` - AC: un run avec `NEXAR_TOKEN` confirme le comportement reel et le distingue du mode demo. - Helper pret: `python3 tools/nexar_mcp_smoke.py --json --live` degrade ou echoue proprement tant que le token ou le mode live manquent. + - Derniere verification: `2026-03-08` sur cette machine -> `blocked by secret/env` + - Evidence: `python3 tools/nexar_mcp_smoke.py --json --live` + - Resultat: `{"status":"degraded","token_configured":false,"demo_mode":true,"error":"NEXAR token missing or server running in demo mode"}` -- [x] K-015 — Implémenter le MCP `Notion` - - AC: `tools/run_notion_mcp.sh` expose `search_pages`, `read_page`, `append_to_page`, `create_page` sans retirer le bridge HTTP en V1. +- [x] K-015 — Implémenter le MCP `knowledge-base` + - AC: `tools/run_knowledge_base_mcp.sh` expose `search_pages`, `read_page`, `append_to_page`, `create_page` sans retirer le bridge HTTP en V1. + - Resultat: le serveur `knowledge-base` est maintenant un MCP de compat vers `KNOWLEDGE_BASE_PROVIDER=memos|docmost`. - [x] K-016 — Implémenter le MCP `GitHub dispatch` - AC: `tools/run_github_dispatch_mcp.sh` expose `list_allowlisted_workflows`, `dispatch_workflow` et `get_dispatch_status` sans retirer la voie API actuelle en V1. - [x] K-017 — Ajouter des smokes MCP dedies hors KiCad - - AC: `validate-specs`, `notion` et `github-dispatch` ont chacun un smoke versionne avec sortie JSON. + - AC: `validate-specs`, `knowledge-base` et `github-dispatch` ont chacun un smoke versionne avec sortie JSON. - [x] K-018 — Etendre l'observabilite MCP a plusieurs serveurs - - AC: `/api/ops/summary` expose un etat agrege et le detail par serveur pour `kicad`, `validate-specs`, `notion` et `github-dispatch`. + - AC: `/api/ops/summary` expose un etat agrege et le detail par serveur pour `kicad`, `validate-specs`, `knowledge-base`, `github-dispatch`, `freecad` et `openscad`. - [x] K-019 — Ajouter un helper de readiness host-native KiCad - AC: `python3 tools/hw/kicad_host_mcp_smoke.py --json --quick` retourne `ready` ou `degraded` sans ambiguite. @@ -83,3 +98,15 @@ Format: - [x] K-021 — Ajouter un rapport MCP local synthetique - AC: `python3 tools/mcp_runtime_status.py --json` agrege les smokes supportes et rend visibles les blocages K-012/K-014. + +- [x] K-022 — Valider la knowledge base active en live via le MCP `knowledge-base` + - AC: un run avec le provider actif et sa cible de smoke confirme `search_pages` et `read_page`. + - Resultat: validation live fermee sur le provider actif `memos` auto-heberge, avec `python3 tools/knowledge_base_mcp_smoke.py --json` en `ready`. + - Variantes supportees: + - `memos`: `MEMOS_BASE_URL` + `MEMOS_ACCESS_TOKEN` + `KNOWLEDGE_BASE_SMOKE_PAGE_ID` optionnel + - `docmost`: `DOCMOST_BASE_URL` + `DOCMOST_EMAIL` + `DOCMOST_PASSWORD` + `KNOWLEDGE_BASE_SMOKE_PAGE_ID` + +- [x] K-023 — Valider `GitHub dispatch MCP` en live + - AC: un run avec `KILL_LIFE_GITHUB_TOKEN` ou un `GitHub App` valide confirme `list_allowlisted_workflows` et `dispatch_workflow` sur une cible autorisee. + - Resultat: validation live fermee via token GitHub persiste dans `runtime-secrets`; le smoke versionne confirme `list_allowlisted_workflows`, `dispatch_workflow` et `get_dispatch_status`. + - Evidence: `python3 tools/github_dispatch_mcp_smoke.py --json --live` diff --git a/ai-agentic-embedded-base/specs/notion_mcp_conversion_spec.md b/ai-agentic-embedded-base/specs/notion_mcp_conversion_spec.md deleted file mode 100644 index 3fde2e8..0000000 --- a/ai-agentic-embedded-base/specs/notion_mcp_conversion_spec.md +++ /dev/null @@ -1,51 +0,0 @@ -# Spec conversion MCP Notion - -Last updated: 2026-03-07 - -## Objectif - -Documenter l'implémentation MCP `notion` à partir de l'intégration `Notion` existante, sans remplacer la voie HTTP existante en V1. - -## État actuel - -- backend réel dans `mascarade/core/mascarade/integrations/notion.py` -- bridge HTTP existant dans `mascarade/api/src/routes/notion.ts` -- UI consommatrice existante dans `crazy_life/src/pages/NotionBrowser.tsx` -- dépendance secrète: `NOTION_API_KEY` - -## Surface MCP implémentée - -Serveur MCP `notion` local avec les outils: - -- `search_pages` -- `read_page` -- `append_to_page` -- `create_page` - -## Mapping depuis l'existant - -- `GET /api/notion/search` -> `search_pages` -- `GET /api/notion/pages/:pageId` -> `read_page` -- `POST /api/notion/pages/:pageId/append` -> `append_to_page` -- `POST /api/notion/pages` -> `create_page` - -## Contraintes - -- garder `stdio` comme transport par défaut -- ne pas exposer un MCP réseau en V1 -- erreurs explicites si `NOTION_API_KEY` absent -- conserver le bridge HTTP existant tant que la migration client n'est pas faite -- launcher opérateur: `tools/run_notion_mcp.sh` - -## Hors scope V1 - -- suppression du bridge HTTP existant -- édition riche avancée ou gestion fine des blocs Notion -- sync bidirectionnelle UI <-> MCP - -## Validation minimale - -- handshake `initialize -> tools/list` vert -- `search_pages` et `read_page` smokés -- erreur structurée et non ambiguë si `NOTION_API_KEY` absent -- documentation opérateur et matrice de support mises à jour diff --git a/ai-agentic-embedded-base/specs/zeroclaw_dual_hw_todo.md b/ai-agentic-embedded-base/specs/zeroclaw_dual_hw_todo.md index 0c7b9b1..f228e17 100644 --- a/ai-agentic-embedded-base/specs/zeroclaw_dual_hw_todo.md +++ b/ai-agentic-embedded-base/specs/zeroclaw_dual_hw_todo.md @@ -64,6 +64,7 @@ Last updated: 2026-02-21 - [x] I-203 - Add local docker compose stack for n8n (`5678`). - [x] I-204 - Add runtime scripts (`integrations_up/down/status`, `import_n8n`). - [ ] I-205 - Validate end-to-end import + activation on local Docker runtime. + - status: Deferred (post-stabilization) - command: `tools/ai/zeroclaw_integrations_up.sh` - command: `tools/ai/zeroclaw_integrations_status.sh` - command: `tools/ai/zeroclaw_integrations_import_n8n.sh` diff --git a/docs/LOCAL_CHANGE_BUNDLES_2026-03-08.md b/docs/LOCAL_CHANGE_BUNDLES_2026-03-08.md index 8e7884f..4efd635 100644 --- a/docs/LOCAL_CHANGE_BUNDLES_2026-03-08.md +++ b/docs/LOCAL_CHANGE_BUNDLES_2026-03-08.md @@ -1,15 +1,19 @@ # Local Change Bundles — 2026-03-08 -But: reduire la derive locale de `Kill_LIFE` en regroupant le worktree en lots revus/publies par sujet. +But: sortir le worktree `Kill_LIFE` en lots publiables reellement alignes sur +le delta courant, et non plus seulement "reviewables par sujet". ## Etat courant -Le worktree local n'est plus un melange de dizaines de sujets heterogenes. Il se lit maintenant en `2` lots principaux: +Le delta `Kill_LIFE` se lit maintenant en `3` lots publiables plus un reliquat +runtime local non versionnable: 1. `mcp-runtime` -2. `python-local` +2. `cad-mcp` +3. `python-local` +4. `.mascarade/` = runtime local genere, a exclure des commits -Le helper de revue associe est: +Le helper de revue associe reste: ```bash bash tools/review_local_change_bundle.sh [status|diff|paths] @@ -19,74 +23,126 @@ bash tools/review_local_change_bundle.sh [status|diff|paths] Objet: -- `runtime home` local explicite pour les launchers MCP et CAD -- smokes MCP alignes sur les modes d'auth reels -- doc operateur associee - -Fichiers: - -- `.gitignore` -- `ai-agentic-embedded-base/specs/mcp_tasks.md` -- `docs/QUICKSTART.md` -- `docs/index.md` -- `docs/RUNTIME_HOME.md` -- `tools/github_dispatch_mcp_smoke.py` -- `tools/hw/kicad_cli.sh` -- `tools/hw/run_kicad_mcp.sh` -- `tools/lib/runtime_home.sh` -- `tools/notion_mcp.py` -- `tools/notion_mcp_smoke.py` -- `tools/run_github_dispatch_mcp.sh` -- `tools/run_nexar_mcp.sh` -- `tools/run_notion_mcp.sh` - -Revue: - -```bash -bash tools/review_local_change_bundle.sh mcp-runtime status -bash tools/review_local_change_bundle.sh mcp-runtime diff -``` - -## Lot 2 — `python-local` - -Objet: - -- bootstrap Python repo-local stable -- commande de tests repo-locale stable -- harness de tests ajustes pour ce chemin supporte +- remplacer les anciennes surfaces `notion` par `knowledge-base` +- figer le `runtime home` local et le statut MCP courant +- aligner GitHub dispatch et la doc MCP sur le contrat reel Fichiers: - `README.md` -- `test/test_openclaw_sanitizer.py` -- `tools/bootstrap_python_env.sh` -- `tools/hw/schops/tests/test_rules_engine.py` -- `tools/test_python.sh` +- `ai-agentic-embedded-base/specs/README.md` +- `ai-agentic-embedded-base/specs/knowledge_base_mcp_spec.md` +- `ai-agentic-embedded-base/specs/mcp_tasks.md` +- `ai-agentic-embedded-base/specs/notion_mcp_conversion_spec.md` +- `ai-agentic-embedded-base/specs/zeroclaw_dual_hw_todo.md` +- `docs/LOCAL_CHANGE_BUNDLES_2026-03-08.md` +- `docs/MCP_ECOSYSTEM_MATRIX.md` +- `docs/MCP_SETUP.md` +- `docs/MCP_SUPPORT_MATRIX.md` +- `docs/RUNTIME_HOME.md` +- `docs/plans/15_plan_mcp_runtime_alignment.md` +- `docs/plans/README.md` +- `mcp.json` +- `specs/README.md` +- `specs/knowledge_base_mcp_spec.md` +- `specs/mcp_tasks.md` +- `specs/notion_mcp_conversion_spec.md` +- `test/test_knowledge_base_mcp.py` +- `test/test_notion_mcp.py` +- `tools/github_dispatch_mcp_smoke.py` +- `tools/knowledge_base_mcp.py` +- `tools/knowledge_base_mcp_smoke.py` +- `tools/lib/runtime_home.sh` +- `tools/mcp_runtime_status.py` +- `tools/mcp_smoke_common.py` +- `tools/notion_mcp.py` +- `tools/notion_mcp_smoke.py` +- `tools/review_local_change_bundle.sh` +- `tools/run_github_dispatch_mcp.sh` +- `tools/run_knowledge_base_mcp.sh` +- `tools/run_notion_mcp.sh` -Revue: +Validation minimale: ```bash -bash tools/review_local_change_bundle.sh python-local status -bash tools/review_local_change_bundle.sh python-local diff +cd /home/clems/Kill_LIFE && bash tools/test_python.sh --suite stable +cd /home/clems/Kill_LIFE && python3 tools/validate_specs.py --json ``` +## Lot 2 — `cad-mcp` + +Objet: + +- pile CAD/MCP locale (`FreeCAD`, `OpenSCAD`, compose CAD, runtime status) +- specs, plans et docs operateur associes +- smokes et launchers dedies a cette pile + +Fichiers: + +- `Makefile` +- `ai-agentic-embedded-base/specs/cad_modeling_tasks.md` +- `ai-agentic-embedded-base/specs/mcp_agentics_target_backlog.md` +- `deploy/cad/README.md` +- `deploy/cad/Dockerfile.openscad-headless` +- `deploy/cad/docker-compose.yml` +- `docs/plans/16_plan_cad_modeling_stack.md` +- `docs/plans/17_plan_target_architecture_mcp_agentics_2028.md` +- `specs/cad_modeling_tasks.md` +- `specs/mcp_agentics_target_backlog.md` +- `test/test_freecad_mcp.py` +- `test/test_openscad_mcp.py` +- `tools/cad_runtime.py` +- `tools/freecad_mcp.py` +- `tools/freecad_mcp_smoke.py` +- `tools/hw/cad_stack.sh` +- `tools/hw/freecad_smoke.py` +- `tools/hw/openscad_smoke.py` +- `tools/mcp_telemetry.py` +- `tools/openscad_mcp.py` +- `tools/openscad_mcp_smoke.py` +- `tools/run_freecad_mcp.sh` +- `tools/run_openscad_mcp.sh` + +Validation minimale: + +```bash +cd /home/clems/Kill_LIFE && bash tools/hw/cad_stack.sh doctor +cd /home/clems/Kill_LIFE && bash tools/hw/cad_stack.sh doctor-mcp +``` + +## Lot 3 — `python-local` + +Objet: + +- garder un chemin de test repo-local stable et publiable +- maintenir la commande Python minimale supportee dans le delta courant + +Fichiers: + +- `tools/test_python.sh` + Validation repo-locale: ```bash -bash tools/bootstrap_python_env.sh -bash tools/test_python.sh --suite stable +cd /home/clems/Kill_LIFE && bash tools/test_python.sh --suite stable ``` +## Exclusion explicite + +Ne pas versionner le runtime local genere: + +- `.mascarade/mcp/github-dispatch/*.json` + +Ces fichiers servent d'evidence locale runtime, pas de source de verite repo. + ## Ordre recommande -1. publier ou reviewer `mcp-runtime` -2. publier ou reviewer `python-local` +1. publier `mcp-runtime` +2. publier `cad-mcp` +3. publier `python-local` Ce decoupage evite de melanger: -- la hygiene runtime des launchers MCP -- le contrat operateur Python repo-local - -## Note - -Les tests MCP d'integration (`--suite mcp`) restent un lot de verification distinct du chemin repo-local stable. Ils dependent encore des launchers et des runtimes compagnons, donc ils ne servent pas de gate minimale pour le lot `python-local`. +- la migration `notion -> knowledge-base` et la hygiene runtime MCP +- la pile CAD/MCP et ses smokes associes +- le contrat Python repo-local minimal diff --git a/docs/MCP_ECOSYSTEM_MATRIX.md b/docs/MCP_ECOSYSTEM_MATRIX.md index b0dbc5d..66d415e 100644 --- a/docs/MCP_ECOSYSTEM_MATRIX.md +++ b/docs/MCP_ECOSYSTEM_MATRIX.md @@ -1,6 +1,6 @@ # MCP ecosystem matrix -Last updated: 2026-03-07 +Last updated: 2026-03-08 Matrice transverse des surfaces MCP et non-MCP observees dans `Kill_LIFE`, `mascarade` et `crazy_life`. @@ -18,44 +18,43 @@ Matrice transverse des surfaces MCP et non-MCP observees dans `Kill_LIFE`, `masc | --- | --- | --- | --- | --- | --- | | `kicad` | `Kill_LIFE` + `mascarade` | serveur MCP | `tools/hw/run_kicad_mcp.sh` | supporte | runtime KiCad canonique; implementation dans `mascarade/finetune/kicad_mcp_server` | | `validate-specs` | `Kill_LIFE` | serveur MCP | `python3 tools/validate_specs.py --mcp` | supporte | validation repo/specs; pas un runtime CAD | -| `notion` | `Kill_LIFE` + `mascarade` | serveur MCP | `tools/run_notion_mcp.sh` | supporte avec dependance externe | MCP local branche sur `mascarade/core/mascarade/integrations/notion.py`; garde le bridge HTTP existant | -| `github-dispatch` | `Kill_LIFE` + `mascarade` | serveur MCP | `tools/run_github_dispatch_mcp.sh` | supporte avec dependance externe | MCP local branche sur `mascarade/core/mascarade/integrations/github_dispatch.py`; garde l'API directe existante | +| `knowledge-base` | `Kill_LIFE` + `mascarade` | serveur MCP | `tools/run_knowledge_base_mcp.sh` | supporte avec dependance externe | serveur MCP de compat sur la knowledge base configuree (`memos` ou `docmost`); validation live fermee sur le provider actif `memos` auto-heberge | +| `github-dispatch` | `Kill_LIFE` + `mascarade` | serveur MCP | `tools/run_github_dispatch_mcp.sh` | supporte avec dependance externe | serveur stable, validation live fermee via token GitHub persiste dans `runtime-secrets` | +| `freecad` | `Kill_LIFE` + `mascarade` | serveur MCP | `tools/run_freecad_mcp.sh` | supporte | serveur MCP headless local base sur `FreeCADCmd` | +| `openscad` | `Kill_LIFE` + `mascarade` | serveur MCP | `tools/run_openscad_mcp.sh` | supporte | serveur MCP headless local stateless base sur `openscad` | | `component_database` | `mascarade` | micro-serveur MCP | `python3 -m mcp_servers.component_db` | supporte avec dependance externe | serveur auxiliaire; depend du cache KiCad v10 et du repo compagnon | | `kicad_tools` | `mascarade` | micro-serveur MCP | `python3 -m mcp_servers.kicad_tools` | supporte avec dependance externe | serveur auxiliaire; depend des fichiers KiCad reels et du repo compagnon | -| `nexar_api` | `Kill_LIFE` + `mascarade` | micro-serveur MCP | `tools/run_nexar_mcp.sh` | experimental | wrapper `Kill_LIFE` sur le serveur auxiliaire `mascarade`; mode demo sans token, validation live encore ouverte | +| `nexar_api` | `Kill_LIFE` + `mascarade` | micro-serveur MCP | `tools/run_nexar_mcp.sh` | experimental | mode demo sans token; validation live encore ouverte | ## 2. Consommateurs et configs MCP | Surface | Repo principal | Type | Point d'entree | Statut | Notes | | --- | --- | --- | --- | --- | --- | -| `mcp.json` | `Kill_LIFE` | config consommateur MCP | `mcp.json` | supporte | reference `kicad`, `validate-specs`, `notion` et `github-dispatch` | +| `mcp.json` | `Kill_LIFE` | config consommateur MCP | `mcp.json` | supporte | reference `kicad`, `validate-specs`, `knowledge-base`, `github-dispatch`, `freecad`, `openscad` | | `MCP setup` | `Kill_LIFE` | doc operateur MCP | `docs/MCP_SETUP.md` | supporte | source de verite d'usage local | -| `KiCad plugin MCP config` | `mascarade` | config plugin MCP | `finetune/kicad_kic_ai/plugins/mcp_config.json` | supporte avec dependance externe | concerne les micro-serveurs auxiliaires, pas le runtime canonique | -| `ops MCP probe` | `mascarade` | observabilite synthetique | `/api/ops/summary` via `api/src/routes/ops.ts` | supporte avec dependance externe | expose l'etat agrege de `kicad`, `validate-specs`, `notion` et `github-dispatch`, plus le detail par serveur | -| `crazy_life MCP positionnement` | `crazy_life` | doc de non-ownership | `docs/MCP_PLAN_2026-03-07.md` | supporte | `crazy_life` ne porte pas de serveur MCP | +| `ops MCP probe` | `mascarade` | observabilite synthetique | `/api/ops/summary` via `api/src/routes/ops.ts` | supporte avec dependance externe | expose l'etat agrege de `kicad`, `validate-specs`, `knowledge-base`, `github-dispatch`, `freecad`, `openscad` et des surfaces distantes suivies par le cockpit | +| `crazy_life MCP positionnement` | `crazy_life` | doc de non-ownership / cockpit | `docs/MCP_PLAN_2026-03-07.md` | supporte | `crazy_life` consomme l'etat MCP et les probes, sans porter de serveur | ## 3. Integrations tierces non-MCP | Surface | Repo principal | Type | Point d'entree | Dependance | Statut | Notes | | --- | --- | --- | --- | --- | --- | --- | -| `Notion bridge` | `mascarade` | API HTTP interne | `/api/notion/*` via `api/src/routes/notion.ts` | `NOTION_API_KEY` | supporte | bridge backend vers le client Notion, conserve en parallele du MCP `notion` | -| `Notion client core` | `mascarade` | integration SDK | `core/mascarade/integrations/notion.py` | `NOTION_API_KEY` | supporte | source de verite backend Notion | -| `NotionBrowser UI` | `crazy_life` | UI consommatrice | `src/pages/NotionBrowser.tsx` | backend `mascarade` | supporte | consomme encore le bridge HTTP Notion; migration MCP possible ensuite | +| `Knowledge base bridge` | `mascarade` | API HTTP interne | `/api/knowledge-base/*` via `api/src/routes/knowledgeBase.ts` | provider actif + credentials associes | supporte | bridge HTTP de compat vers la knowledge base configuree | +| `Knowledge base core` | `mascarade` | integration provider-aware | `core/mascarade/integrations/knowledge_base.py` | provider actif + credentials associes | supporte | source de verite backend pour `memos` et `docmost` | | `GitHub dispatch` | `mascarade` | API GitHub directe | `api/src/lib/killlife.ts` | `KILL_LIFE_GITHUB_TOKEN` ou `GITHUB_TOKEN` | supporte | lance `workflow_dispatch`, conserve en parallele du MCP `github-dispatch` | -| `GitHub dispatch` | `crazy_life` | API GitHub directe | `api/src/lib/killlife.ts` | `KILL_LIFE_GITHUB_TOKEN` ou `GITHUB_TOKEN` | supporte | miroir cote repo canonique frontend/API; migration MCP possible ensuite | -| `KillLife workflow editors` | `mascarade` + `crazy_life` | UI orchestration | `web/src/pages/KillLifeWorkflowEditor.tsx`, `src/pages/KillLifeWorkflowEditor.tsx`, `src/pages/CrazyLaneEditor.tsx` | backend API | supporte | exposent `github-dispatch` comme type de noeud, pas comme MCP | +| `FreeCAD agent` | `mascarade` | agent metier | `core/mascarade/agents/freecad_agent.py` | backend LLM | supporte | reste un agent de guidance; l'execution outillee passe par le MCP `freecad` pour la suite cible | +| `Workflow editors` | `mascarade` + `crazy_life` | UI orchestration | `web/src/pages/KillLifeWorkflowEditor.tsx`, `src/pages/KillLifeWorkflowEditor.tsx`, `src/pages/CrazyLaneEditor.tsx` | backend API | supporte | exposent `github-dispatch` comme type de noeud, pas comme serveur MCP | ## 4. Surfaces infra-only ou migration | Surface | Repo principal | Type | Point d'entree | Statut | Notes | | --- | --- | --- | --- | --- | --- | -| `openmemory-mcp / mem0` | `mascarade` | conteneur MCP tiers | `deploy/migration/compose.tools.ai.yml` | infra-only | profil `heavy`, non documente comme point d'entree canonique, non observe en execution sur cette machine | +| `openmemory-mcp / mem0` | `mascarade` | conteneur MCP tiers | `deploy/migration/compose.tools.ai.yml` | infra-only | profil `heavy`, non documente comme point d'entree canonique | | `kicad-sch-mcp` | docs historiques | ancien serveur MCP externe | documentation seulement | non supporte | pas package ni retenu dans le workspace actuel | ## 5. Conclusion -- le point d'entree operateur MCP de `Kill_LIFE` reste la surface `kicad` lancee par `tools/hw/run_kicad_mcp.sh` -- `Notion` et `GitHub dispatch` disposent aussi d'un serveur MCP local, mais leur logique applicative reste fournie par `mascarade` -- `crazy_life` consomme des surfaces applicatives, mais n'own pas de serveur MCP -- `mem0/openmemory-mcp` reste une brique d'infra optionnelle, hors chaine operateur canonique -- les surfaces auxiliaires `kicad_kic_ai` ne doivent pas etre lues comme equivalentes au point d'entree operateur `kicad` +- `Kill_LIFE` porte maintenant des MCP locaux supportes pour `kicad`, `validate-specs`, `knowledge-base`, `github-dispatch`, `freecad` et `openscad` +- `mascarade` porte l'agregation ops et les integrations applicatives encore existantes en parallele +- `crazy_life` consomme et supervise, mais n'own pas de serveur MCP +- `A2A` reste ferme tant que l'orchestrateur n'est pas branche sur les MCP specialises et que l'observabilite MCP n'est pas completement homogene diff --git a/docs/MCP_SETUP.md b/docs/MCP_SETUP.md index 98e7396..06f7efd 100644 --- a/docs/MCP_SETUP.md +++ b/docs/MCP_SETUP.md @@ -1,54 +1,56 @@ # MCP setup +Last updated: 2026-03-08 + Source canonique pour l'usage MCP local de `Kill_LIFE`. References canoniques: -- spec de perimetre: `specs/kicad_mcp_scope_spec.md` +- spec de perimetre KiCad: `specs/kicad_mcp_scope_spec.md` - matrice de support: `docs/MCP_SUPPORT_MATRIX.md` - matrice ecosysteme: `docs/MCP_ECOSYSTEM_MATRIX.md` -- backlog MCP: `specs/mcp_tasks.md` +- backlog MCP KiCad: `specs/mcp_tasks.md` +- backlog cible MCP/agentics: `specs/mcp_agentics_target_backlog.md` ## Source de verite et ownership -- `Kill_LIFE` own le lancement, la consommation et la gouvernance documentaire MCP locale -- `mascarade` own l'implementation du serveur KiCad principal et l'observabilite compagnon +- `Kill_LIFE` own le lancement, les smokes et la gouvernance documentaire MCP locale +- `mascarade` own l'agregation ops et l'observabilite compagnon - `specs/` a la racine de `Kill_LIFE` est la source de verite canonique - `ai-agentic-embedded-base/specs/` n'est qu'un miroir exporte ## Chemins supportes -- serveur MCP KiCad supporte: `../mascarade/finetune/kicad_mcp_server` -- launcher supporte cote `Kill_LIFE`: `tools/hw/run_kicad_mcp.sh` -- alias operateur supporte: `tools/hw/cad_stack.sh mcp` -- serveur auxiliaire supporte: `python3 tools/validate_specs.py --mcp` -- serveur MCP Notion supporte: `tools/run_notion_mcp.sh` -- serveur MCP GitHub dispatch supporte: `tools/run_github_dispatch_mcp.sh` -- transport supporte: `stdio` local uniquement - -`kicad-sch-mcp` n'est plus un chemin supporte dans ce repo. Il reste un ancien axe documentaire, pas un runtime principal. +- `kicad`: `tools/hw/run_kicad_mcp.sh` +- `validate-specs`: `python3 tools/validate_specs.py --mcp` +- `knowledge-base`: `tools/run_knowledge_base_mcp.sh` +- `github-dispatch`: `tools/run_github_dispatch_mcp.sh` +- `freecad`: `tools/run_freecad_mcp.sh` +- `openscad`: `tools/run_openscad_mcp.sh` +- transport supporte: `stdio` local uniquement, sauf surface distante explicitement declaree par le cockpit compagnon ## Ce qui est reellement supporte -- `kicad`: runtime MCP KiCad canonique, avec `tools`, `resources` et `prompts` -- `validate-specs`: validation repo/specs cote `Kill_LIFE`, sans role CAD -- `notion`: MCP local sur le backend Notion existant de `mascarade` +- `kicad`: runtime MCP CAD canonique, avec `tools`, `resources` et `prompts` +- `validate-specs`: validation repo/specs cote `Kill_LIFE` +- `knowledge-base`: MCP local de compat sur la knowledge base configuree dans `mascarade` (`memos` ou `docmost`) - `github-dispatch`: MCP local pour workflows GitHub allowlistes +- `freecad`: MCP local headless pour infos runtime, document minimal, export et script Python controle +- `openscad`: MCP local headless pour infos runtime, validation, rendu et export -Les micro-serveurs `kicad_kic_ai` de `mascarade` sont suivis comme surfaces auxiliaires. Ils ne sont pas des points d'entree operateur `Kill_LIFE`, et leur statut doit etre lu avec leurs dependances externes dans `docs/MCP_SUPPORT_MATRIX.md`. +Les micro-serveurs `kicad_kic_ai` de `mascarade` restent suivis comme surfaces auxiliaires dans `docs/MCP_SUPPORT_MATRIX.md`. ## Prerequis - le repo compagnon `mascarade` existe en voisin (`../mascarade`) ou via `MASCARADE_DIR` -- `node` est disponible sur la machine -- le serveur est builde dans `mascarade/finetune/kicad_mcp_server/dist/index.js` -- le venv `mascarade/core/.venv` existe, ou `MASCARADE_CORE_PYTHON` pointe vers un Python avec `notion-client` et `httpx` -- Docker est disponible pour le fallback conteneur KiCad v10 -- `pcbnew` cote hote est optionnel: s'il est absent, le launcher bascule vers le conteneur supporte -- `NOTION_API_KEY` est requis pour les outils `notion` -- `KILL_LIFE_GITHUB_TOKEN` ou `GITHUB_TOKEN` est requis pour `dispatch_workflow` - -Le runtime prepare un environnement writable sous `.cad-home/kicad-mcp/` et exporte `KICAD_MCP_DATA_DIR` pour eviter les ecritures dans un prefixe immuable. +- Docker est disponible pour `kicad`, `freecad` et `openscad` +- `node` est disponible pour le build/API compagnon +- le venv `mascarade/core/.venv` existe, ou `MASCARADE_CORE_PYTHON` pointe vers un Python avec les dependances knowledge base (`httpx`) +- `KNOWLEDGE_BASE_PROVIDER` selectionne `memos` ou `docmost` +- selon le provider: + - `memos`: `MEMOS_BASE_URL` et `MEMOS_ACCESS_TOKEN` + - `docmost`: `DOCMOST_BASE_URL`, `DOCMOST_EMAIL` et `DOCMOST_PASSWORD` +- `KILL_LIFE_GITHUB_TOKEN`, `GITHUB_TOKEN` ou une configuration `GitHub App` est requise pour la validation live de `github-dispatch` ## Diagnostic rapide @@ -56,31 +58,25 @@ Depuis `Kill_LIFE`: ```bash tools/hw/run_kicad_mcp.sh --doctor -tools/hw/cad_stack.sh mcp --doctor -tools/run_notion_mcp.sh --doctor +tools/run_knowledge_base_mcp.sh --doctor tools/run_github_dispatch_mcp.sh --doctor -tools/run_nexar_mcp.sh --doctor -python3 tools/mcp_runtime_status.py --json +tools/run_freecad_mcp.sh --doctor +tools/run_openscad_mcp.sh --doctor python3 tools/validate_specs_mcp_smoke.py --json --quick -python3 tools/notion_mcp_smoke.py --json --quick +python3 tools/knowledge_base_mcp_smoke.py --json --quick python3 tools/github_dispatch_mcp_smoke.py --json --quick -python3 tools/nexar_mcp_smoke.py --json -python3 tools/hw/kicad_host_mcp_smoke.py --json --quick -python3 tools/hw/mcp_smoke.py --json --quick --timeout 30 -python3 tools/hw/mcp_smoke.py --timeout 30 -python3 tools/validate_specs.py --json +python3 tools/freecad_mcp_smoke.py --json +python3 tools/openscad_mcp_smoke.py --json +python3 tools/hw/freecad_smoke.py --json +python3 tools/hw/openscad_smoke.py --json +python3 tools/mcp_runtime_status.py --json ``` -Sur cette machine auditee, le smoke KiCad passe via le fallback conteneur: +Sur la machine de reference: -- `HOST_PCBNEW_IMPORT=missing` -- `CONTAINER_STATUS=available` -- `python3 tools/hw/mcp_smoke.py --timeout 30` passe -- `python3 tools/notion_mcp_smoke.py --json --quick` retourne `degraded` tant que `NOTION_API_KEY` est absent -- `python3 tools/github_dispatch_mcp_smoke.py --json --quick` retourne `degraded` tant que le token GitHub est absent -- `python3 tools/mcp_runtime_status.py --json` agrege les smokes MCP locaux et remonte les blocages K-012/K-014 -- `python3 tools/nexar_mcp_smoke.py --json` retourne `degraded` tant que `NEXAR_TOKEN` ou `NEXAR_API_KEY` est absent -- `python3 tools/hw/kicad_host_mcp_smoke.py --json --quick` retourne `degraded` tant que `pcbnew` n'est pas importable cote hote +- `kicad`, `validate-specs`, `freecad` et `openscad` sont `ready` +- `knowledge-base` est `ready` sur le provider actif `memos` auto-heberge +- `github-dispatch` est `ready`, avec validation live fermee via token GitHub persiste ## Configuration locale @@ -101,10 +97,10 @@ Le fichier versionne [mcp.json](../mcp.json) pointe vers les serveurs MCP reelle "args": ["tools/validate_specs.py", "--mcp"], "tools": ["*"] }, - "notion": { + "knowledge-base": { "type": "local", "command": "bash", - "args": ["tools/run_notion_mcp.sh"], + "args": ["tools/run_knowledge_base_mcp.sh"], "tools": ["*"] }, "github-dispatch": { @@ -112,6 +108,18 @@ Le fichier versionne [mcp.json](../mcp.json) pointe vers les serveurs MCP reelle "command": "bash", "args": ["tools/run_github_dispatch_mcp.sh"], "tools": ["*"] + }, + "freecad": { + "type": "local", + "command": "bash", + "args": ["tools/run_freecad_mcp.sh"], + "tools": ["*"] + }, + "openscad": { + "type": "local", + "command": "bash", + "args": ["tools/run_openscad_mcp.sh"], + "tools": ["*"] } } } @@ -123,52 +131,33 @@ Depuis `Kill_LIFE`: ```bash tools/hw/run_kicad_mcp.sh -tools/hw/cad_stack.sh mcp python3 tools/validate_specs.py --mcp -tools/run_notion_mcp.sh +tools/run_knowledge_base_mcp.sh tools/run_github_dispatch_mcp.sh +tools/run_freecad_mcp.sh +tools/run_openscad_mcp.sh ``` -Le smoke `tools/hw/mcp_smoke.py` valide actuellement la surface cible suivante: - -- `initialize` -- `tools/list` -- `resources/list` -- `prompts/list` -- creation de projet -- lecture de resources stables -- lecture d'un prompt stable - Les smokes dedies supplementaires sont: -- `tools/validate_specs_mcp_smoke.py`: handshake MCP de `validate-specs` -- `tools/notion_mcp_smoke.py`: handshake MCP de `notion`, puis validation live si le secret est disponible -- `tools/github_dispatch_mcp_smoke.py`: handshake MCP de `github-dispatch`, puis validation allowlist et live optionnelle +- `tools/validate_specs_mcp_smoke.py` +- `tools/knowledge_base_mcp_smoke.py` +- `tools/github_dispatch_mcp_smoke.py` +- `tools/freecad_mcp_smoke.py` +- `tools/openscad_mcp_smoke.py` -Le chemin d'observabilite synthetique n'est pas fourni par `Kill_LIFE` seul. Si la stack compagnon `mascarade` tourne, `/api/ops/summary` expose un bloc `mcp` qui remonte le statut, le runtime reellement choisi, la version de protocole et les compteurs de surface. - -## Outils auxiliaires - -- `python3 tools/mcp_runtime_status.py --json` est le rapport MCP local synthetique de premier niveau pour les smokes supportes -- `tools/run_nexar_mcp.sh` et `python3 tools/nexar_mcp_smoke.py` sont des helpers experimentaux pour qualifier `nexar_api` sans le promouvoir en point d'entree operateur -- `tools/hw/kicad_host_mcp_smoke.py` est un helper de readiness pour verifier explicitement le chemin host-native -- `tools/hw/sync_kicad_v10_libs.sh` est un helper auxiliaire optionnel, hors chemin operateur principal, pour prechauffer les libs/cache KiCad v10 des micro-serveurs auxiliaires -- ce helper depend: - - d'une image Docker locale `kill_life_cad-kicad-mcp:latest` - - du repo compagnon `mascarade` -- ce helper n'est pas requis pour lancer le runtime `kicad` canonique +Le chemin d'observabilite synthetique n'est pas fourni par `Kill_LIFE` seul. Si la stack compagnon `mascarade` tourne, `/api/ops/summary` expose un bloc `mcp` agrege qui remonte `kicad`, `validate-specs`, `knowledge-base`, `github-dispatch`, `freecad`, `openscad` et les surfaces distantes suivies par le cockpit. ## Politique de support - `stdio` reste le seul transport supporte par defaut -- aucun serveur MCP reseau n'est expose par defaut -- `tools/hw/cad_stack.sh mcp` est un alias supporte du launcher canonique -- le runtime KiCad canonique est celui de `mascarade/finetune/kicad_mcp_server` -- `validate-specs` reste un MCP auxiliaire repo/specs, pas un runtime CAD -- `notion` et `github-dispatch` restent en `stdio` local et reutilisent les backends existants -- les micro-serveurs `kicad_kic_ai` sont des surfaces auxiliaires suivies, mais restent hors chemin operateur `Kill_LIFE` +- aucun serveur MCP reseau n'est expose par defaut pour ces surfaces locales +- `knowledge-base` et `github-dispatch` sont valides en live sur la machine de reference +- `freecad` et `openscad` sont supportes comme serveurs MCP headless locaux +- `kicad` reste le runtime MCP CAD principal ## Points encore ouverts -- le chemin host-native avec `pcbnew` doit encore etre revalide sur une machine qui expose reellement KiCad Python -- `nexar_api` doit encore etre valide en mode live avec `NEXAR_TOKEN` +- finir l'observabilite MCP homogene +- requalifier l'ouverture future de `A2A` une fois l'observabilite MCP homogene fermee +- fermer `K-012` sur une machine avec `pcbnew` host-native et `K-014` en mode `nexar_api` live diff --git a/docs/MCP_SUPPORT_MATRIX.md b/docs/MCP_SUPPORT_MATRIX.md index a839727..7757a69 100644 --- a/docs/MCP_SUPPORT_MATRIX.md +++ b/docs/MCP_SUPPORT_MATRIX.md @@ -1,6 +1,6 @@ # MCP support matrix -Last updated: 2026-03-07 +Last updated: 2026-03-08 Matrice canonique du statut MCP pour `Kill_LIFE`, `mascarade` et les surfaces associees. @@ -19,11 +19,13 @@ Vue plus large de l'ecosysteme: | Surface | Point d'entree | Ownership | Protocole observe / declare | Statut | Notes | | --- | --- | --- | --- | --- | --- | -| `kicad` | `tools/hw/run_kicad_mcp.sh` | launcher `Kill_LIFE`, serveur `mascarade/finetune/kicad_mcp_server` | `2025-03-26` observe au smoke | supporte | runtime KiCad canonique; `tools/hw/cad_stack.sh mcp` est un alias supporte | +| `kicad` | `tools/hw/run_kicad_mcp.sh` | launcher `Kill_LIFE`, implementation `mascarade/finetune/kicad_mcp_server` | `2025-03-26` observe au smoke | supporte | runtime KiCad canonique; `tools/hw/cad_stack.sh mcp` est un alias supporte | | `validate-specs` | `python3 tools/validate_specs.py --mcp` | `Kill_LIFE` | `2025-03-26` observe en test | supporte | validation repo/specs; ne remplace pas le runtime KiCad | -| `notion` | `tools/run_notion_mcp.sh` | launcher `Kill_LIFE`, backend `mascarade/core/mascarade/integrations/notion.py` | `2025-03-26` observe en test | supporte avec dependance externe | MCP local branche sur le backend Notion de `mascarade`; requiert `NOTION_API_KEY` et le repo compagnon | -| `github-dispatch` | `tools/run_github_dispatch_mcp.sh` | launcher `Kill_LIFE`, backend `mascarade/core/mascarade/integrations/github_dispatch.py` | `2025-03-26` observe en test | supporte avec dependance externe | MCP local pour workflows allowlistes; requiert `KILL_LIFE_GITHUB_TOKEN` ou `GITHUB_TOKEN` et le repo compagnon | -| `component_database` | `python3 -m mcp_servers.component_db` | `mascarade/finetune/kicad_kic_ai` | `2025-03-26` observe au handshake | supporte avec dependance externe | micro-serveur auxiliaire; depend du repo compagnon `mascarade`, du cache KiCad v10 et d'un index local prechauffe | +| `knowledge-base` | `tools/run_knowledge_base_mcp.sh` | launcher `Kill_LIFE`, backend `mascarade/core/mascarade/integrations/knowledge_base.py` | `2025-03-26` observe en test | supporte avec dependance externe | serveur MCP de compat vers la knowledge base configuree (`memos` ou `docmost`); validation live fermee sur le provider actif `memos` auto-heberge | +| `github-dispatch` | `tools/run_github_dispatch_mcp.sh` | launcher `Kill_LIFE`, backend `mascarade/core/mascarade/integrations/github_dispatch.py` | `2025-03-26` observe en test | supporte avec dependance externe | serveur local stable; validation live fermee via token GitHub persiste dans `runtime-secrets` | +| `freecad` | `tools/run_freecad_mcp.sh` | runtime `Kill_LIFE`, ops `mascarade` | `2025-03-26` observe au smoke | supporte | MCP local headless pour infos runtime, creation minimale, export et script controle | +| `openscad` | `tools/run_openscad_mcp.sh` | runtime `Kill_LIFE`, ops `mascarade` | `2025-03-26` observe au smoke | supporte | MCP local headless stateless pour validation, rendu et export | +| `component_database` | `python3 -m mcp_servers.component_db` | `mascarade/finetune/kicad_kic_ai` | `2025-03-26` observe au handshake | supporte avec dependance externe | micro-serveur auxiliaire; depend du cache KiCad v10 et du repo compagnon | | `kicad_tools` | `python3 -m mcp_servers.kicad_tools` | `mascarade/finetune/kicad_kic_ai` | `2025-03-26` observe au handshake | supporte avec dependance externe | micro-serveur auxiliaire; analyses reelles si les fichiers KiCad et dependances associees sont disponibles | | `nexar_api` | `tools/run_nexar_mcp.sh` | launcher `Kill_LIFE`, serveur `mascarade/finetune/kicad_kic_ai/mcp_servers/nexar.py` | `2025-03-26` observe au handshake | experimental | micro-serveur auxiliaire; sans `NEXAR_TOKEN`, reste en mode demo; validation live encore ouverte | @@ -35,15 +37,12 @@ Vue plus large de l'ecosysteme: ## Decisions importantes -- `Kill_LIFE` n'own pas de second serveur KiCad concurrent -- `stdio` reste le seul transport supporte par defaut -- le runtime KiCad supporte passe par `Kill_LIFE` pour le lancement et par `mascarade` pour l'implementation -- `notion` et `github-dispatch` sont supportes comme serveurs MCP locaux, mais leur logique applicative reste fournie par `mascarade` -- les micro-serveurs `kicad_kic_ai` sont suivis comme surfaces auxiliaires, pas comme point d'entree operateur `Kill_LIFE` -- le probe synthetique MCP expose cote ops appartient a `mascarade/api/src/routes/ops.ts`; il n'est disponible que si la stack compagnon tourne +- `stdio` reste le seul transport supporte par defaut pour les serveurs locaux +- `knowledge-base` et `github-dispatch` sont supportes comme serveurs MCP locaux, avec validation live fermee sur la machine de reference +- `freecad` et `openscad` sont des serveurs MCP supportes et sondes par le cockpit ops +- le probe synthetique MCP expose cote ops appartient a `mascarade/api/src/routes/ops.ts` ## Dettes encore ouvertes -- le chemin host-native avec `pcbnew` n'est pas encore revalide sur une machine qui l'expose reellement -- `nexar_api` doit encore etre valide en mode live avec credentials -- le statut des micro-serveurs auxiliaires doit rester distinct de la surface operateur `kicad` +- fermer `K-012` sur une machine avec `pcbnew` host-native +- fermer `K-014` en mode `nexar_api` live avec credentials diff --git a/docs/RUNTIME_HOME.md b/docs/RUNTIME_HOME.md index 16f3934..68091dd 100644 --- a/docs/RUNTIME_HOME.md +++ b/docs/RUNTIME_HOME.md @@ -82,7 +82,7 @@ docker run --rm \ - [tools/hw/kicad_cli.sh](../tools/hw/kicad_cli.sh) - [tools/hw/run_kicad_mcp.sh](../tools/hw/run_kicad_mcp.sh) -- [tools/run_notion_mcp.sh](../tools/run_notion_mcp.sh) +- [tools/run_knowledge_base_mcp.sh](../tools/run_knowledge_base_mcp.sh) - [tools/run_github_dispatch_mcp.sh](../tools/run_github_dispatch_mcp.sh) - [tools/run_nexar_mcp.sh](../tools/run_nexar_mcp.sh) diff --git a/docs/plans/15_plan_mcp_runtime_alignment.md b/docs/plans/15_plan_mcp_runtime_alignment.md index 57916f7..2d2a3dc 100644 --- a/docs/plans/15_plan_mcp_runtime_alignment.md +++ b/docs/plans/15_plan_mcp_runtime_alignment.md @@ -17,7 +17,7 @@ Faire de `Kill_LIFE` le repo de consommation et de gouvernance MCP, sans mainten ## Etat actuel -- `mcp.json` pointe vers des launchers MCP reels pour `kicad`, `validate-specs`, `notion` et `github-dispatch` +- `mcp.json` pointe vers des launchers MCP reels pour `kicad`, `validate-specs`, `knowledge-base` et `github-dispatch` - `tools/hw/run_kicad_mcp.sh` est le point d'entree canonique pour le runtime KiCad - `tools/hw/cad_stack.sh mcp` est deja aligne sur ce launcher - `python3 tools/hw/mcp_smoke.py --timeout 30` passe sur la machine auditee via fallback conteneur diff --git a/docs/plans/README.md b/docs/plans/README.md index f2545db..37fbbcf 100644 --- a/docs/plans/README.md +++ b/docs/plans/README.md @@ -23,6 +23,8 @@ Ces plans sont des **runbooks** : tu peux les suivre tels quels, ou les transfor 13. [Plan de troubleshooting](13_plan_troubleshooting.md) 14. [Plan de release & versioning](14_plan_release_versioning.md) 15. [Plan d'alignement MCP local](15_plan_mcp_runtime_alignment.md) +16. [Plan de stack CAD modelling locale](16_plan_cad_modeling_stack.md) +17. [Plan cible MCP + agentics + A2A](17_plan_target_architecture_mcp_agentics_2028.md) --- diff --git a/mcp.json b/mcp.json index ea053fc..eaa654d 100644 --- a/mcp.json +++ b/mcp.json @@ -12,10 +12,10 @@ "args": ["tools/validate_specs.py", "--mcp"], "tools": ["*"] }, - "notion": { + "knowledge-base": { "type": "local", "command": "bash", - "args": ["tools/run_notion_mcp.sh"], + "args": ["tools/run_knowledge_base_mcp.sh"], "tools": ["*"] }, "github-dispatch": { @@ -24,6 +24,18 @@ "args": ["tools/run_github_dispatch_mcp.sh"], "tools": ["*"] }, + "freecad": { + "type": "local", + "command": "bash", + "args": ["tools/run_freecad_mcp.sh"], + "tools": ["*"] + }, + "openscad": { + "type": "local", + "command": "bash", + "args": ["tools/run_openscad_mcp.sh"], + "tools": ["*"] + }, "huggingface": { "type": "url", "url": "https://huggingface.co/mcp", diff --git a/specs/README.md b/specs/README.md index 2372839..4940c0e 100644 --- a/specs/README.md +++ b/specs/README.md @@ -13,9 +13,11 @@ Le fichier `constraints.yaml` est la **source de verite** des contraintes non-fo Specs complementaires: - `github_mcp_conversion_spec.md`: prep de conversion de `workflow_dispatch` vers une surface MCP future. +- `cad_modeling_tasks.md`: backlog canonique `FreeCAD/OpenSCAD` pour la stack CAD locale hors MCP. - `kicad_mcp_scope_spec.md`: perimetre fonctionnel, hors scope et criteres d'acceptation du MCP KiCad supporte. +- `mcp_agentics_target_backlog.md`: backlog cible 2026 -> 2028 pour `MCP`, `agentics`, `A2A`, avec ownership par repo. - `mcp_tasks.md`: backlog canonique des actions MCP locales, partage entre runtime, doc et gouvernance. -- `notion_mcp_conversion_spec.md`: prep de conversion du bridge Notion actuel vers une surface MCP future. +- `knowledge_base_mcp_spec.md`: spec canonique du bridge et du MCP knowledge base (`memos` / `docmost`). - `zeroclaw_dual_hw_orchestration_spec.md`: architecture d'orchestration ZeroClaw multi-repo + double materiel. - `zeroclaw_dual_hw_todo.md`: backlog operationnel court terme pour autonomie controlee. diff --git a/specs/knowledge_base_mcp_spec.md b/specs/knowledge_base_mcp_spec.md new file mode 100644 index 0000000..6fdd79c --- /dev/null +++ b/specs/knowledge_base_mcp_spec.md @@ -0,0 +1,61 @@ +# Spec MCP Knowledge Base + +Last updated: 2026-03-08 + +## Objectif + +Documenter l'implementation MCP `knowledge-base` a partir du bridge knowledge base actuel, avec uniquement `memos` et `docmost` dans la pile supportee. + +## Etat actuel + +- backend reel dans `mascarade/core/mascarade/integrations/knowledge_base.py` +- bridge HTTP canonique dans `mascarade/api/src/routes/knowledgeBase.ts` +- UI consommatrice dans `mascarade/web/src/pages/KnowledgeBrowser.tsx` et `crazy_life/src/pages/KnowledgeBrowser.tsx` +- providers supportes: + - `memos` + - `docmost` + +## Surface MCP implemente + +Serveur MCP `knowledge-base` local avec les outils: + +- `search_pages` +- `read_page` +- `append_to_page` +- `create_page` + +## Mapping depuis l'existant + +- `GET /api/knowledge-base/search` -> `search_pages` +- `GET /api/knowledge-base/pages/:pageId` -> `read_page` +- `POST /api/knowledge-base/pages/:pageId/append` -> `append_to_page` +- `POST /api/knowledge-base/pages` -> `create_page` + +## Contraintes + +- garder `stdio` comme transport par defaut +- ne pas exposer un MCP reseau en v1 +- garder le bridge HTTP canonique tant que les clients UI le consomment +- launcher operateur: `tools/run_knowledge_base_mcp.sh` +- erreurs explicites si le provider actif n'est pas configure + +## Providers supportes + +- `memos` + - prerequis: `MEMOS_BASE_URL`, `MEMOS_ACCESS_TOKEN` +- `docmost` + - prerequis: `DOCMOST_BASE_URL`, `DOCMOST_EMAIL`, `DOCMOST_PASSWORD` + +## Hors scope v1 + +- ajout d'un troisieme provider non qualifie +- suppression du bridge HTTP canonique +- edition riche avancee ou blocs proprietaires +- sync bidirectionnelle UI <-> MCP hors actions explicites + +## Validation minimale + +- handshake `initialize -> tools/list` vert +- `search_pages` et `read_page` smokees sur le provider actif +- erreur structuree et non ambigue si le provider actif n'est pas configure +- documentation operateur et matrice de support mises a jour diff --git a/specs/mcp_tasks.md b/specs/mcp_tasks.md index 5a313b9..0d23f9b 100644 --- a/specs/mcp_tasks.md +++ b/specs/mcp_tasks.md @@ -1,6 +1,6 @@ # Tasks MCP local -Last updated: 2026-03-07 +Last updated: 2026-03-08 Backlog MCP canonique pour `Kill_LIFE`. @@ -17,6 +17,14 @@ Format: ## État courant +- Etat agrege courant: + - `kicad`, `validate-specs` et `huggingface` sont `ready` + - `knowledge-base` est `ready` sur le provider actif `memos`, avec smoke live valide + - `github-dispatch` est `ready`, avec smoke live valide via token GitHub persiste + - le backlog MCP restant est limite a deux blocages specialises constates sur cette machine: + - `K-012`: `pcbnew` absent sur l'hote, donc validation host-native impossible ici + - `K-014`: `NEXAR_TOKEN` absent, donc `nexar_api` reste en mode demo + - [x] K-001 — Rendre `validate-specs` réel - AC: `mcp.json` ne référence plus de chemin absent. @@ -55,6 +63,9 @@ Format: - [ ] K-012 — Rejouer la validation host-native sur une machine avec `pcbnew` - AC: le smoke passe aussi sur le chemin hote, pas seulement via le fallback conteneur. - Helper pret: `python3 tools/hw/kicad_host_mcp_smoke.py --json --quick` degrade proprement si `pcbnew` est absent. + - Derniere verification: `2026-03-08` sur cette machine -> `blocked by host environment` + - Evidence: `python3 tools/hw/kicad_host_mcp_smoke.py --json --quick` + - Resultat: `{"status":"degraded","host_pcbnew_import":"missing","error":"pcbnew not importable on host runtime"}` - [x] K-013 — Décider du statut final des micro-serveurs `kicad_kic_ai` - AC: `component_database`, `kicad_tools` et `nexar_api` sont explicitement promus en surfaces auxiliaires supportées. @@ -62,18 +73,22 @@ Format: - [ ] K-014 — Valider le mode live de `nexar_api` - AC: un run avec `NEXAR_TOKEN` confirme le comportement reel et le distingue du mode demo. - Helper pret: `python3 tools/nexar_mcp_smoke.py --json --live` degrade ou echoue proprement tant que le token ou le mode live manquent. + - Derniere verification: `2026-03-08` sur cette machine -> `blocked by secret/env` + - Evidence: `python3 tools/nexar_mcp_smoke.py --json --live` + - Resultat: `{"status":"degraded","token_configured":false,"demo_mode":true,"error":"NEXAR token missing or server running in demo mode"}` -- [x] K-015 — Implémenter le MCP `Notion` - - AC: `tools/run_notion_mcp.sh` expose `search_pages`, `read_page`, `append_to_page`, `create_page` sans retirer le bridge HTTP en V1. +- [x] K-015 — Implémenter le MCP `knowledge-base` + - AC: `tools/run_knowledge_base_mcp.sh` expose `search_pages`, `read_page`, `append_to_page`, `create_page` sans retirer le bridge HTTP en V1. + - Resultat: le serveur `knowledge-base` est maintenant un MCP de compat vers `KNOWLEDGE_BASE_PROVIDER=memos|docmost`. - [x] K-016 — Implémenter le MCP `GitHub dispatch` - AC: `tools/run_github_dispatch_mcp.sh` expose `list_allowlisted_workflows`, `dispatch_workflow` et `get_dispatch_status` sans retirer la voie API actuelle en V1. - [x] K-017 — Ajouter des smokes MCP dedies hors KiCad - - AC: `validate-specs`, `notion` et `github-dispatch` ont chacun un smoke versionne avec sortie JSON. + - AC: `validate-specs`, `knowledge-base` et `github-dispatch` ont chacun un smoke versionne avec sortie JSON. - [x] K-018 — Etendre l'observabilite MCP a plusieurs serveurs - - AC: `/api/ops/summary` expose un etat agrege et le detail par serveur pour `kicad`, `validate-specs`, `notion` et `github-dispatch`. + - AC: `/api/ops/summary` expose un etat agrege et le detail par serveur pour `kicad`, `validate-specs`, `knowledge-base`, `github-dispatch`, `freecad` et `openscad`. - [x] K-019 — Ajouter un helper de readiness host-native KiCad - AC: `python3 tools/hw/kicad_host_mcp_smoke.py --json --quick` retourne `ready` ou `degraded` sans ambiguite. @@ -83,3 +98,15 @@ Format: - [x] K-021 — Ajouter un rapport MCP local synthetique - AC: `python3 tools/mcp_runtime_status.py --json` agrege les smokes supportes et rend visibles les blocages K-012/K-014. + +- [x] K-022 — Valider la knowledge base active en live via le MCP `knowledge-base` + - AC: un run avec le provider actif et sa cible de smoke confirme `search_pages` et `read_page`. + - Resultat: validation live fermee sur le provider actif `memos` auto-heberge, avec `python3 tools/knowledge_base_mcp_smoke.py --json` en `ready`. + - Variantes supportees: + - `memos`: `MEMOS_BASE_URL` + `MEMOS_ACCESS_TOKEN` + `KNOWLEDGE_BASE_SMOKE_PAGE_ID` optionnel + - `docmost`: `DOCMOST_BASE_URL` + `DOCMOST_EMAIL` + `DOCMOST_PASSWORD` + `KNOWLEDGE_BASE_SMOKE_PAGE_ID` + +- [x] K-023 — Valider `GitHub dispatch MCP` en live + - AC: un run avec `KILL_LIFE_GITHUB_TOKEN` ou un `GitHub App` valide confirme `list_allowlisted_workflows` et `dispatch_workflow` sur une cible autorisee. + - Resultat: validation live fermee via token GitHub persiste dans `runtime-secrets`; le smoke versionne confirme `list_allowlisted_workflows`, `dispatch_workflow` et `get_dispatch_status`. + - Evidence: `python3 tools/github_dispatch_mcp_smoke.py --json --live` diff --git a/specs/notion_mcp_conversion_spec.md b/specs/notion_mcp_conversion_spec.md deleted file mode 100644 index 3fde2e8..0000000 --- a/specs/notion_mcp_conversion_spec.md +++ /dev/null @@ -1,51 +0,0 @@ -# Spec conversion MCP Notion - -Last updated: 2026-03-07 - -## Objectif - -Documenter l'implémentation MCP `notion` à partir de l'intégration `Notion` existante, sans remplacer la voie HTTP existante en V1. - -## État actuel - -- backend réel dans `mascarade/core/mascarade/integrations/notion.py` -- bridge HTTP existant dans `mascarade/api/src/routes/notion.ts` -- UI consommatrice existante dans `crazy_life/src/pages/NotionBrowser.tsx` -- dépendance secrète: `NOTION_API_KEY` - -## Surface MCP implémentée - -Serveur MCP `notion` local avec les outils: - -- `search_pages` -- `read_page` -- `append_to_page` -- `create_page` - -## Mapping depuis l'existant - -- `GET /api/notion/search` -> `search_pages` -- `GET /api/notion/pages/:pageId` -> `read_page` -- `POST /api/notion/pages/:pageId/append` -> `append_to_page` -- `POST /api/notion/pages` -> `create_page` - -## Contraintes - -- garder `stdio` comme transport par défaut -- ne pas exposer un MCP réseau en V1 -- erreurs explicites si `NOTION_API_KEY` absent -- conserver le bridge HTTP existant tant que la migration client n'est pas faite -- launcher opérateur: `tools/run_notion_mcp.sh` - -## Hors scope V1 - -- suppression du bridge HTTP existant -- édition riche avancée ou gestion fine des blocs Notion -- sync bidirectionnelle UI <-> MCP - -## Validation minimale - -- handshake `initialize -> tools/list` vert -- `search_pages` et `read_page` smokés -- erreur structurée et non ambiguë si `NOTION_API_KEY` absent -- documentation opérateur et matrice de support mises à jour diff --git a/test/test_notion_mcp.py b/test/test_knowledge_base_mcp.py similarity index 90% rename from test/test_notion_mcp.py rename to test/test_knowledge_base_mcp.py index c9c1d36..182108c 100644 --- a/test/test_notion_mcp.py +++ b/test/test_knowledge_base_mcp.py @@ -9,7 +9,7 @@ from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] -SCRIPT = REPO_ROOT / "tools" / "run_notion_mcp.sh" +SCRIPT = REPO_ROOT / "tools" / "run_knowledge_base_mcp.sh" def write_message(proc: subprocess.Popen[str], payload: dict) -> None: @@ -34,10 +34,11 @@ def read_message(proc: subprocess.Popen[str]) -> dict: return json.loads(body) -class NotionMcpTests(unittest.TestCase): +class KnowledgeBaseMcpTests(unittest.TestCase): def test_server_supports_initialize_and_tools_list(self): env = os.environ.copy() - env.pop("NOTION_API_KEY", None) + env["KNOWLEDGE_BASE_PROVIDER"] = "memos" + env.pop("MEMOS_ACCESS_TOKEN", None) proc = subprocess.Popen( ["bash", str(SCRIPT)], cwd=REPO_ROOT, @@ -59,7 +60,9 @@ class NotionMcpTests(unittest.TestCase): }, ) initialize = read_message(proc) - self.assertEqual(initialize["result"]["serverInfo"]["name"], "notion") + self.assertEqual( + initialize["result"]["serverInfo"]["name"], "knowledge-base" + ) self.assertEqual(initialize["result"]["protocolVersion"], "2025-03-26") write_message( @@ -89,7 +92,8 @@ class NotionMcpTests(unittest.TestCase): def test_search_pages_returns_structured_missing_secret_error(self): env = os.environ.copy() - env.pop("NOTION_API_KEY", None) + env["KNOWLEDGE_BASE_PROVIDER"] = "memos" + env.pop("MEMOS_ACCESS_TOKEN", None) proc = subprocess.Popen( ["bash", str(SCRIPT)], cwd=REPO_ROOT, diff --git a/tools/github_dispatch_mcp_smoke.py b/tools/github_dispatch_mcp_smoke.py index 9f9e717..60a68f8 100755 --- a/tools/github_dispatch_mcp_smoke.py +++ b/tools/github_dispatch_mcp_smoke.py @@ -14,6 +14,7 @@ from mcp_smoke_common import ( emit_payload, initialize, list_tools, + load_runtime_env, spawn_server, terminate_server, ) @@ -54,6 +55,7 @@ def parse_args() -> argparse.Namespace: def main() -> int: + load_runtime_env() args = parse_args() token_configured = github_dispatch_auth_configured() proc = spawn_server(["bash", str(SERVER)], ROOT) diff --git a/tools/notion_mcp.py b/tools/knowledge_base_mcp.py similarity index 72% rename from tools/notion_mcp.py rename to tools/knowledge_base_mcp.py index 0ff8d90..076f38c 100755 --- a/tools/notion_mcp.py +++ b/tools/knowledge_base_mcp.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Local MCP server for the existing Mascarade Notion integration.""" +"""Local MCP server for the configured Mascarade knowledge-base integration.""" from __future__ import annotations @@ -26,16 +26,19 @@ MASCARADE_CORE_DIR = MASCARADE_DIR / "core" if str(MASCARADE_CORE_DIR) not in sys.path: sys.path.insert(0, str(MASCARADE_CORE_DIR)) -from mascarade.integrations.notion import ( # noqa: E402 - NotionClient, - notion_auth_configured, +from mascarade.integrations.knowledge_base import ( # noqa: E402 + KnowledgeBaseClient, + knowledge_base_auth_configured, + knowledge_base_provider_label, + knowledge_base_status_detail, + normalized_knowledge_base_provider, ) TOOLS = [ { "name": "search_pages", - "description": "Search pages through the existing Mascarade Notion integration.", + "description": "Search pages through the configured Mascarade knowledge-base integration.", "inputSchema": { "type": "object", "properties": { @@ -51,22 +54,22 @@ TOOLS = [ }, { "name": "read_page", - "description": "Read a Notion page and return its plain-text content.", + "description": "Read a knowledge-base page and return its plain-text content.", "inputSchema": { "type": "object", "properties": { - "page_id": {"type": "string", "description": "Notion page identifier"} + "page_id": {"type": "string", "description": "Knowledge-base page identifier"} }, "required": ["page_id"], }, }, { "name": "append_to_page", - "description": "Append a paragraph block to an existing Notion page.", + "description": "Append content to an existing knowledge-base page.", "inputSchema": { "type": "object", "properties": { - "page_id": {"type": "string", "description": "Notion page identifier"}, + "page_id": {"type": "string", "description": "Knowledge-base page identifier"}, "content": {"type": "string", "description": "Paragraph content to append"}, }, "required": ["page_id", "content"], @@ -74,7 +77,7 @@ TOOLS = [ }, { "name": "create_page", - "description": "Create a page under a parent page using the existing Notion integration.", + "description": "Create a page under a parent page using the configured knowledge-base integration.", "inputSchema": { "type": "object", "properties": { @@ -93,26 +96,26 @@ TOOLS = [ def _missing_secret_payload() -> dict[str, Any]: + label = knowledge_base_provider_label() return { "ok": False, + "provider": normalized_knowledge_base_provider(), "error": { "code": "missing_secret", - "message": ( - "Notion non configure " - "(NOTION_API_KEY ou credentials OAuth Notion manquants)" - ), + "message": knowledge_base_status_detail(), }, + "provider_label": label, } async def _with_client(callback): - if not notion_auth_configured(): + if not knowledge_base_auth_configured(): return error_tool_result( - "Notion non configure (NOTION_API_KEY ou credentials OAuth Notion manquants)", + knowledge_base_status_detail(), _missing_secret_payload(), ) - client = NotionClient() + client = KnowledgeBaseClient() try: return await callback(client) finally: @@ -129,11 +132,17 @@ async def tool_search_pages(arguments: dict[str, Any]) -> dict[str, Any]: {"ok": False, "error": {"code": "invalid_arguments", "message": "query is required"}}, ) - async def _run(client: NotionClient) -> dict[str, Any]: - results = await client.search(query) - payload = {"ok": True, "query": query, "results": results[:limit]} + async def _run(client: KnowledgeBaseClient) -> dict[str, Any]: + results = await client.search(query, limit=limit) + payload = { + "ok": True, + "provider": client.provider, + "provider_label": client.label, + "query": query, + "results": results[:limit], + } return ok_tool_result( - f"Found {len(payload['results'])} Notion page(s) for '{query}'", + f"Found {len(payload['results'])} {client.label} page(s) for '{query}'", payload, ) @@ -151,11 +160,17 @@ async def tool_read_page(arguments: dict[str, Any]) -> dict[str, Any]: }, ) - async def _run(client: NotionClient) -> dict[str, Any]: + async def _run(client: KnowledgeBaseClient) -> dict[str, Any]: content = await client.read_page(page_id) - payload = {"ok": True, "page_id": page_id, "content": content} + payload = { + "ok": True, + "provider": client.provider, + "provider_label": client.label, + "page_id": page_id, + "content": content, + } return ok_tool_result( - f"Read Notion page {page_id}", + f"Read {client.label} page {page_id}", payload, ) @@ -177,11 +192,17 @@ async def tool_append_to_page(arguments: dict[str, Any]) -> dict[str, Any]: }, ) - async def _run(client: NotionClient) -> dict[str, Any]: + async def _run(client: KnowledgeBaseClient) -> dict[str, Any]: await client.append_to_page(page_id, content) - payload = {"ok": True, "page_id": page_id, "content_length": len(content)} + payload = { + "ok": True, + "provider": client.provider, + "provider_label": client.label, + "page_id": page_id, + "content_length": len(content), + } return ok_tool_result( - f"Appended content to Notion page {page_id}", + f"Appended content to {client.label} page {page_id}", payload, ) @@ -204,11 +225,18 @@ async def tool_create_page(arguments: dict[str, Any]) -> dict[str, Any]: }, ) - async def _run(client: NotionClient) -> dict[str, Any]: + async def _run(client: KnowledgeBaseClient) -> dict[str, Any]: page_id = await client.create_page(parent_id, title, content) - payload = {"ok": True, "page_id": page_id, "parent_id": parent_id, "title": title} + payload = { + "ok": True, + "provider": client.provider, + "provider_label": client.label, + "page_id": page_id, + "parent_id": parent_id, + "title": title, + } return ok_tool_result( - f"Created Notion page '{title}'", + f"Created {client.label} page '{title}'", payload, ) @@ -232,7 +260,7 @@ def serve_mcp() -> int: { "protocolVersion": PROTOCOL_VERSION, "capabilities": {"tools": {"listChanged": False}}, - "serverInfo": {"name": "notion", "version": "1.0.0"}, + "serverInfo": {"name": "knowledge-base", "version": "1.0.0"}, }, ) ) diff --git a/tools/notion_mcp_smoke.py b/tools/knowledge_base_mcp_smoke.py similarity index 71% rename from tools/notion_mcp_smoke.py rename to tools/knowledge_base_mcp_smoke.py index 135a4de..834c695 100755 --- a/tools/notion_mcp_smoke.py +++ b/tools/knowledge_base_mcp_smoke.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Smoke checks for the local Notion MCP server.""" +"""Smoke checks for the local knowledge-base MCP server.""" from __future__ import annotations @@ -14,27 +14,30 @@ from mcp_smoke_common import ( emit_payload, initialize, list_tools, + load_runtime_env, spawn_server, terminate_server, ) ROOT = Path(__file__).resolve().parents[1] -SERVER = ROOT / "tools" / "run_notion_mcp.sh" -PAGE_ID_ENV = "NOTION_MCP_SMOKE_PAGE_ID" +SERVER = ROOT / "tools" / "run_knowledge_base_mcp.sh" +PAGE_ID_ENV = "KNOWLEDGE_BASE_SMOKE_PAGE_ID" -def notion_auth_configured() -> bool: - auth_mode = os.getenv("NOTION_AUTH_MODE", "api_key").strip().lower() - if auth_mode == "oauth_oidc": +def knowledge_base_auth_configured() -> bool: + provider = os.getenv("KNOWLEDGE_BASE_PROVIDER", "memos").strip().lower() or "memos" + if provider == "memos": return bool( - os.getenv("NOTION_OAUTH_CLIENT_ID", "").strip() - and os.getenv("NOTION_OAUTH_CLIENT_SECRET", "").strip() - and ( - os.getenv("NOTION_OAUTH_ACCESS_TOKEN", "").strip() - or os.getenv("NOTION_OAUTH_REFRESH_TOKEN", "").strip() - ) + os.getenv("MEMOS_BASE_URL", "").strip() + and os.getenv("MEMOS_ACCESS_TOKEN", "").strip() ) - return bool(os.getenv("NOTION_API_KEY")) + if provider == "docmost": + return bool( + os.getenv("DOCMOST_BASE_URL", "").strip() + and os.getenv("DOCMOST_EMAIL", "").strip() + and os.getenv("DOCMOST_PASSWORD", "").strip() + ) + return False def parse_args() -> argparse.Namespace: @@ -42,18 +45,24 @@ def parse_args() -> argparse.Namespace: parser.add_argument("--timeout", type=float, default=15.0) parser.add_argument("--json", action="store_true") parser.add_argument("--quick", action="store_true") - parser.add_argument("--page-id", default=os.getenv(PAGE_ID_ENV, "")) + parser.add_argument( + "--page-id", + default=os.getenv(PAGE_ID_ENV, "").strip(), + ) return parser.parse_args() def main() -> int: + load_runtime_env() args = parse_args() - secret_configured = notion_auth_configured() + secret_configured = knowledge_base_auth_configured() proc = spawn_server(["bash", str(SERVER)], ROOT) + provider = os.getenv("KNOWLEDGE_BASE_PROVIDER", "memos").strip().lower() or "memos" payload = { "status": "failed", "protocol_version": None, - "server_name": "notion", + "server_name": "knowledge-base", + "provider": provider, "tool_count": 0, "checks": [], "secret_configured": secret_configured, @@ -62,15 +71,19 @@ def main() -> int: } try: - init = initialize(proc, args.timeout, "kill-life-notion-mcp-smoke") + init = initialize(proc, args.timeout, "kill-life-knowledge-base-mcp-smoke") tools = list_tools(proc, args.timeout) tool_names = {tool.get("name") for tool in tools} expected = {"search_pages", "read_page", "append_to_page", "create_page"} if expected - tool_names: - raise SmokeError(f"notion tools missing: {sorted(expected - tool_names)}") + raise SmokeError( + f"knowledge-base tools missing: {sorted(expected - tool_names)}" + ) payload["protocol_version"] = init.get("protocolVersion", PROTOCOL_VERSION) - payload["server_name"] = (init.get("serverInfo") or {}).get("name", "notion") + payload["server_name"] = ( + init.get("serverInfo") or {} + ).get("name", "knowledge-base") payload["tool_count"] = len(tools) payload["checks"] = ["initialize", "tools/list"] @@ -81,7 +94,7 @@ def main() -> int: else: payload["status"] = "degraded" payload["live_validation"] = "missing_secret" - payload["error"] = "NOTION auth missing" + payload["error"] = f"{provider} auth missing" return emit_payload(payload, json_output=args.json) search_result = call_tool( diff --git a/tools/lib/runtime_home.sh b/tools/lib/runtime_home.sh index 26fe765..0080656 100644 --- a/tools/lib/runtime_home.sh +++ b/tools/lib/runtime_home.sh @@ -2,12 +2,35 @@ # shellcheck shell=bash +kill_life_runtime_home_is_writable() { + local target_dir="$1" + local probe_dir="$target_dir" + + while [ ! -e "$probe_dir" ]; do + probe_dir="$(dirname "$probe_dir")" + if [ "$probe_dir" = "/" ]; then + break + fi + done + + [ -w "$probe_dir" ] +} + kill_life_runtime_home_init() { local root_dir="$1" local runtime_name="$2" local base_dir="${3:-$root_dir/.runtime-home}" + local fallback_base_dir="${KILL_LIFE_RUNTIME_BASE_DIR:-$root_dir/.cad-home/runtime-home}" + local selected_base_dir="$base_dir" - RUNTIME_HOME="${KILL_LIFE_RUNTIME_HOME:-$base_dir/$runtime_name}" + if [ -z "${KILL_LIFE_RUNTIME_HOME:-}" ] && ! kill_life_runtime_home_is_writable "$base_dir"; then + selected_base_dir="$fallback_base_dir" + if ! kill_life_runtime_home_is_writable "$selected_base_dir"; then + selected_base_dir="${XDG_RUNTIME_DIR:-/tmp}/kill-life-runtime-home" + fi + fi + + RUNTIME_HOME="${KILL_LIFE_RUNTIME_HOME:-$selected_base_dir/$runtime_name}" XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$RUNTIME_HOME/.config}" XDG_CACHE_HOME="${XDG_CACHE_HOME:-$RUNTIME_HOME/.cache}" HOME="$RUNTIME_HOME" diff --git a/tools/mcp_runtime_status.py b/tools/mcp_runtime_status.py index 03a7697..e2b13d1 100644 --- a/tools/mcp_runtime_status.py +++ b/tools/mcp_runtime_status.py @@ -10,6 +10,11 @@ import sys from pathlib import Path from typing import Any +try: + from .mcp_smoke_common import load_runtime_env +except ImportError: # pragma: no cover - script entrypoint fallback + from mcp_smoke_common import load_runtime_env + ROOT = Path(__file__).resolve().parents[1] CHECKS: tuple[dict[str, Any], ...] = ( @@ -31,8 +36,8 @@ CHECKS: tuple[dict[str, Any], ...] = ( "blocked_when": "host_pcbnew_import != ok", }, { - "name": "notion", - "cmd": ["python3", "tools/notion_mcp_smoke.py", "--json", "--quick"], + "name": "knowledge-base", + "cmd": ["python3", "tools/knowledge_base_mcp_smoke.py", "--json", "--quick"], "accept_degraded": True, }, { @@ -40,6 +45,18 @@ CHECKS: tuple[dict[str, Any], ...] = ( "cmd": ["python3", "tools/github_dispatch_mcp_smoke.py", "--json", "--quick"], "accept_degraded": True, }, + { + "name": "freecad", + "cmd": ["python3", "tools/freecad_mcp_smoke.py", "--json", "--quick"], + "accept_degraded": False, + "task": "F-101", + }, + { + "name": "openscad", + "cmd": ["python3", "tools/openscad_mcp_smoke.py", "--json", "--quick"], + "accept_degraded": False, + "task": "O-101", + }, { "name": "nexar-api", "cmd": ["python3", "tools/nexar_mcp_smoke.py", "--json"], @@ -144,6 +161,7 @@ def emit(payload: dict[str, Any], *, json_output: bool) -> int: def main() -> int: args = parse_args() + load_runtime_env() results = [run_check(spec) for spec in CHECKS] payload = { "status": classify_overall(results, strict=args.strict), diff --git a/tools/mcp_smoke_common.py b/tools/mcp_smoke_common.py index 2fcdbdb..2d27577 100644 --- a/tools/mcp_smoke_common.py +++ b/tools/mcp_smoke_common.py @@ -4,6 +4,7 @@ from __future__ import annotations import json import os +import re import signal import subprocess import sys @@ -13,12 +14,55 @@ from threading import Thread from typing import Any PROTOCOL_VERSION = "2025-03-26" +ENV_ASSIGN_RE = re.compile(r"^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$") +DEFAULT_MASCARADE_DIR = Path( + os.environ.get("MASCARADE_DIR", Path(__file__).resolve().parents[2] / "mascarade") +).resolve() class SmokeError(RuntimeError): """Raised when a smoke check fails.""" +def _decode_env_value(raw_value: str) -> str: + value = raw_value.strip() + if not value: + return "" + if len(value) >= 2 and value[0] == value[-1] == '"': + inner = value[1:-1] + return inner.replace("\\n", "\n").replace('\\"', '"').replace("\\\\", "\\") + if len(value) >= 2 and value[0] == value[-1] == "'": + return value[1:-1] + return value + + +def resolve_mascarade_env_file() -> Path: + return Path( + os.environ.get("MASCARADE_ENV_FILE", DEFAULT_MASCARADE_DIR / ".env") + ).resolve() + + +def load_runtime_env(*, override: bool = False) -> dict[str, str]: + env_file = resolve_mascarade_env_file() + loaded: dict[str, str] = {} + if not env_file.exists(): + return loaded + + for raw_line in env_file.read_text(encoding="utf-8").splitlines(): + stripped = raw_line.strip() + if not stripped or stripped.startswith("#"): + continue + match = ENV_ASSIGN_RE.match(stripped) + if not match: + continue + key = match.group(1) + value = _decode_env_value(match.group(2)) + if override or key not in os.environ or not os.environ.get(key, "").strip(): + os.environ[key] = value + loaded[key] = value + return loaded + + def spawn_server(command: list[str], cwd: Path) -> subprocess.Popen[str]: return subprocess.Popen( command, diff --git a/tools/review_local_change_bundle.sh b/tools/review_local_change_bundle.sh index ccd663c..0830ae0 100755 --- a/tools/review_local_change_bundle.sh +++ b/tools/review_local_change_bundle.sh @@ -10,8 +10,9 @@ usage() { Usage: bash tools/review_local_change_bundle.sh [mode] Bundles: - mcp-runtime Runtime-home, MCP launchers, MCP smokes, docs MCP associees - python-local Bootstrap/test Python repo-local et harness associes + mcp-runtime Runtime-home, knowledge-base, GitHub dispatch, docs MCP associees + cad-mcp Pile CAD/MCP (FreeCAD, OpenSCAD, compose CAD, specs et docs associees) + python-local Commande Python repo-locale stable encore dans le delta courant all Ensemble des lots suivis localement Modes: @@ -21,6 +22,7 @@ Modes: Examples: bash tools/review_local_change_bundle.sh mcp-runtime + bash tools/review_local_change_bundle.sh cad-mcp diff bash tools/review_local_change_bundle.sh python-local diff bash tools/review_local_change_bundle.sh all paths EOF @@ -30,34 +32,76 @@ bundle_paths() { case "$1" in mcp-runtime) cat <<'EOF' -.gitignore +README.md +ai-agentic-embedded-base/specs/README.md ai-agentic-embedded-base/specs/mcp_tasks.md -docs/QUICKSTART.md -docs/index.md +ai-agentic-embedded-base/specs/knowledge_base_mcp_spec.md +ai-agentic-embedded-base/specs/notion_mcp_conversion_spec.md +ai-agentic-embedded-base/specs/zeroclaw_dual_hw_todo.md +docs/LOCAL_CHANGE_BUNDLES_2026-03-08.md +docs/MCP_ECOSYSTEM_MATRIX.md +docs/MCP_SETUP.md +docs/MCP_SUPPORT_MATRIX.md docs/RUNTIME_HOME.md +docs/plans/15_plan_mcp_runtime_alignment.md +docs/plans/README.md +mcp.json +specs/README.md +specs/knowledge_base_mcp_spec.md +specs/mcp_tasks.md +specs/notion_mcp_conversion_spec.md +test/test_knowledge_base_mcp.py +test/test_notion_mcp.py tools/github_dispatch_mcp_smoke.py -tools/hw/kicad_cli.sh -tools/hw/run_kicad_mcp.sh tools/lib/runtime_home.sh +tools/knowledge_base_mcp.py +tools/knowledge_base_mcp_smoke.py +tools/mcp_runtime_status.py +tools/mcp_smoke_common.py tools/notion_mcp.py tools/notion_mcp_smoke.py +tools/review_local_change_bundle.sh tools/run_github_dispatch_mcp.sh -tools/run_nexar_mcp.sh +tools/run_knowledge_base_mcp.sh tools/run_notion_mcp.sh +EOF + ;; + cad-mcp) + cat <<'EOF' +Makefile +ai-agentic-embedded-base/specs/cad_modeling_tasks.md +ai-agentic-embedded-base/specs/mcp_agentics_target_backlog.md +deploy/cad/README.md +deploy/cad/Dockerfile.openscad-headless +deploy/cad/docker-compose.yml +docs/plans/16_plan_cad_modeling_stack.md +docs/plans/17_plan_target_architecture_mcp_agentics_2028.md +specs/cad_modeling_tasks.md +specs/mcp_agentics_target_backlog.md +test/test_freecad_mcp.py +test/test_openscad_mcp.py +tools/cad_runtime.py +tools/freecad_mcp.py +tools/freecad_mcp_smoke.py +tools/hw/cad_stack.sh +tools/hw/freecad_smoke.py +tools/hw/openscad_smoke.py +tools/mcp_telemetry.py +tools/openscad_mcp.py +tools/openscad_mcp_smoke.py +tools/run_freecad_mcp.sh +tools/run_openscad_mcp.sh EOF ;; python-local) cat <<'EOF' -README.md -test/test_openclaw_sanitizer.py -tools/bootstrap_python_env.sh -tools/hw/schops/tests/test_rules_engine.py tools/test_python.sh EOF ;; all) { bundle_paths mcp-runtime + bundle_paths cad-mcp bundle_paths python-local } | awk '!seen[$0]++' ;; diff --git a/tools/run_github_dispatch_mcp.sh b/tools/run_github_dispatch_mcp.sh index 33fff85..f33289c 100755 --- a/tools/run_github_dispatch_mcp.sh +++ b/tools/run_github_dispatch_mcp.sh @@ -3,10 +3,19 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" MASCARADE_DIR="${MASCARADE_DIR:-$ROOT_DIR/../mascarade}" +MASCARADE_ENV_FILE="${MASCARADE_ENV_FILE:-$MASCARADE_DIR/.env}" SERVER_SCRIPT="$ROOT_DIR/tools/github_dispatch_mcp.py" source "$ROOT_DIR/tools/lib/runtime_home.sh" kill_life_runtime_home_init "$ROOT_DIR" "github-dispatch-mcp" +load_mascarade_env() { + [[ -r "$MASCARADE_ENV_FILE" ]] || return 0 + set -a + # shellcheck disable=SC1090 + source "$MASCARADE_ENV_FILE" + set +a +} + detect_core_python() { local candidate="${MASCARADE_CORE_PYTHON:-}" if [[ -n "$candidate" && -x "$candidate" ]]; then @@ -31,11 +40,13 @@ detect_core_python() { } CORE_PYTHON="$(detect_core_python || true)" +load_mascarade_env if [[ "${1:-}" == "--doctor" ]]; then cat <