refactor: rename MCP tool from qwen3_task to task and update delegation flow

- Rename MCP tool `qwen3_task` → `task` in server.py and all references
- Enforce direct use of `mcp__qwen3__task` (no sub-agents) in docs
- Add bootstrap logic to install.sh/update_crazyclaude.sh for repo self-installation
- Introduce tools-local/ directory for local tool overrides
- Add test suite (test_qwen3.sh, test_qwen3.md) to validate Qwen3 file generation
- Update .gitignore: ignore test_qwen3_output/, add missing .claude/
- Add system prompt to server.py for fs_write enforcement
- Update agent docs (qwen3-worker, code-reviewer, delegation-rules, task-planner) to reflect new tool name and workflow
- Add system instruction: always use fs_write/fs_patch, never display content in response
This commit is contained in:
2026-03-27 17:31:40 +01:00
parent ee625dfe3d
commit 5a0874c3c0
13 changed files with 361 additions and 44 deletions

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env -S venv/bin/python3
"""
MCP server stdio exposant un outil `qwen3_task`.
MCP server stdio exposant un outil `task`.
Lance aichat en mode serveur au démarrage et gère la boucle tool_calls.
Les outils disponibles pour Qwen3 sont chargés depuis llm-functions/functions.json.
Appelé par Claude Code via : claude mcp add --transport stdio qwen3 -- .claude/venv/bin/python3 .claude/mcp/qwen3-mcp/server.py
@@ -28,20 +28,28 @@ _FUNCTIONS_JSON = _LLM_FUNCTIONS_DIR / "functions.json"
_BIN_DIR = _LLM_FUNCTIONS_DIR / "bin"
_qwen3_tools: list[dict] | None = None
def _load_qwen3_tools() -> list[dict]:
"""Charge les outils depuis llm-functions/functions.json et les wrappe au format OpenAI."""
"""Charge les outils depuis llm-functions/functions.json (mise en cache au premier appel)."""
global _qwen3_tools
if _qwen3_tools is not None:
return _qwen3_tools
if not _FUNCTIONS_JSON.exists():
print(f"WARN: {_FUNCTIONS_JSON} introuvable — aucun outil disponible",
file=sys.stderr, flush=True)
return []
_qwen3_tools = []
return _qwen3_tools
raw = json.loads(_FUNCTIONS_JSON.read_text())
return [{"type": "function", "function": tool} for tool in raw]
_qwen3_tools = [{"type": "function", "function": tool} for tool in raw]
return _qwen3_tools
# Outil MCP exposé à Claude Code
MCP_TOOLS = [
{
"name": "qwen3_task",
"name": "task",
"description": "Délègue une tâche de codage à Qwen3-Coder via aichat. Qwen3 dispose d'outils filesystem, shell et web (llm-functions).",
"inputSchema": {
"type": "object",
@@ -159,7 +167,18 @@ def call_qwen3(task: str, files: list[str]) -> str:
if context:
user_content += f"\n\nFICHIERS:\n{context}"
messages = [{"role": "user", "content": user_content}]
system = (
"Tu es un assistant de développement logiciel. "
"Pour toute tâche impliquant la création ou modification de fichiers, "
"utilise TOUJOURS les outils fs_write ou fs_patch — ne jamais afficher "
"le contenu dans ta réponse. "
"Utilise execute_command pour vérifier le résultat si nécessaire. "
"Réponds en français, de façon concise."
)
messages = [
{"role": "system", "content": system},
{"role": "user", "content": user_content},
]
payload: dict = {
"model": QWEN3_MODEL,
"messages": messages,
@@ -225,7 +244,7 @@ def handle(req: dict):
params = req.get("params", {})
name = params.get("name", "")
args = params.get("arguments", {})
if name == "qwen3_task":
if name == "task":
try:
text = call_qwen3(args["task"], args.get("files", []))
send({"jsonrpc": "2.0", "id": req_id, "result": {