diff --git a/.github/skills/mcp-protocol-debugging/SKILL.md b/.github/skills/mcp-protocol-debugging/SKILL.md new file mode 100644 index 0000000..d4278cc --- /dev/null +++ b/.github/skills/mcp-protocol-debugging/SKILL.md @@ -0,0 +1,64 @@ +--- +name: mcp-protocol-debugging +description: Guide for exercising and debugging this repo's MCP JSON-RPC server (mcp_server/main.py), including capability negotiation (initialize/tools/list), tool calls, and error handling. Use when asked to verify, debug, or extend MCP support in self-correcting-executor. +--- + +This repo ships a canonical JSON-RPC MCP server at `mcp_server/main.py` +(`MCPServer` class), independent from the FastAPI-based +`mcp_server/real_mcp_server.py`. Use this process to verify or debug MCP +behavior: + +1. **Confirm the baseline capability negotiation works** by driving the + server directly in Python (no transport needed for debugging): + + ```python + import asyncio + from mcp_server.main import MCPServer + + async def main(): + server = MCPServer() + # 1. initialize — must return serverInfo + capabilities.tools/resources + init = await server.handle_request({ + "jsonrpc": "2.0", "id": 1, "method": "initialize", + "params": {"clientInfo": {"name": "debug-client", "version": "1.0"}}, + }) + print(init) + + # 2. tools/list — must match the tools advertised in `initialize` + print(await server.handle_request( + {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}} + )) + + # 3. tools/call — must execute real logic (code_analyzer, protocol_validator, + # self_corrector), never mocked/simulated results + print(await server.handle_request({ + "jsonrpc": "2.0", "id": 3, "method": "tools/call", + "params": {"name": "code_analyzer", "arguments": {"code": "x = 1"}}, + })) + + asyncio.run(main()) + ``` + +2. **Run the automated baseline test** that pins this behavior down: + + ```bash + python -m pytest tests/test_mcp_baseline.py -v + ``` + +3. **When adding a new MCP tool or resource**, update all three of: + - `MCPServer._setup_tools` / `_setup_resources` (declares the capability) + - the corresponding `_execute_` method (real implementation, + never a stub/mock — this repo enforces "no mocks in production", see + `tests/test_mcp_compliance.py::test_no_placeholder_code_in_production`) + - `tests/test_mcp_baseline.py` and/or `tests/test_mcp_compliance.py` to + cover the new capability. + +4. **Unknown or malformed methods must degrade gracefully**: `handle_request` + should always return a well-formed JSON-RPC response with an + `{"error": {"code": ..., "message": ...}}` field rather than raising + an unhandled exception — verify this whenever you touch `_get_handler`. + +5. For the FastAPI-based server (`mcp_server/real_mcp_server.py`) or the + quantum-specific server (`quantum_mcp_server/quantum_mcp.py`), apply the + same principle: prove `initialize`/capability discovery works live before + relying on more advanced features (subscriptions, resource reads, etc.). diff --git a/.github/skills/quantum-connector-testing/SKILL.md b/.github/skills/quantum-connector-testing/SKILL.md new file mode 100644 index 0000000..0f45bc5 --- /dev/null +++ b/.github/skills/quantum-connector-testing/SKILL.md @@ -0,0 +1,53 @@ +--- +name: quantum-connector-testing +description: Guide for testing connectors/dwave_quantum_connector.py and other quantum MCP tools in self-correcting-executor without requiring a live D-Wave QPU token, while respecting this repo's "no simulation/mocks in production" convention. Use when asked to test, debug, or extend quantum computing integration. +--- + +This repo's D-Wave integration (`connectors/dwave_quantum_connector.py`, +`mcp_server/quantum_tools.py`, `quantum_mcp_server/quantum_mcp.py`) is +intentionally built against the **real** D-Wave Ocean SDK and Leap cloud +service — it must never silently fall back to a local simulator/annealer in +production code paths (see the removed `SimulatedAnnealingSampler` import +and `tests/test_mcp_compliance.py::test_quantum_requires_real_qpu`). + +When testing or extending this code: + +1. **Check for `DWAVE_AVAILABLE` / `DWAVE_API_TOKEN` before assuming a QPU is + reachable.** Tests and scripts should `pytest.skip(...)` (not fabricate + results) when the token isn't set, mirroring the existing pattern in + `tests/test_mcp_compliance.py::test_quantum_requires_real_qpu`: + + ```python + import os + import pytest + + if not os.getenv("DWAVE_API_TOKEN"): + pytest.skip("DWAVE_API_TOKEN not set - skipping quantum test") + ``` + +2. **Test the non-quantum-hardware parts directly and deterministically:** + - Input validation / `QuantumResult` dataclass construction + - Problem formulation (`BinaryQuadraticModel` / `ConstrainedQuadraticModel` + building) using `dimod`, which runs locally without QPU access + - Error handling paths that should raise/propagate a `RuntimeError` + (e.g. `"No D-Wave QPU available"`) instead of silently simulating + +3. **Never add a mock/simulated sampler as a fallback** in + `connectors/dwave_quantum_connector.py` or related production files — + this is explicitly checked by + `tests/test_mcp_compliance.py::test_no_placeholder_code_in_production` + and `test_data_processor_no_simulation`, which scan for + `mock`/`simulated`/`placeholder` strings in production directories + (`agents`, `connectors`, `mcp_server`, `protocols`). + +4. **Run the relevant test files** after any change: + + ```bash + python -m pytest tests/test_mcp_compliance.py -k quantum -v + python -m pytest test_real_dwave_quantum.py -v # requires DWAVE_API_TOKEN + ``` + +5. If you need to demonstrate quantum problem formulation without hardware + access, use `simple_quantum_example.py` as a reference for building a + `BinaryQuadraticModel` locally, then document clearly that solving it on + real hardware requires a valid `DWAVE_API_TOKEN`. diff --git a/docs/REPO_MAP.md b/docs/REPO_MAP.md new file mode 100644 index 0000000..fba2b3e --- /dev/null +++ b/docs/REPO_MAP.md @@ -0,0 +1,142 @@ +# Repo Map, Architecture & Agentic Workflow Checklist + +This document maps `self-correcting-executor` for AI coding agents (Copilot +cloud agent, Copilot CLI, etc.) so an agent run can quickly orient itself +before making changes: what the repo does, how its pieces relate, what +already exists for MCP/agents/tools, and what to check before commit/merge. + +## Repo tree (top level) + +``` +self-correcting-executor/ +├── agents/ # A2A agent framework + MCP-enabled agents +├── analyzers/ # Static analysis helpers (pattern_detector, ...) +├── auth/ # Basic auth helpers +├── config/ # MCPConfig, component type definitions +├── connectors/ # MCP base + concrete connectors (D-Wave, GitHub, LLM, xAI) +├── docs/ # Architecture, planning & task docs (this file lives here) +├── fabric/ # Integrated MCP "fabric" / state continuity core +├── frontend/ # Vite/React UI +├── llm/ # Continuous learning system +├── mcp_runtime_template_hg/ # SDK/API/CLI template for MCP runtimes +├── mcp_server/ # Canonical JSON-RPC MCP server (main.py) + quantum tools +├── middleware/ # Security middleware +├── orchestrator.py, orchestrator_mapreduce.py # Task orchestration entrypoints +├── protocols/ # Pluggable "protocol" tasks executed by the orchestrator +├── quantum_mcp_server/ # Quantum-specific MCP server variant +├── scripts/ # Compliance/setup/cleanup scripts +├── tests/ + test_*.py # Pytest suites (see pytest.ini: testpaths = tests) +├── ui/ # Additional UI assets/guide +└── utils/ # Shared logger/tracker/registry utilities +``` + +## Mermaid diagram: high-level architecture + +```mermaid +flowchart TD + subgraph Host["Host: AI Agent / Copilot"] + A[Agent orchestrator.py / orchestrator_mapreduce.py] + end + + subgraph MCPServers["MCP Servers (this repo)"] + M1[mcp_server/main.py
JSON-RPC MCP server] + M2[mcp_server/real_mcp_server.py] + M3[quantum_mcp_server/quantum_mcp.py] + end + + subgraph Tools["Tools exposed via MCP"] + T1[code_analyzer] + T2[protocol_validator] + T3[self_corrector] + end + + subgraph Connectors["Connectors"] + C1[dwave_quantum_connector.py] + C2[github_mcp_connector.py] + C3[llm_connector.py] + C4[xai_connector.py] + end + + subgraph Protocols["protocols/*.py tasks"] + P1[data_processor] + P2[system_monitor] + P3[file_validator] + P4[...] + end + + A -- "JSON-RPC: initialize / tools/list / tools/call" --> M1 + M1 --> T1 + M1 --> T2 + M1 --> T3 + A --> M2 + A --> M3 + M2 --> C1 + M2 --> C2 + M2 --> C3 + M2 --> C4 + A --> Protocols + Protocols --> P1 + Protocols --> P2 + Protocols --> P3 + Protocols --> P4 +``` + +## Checklist for an agent run in this repo + +Use this before proving/implementing anything live in this repo: + +- [x] **Agents** — `agents/a2a_framework.py`, `agents/a2a_mcp_integration.py` + implement agent-to-agent (A2A) communication over MCP. +- [x] **Tools** — MCP tools are declared in `mcp_server/main.py` + (`code_analyzer`, `protocol_validator`, `self_corrector`). +- [x] **MCP** — Baseline JSON-RPC server exists (`mcp_server/main.py`) and is + verified live in `tests/test_mcp_baseline.py` (`initialize`, + `tools/list`, `tools/call`, and error handling for unknown methods). +- [ ] **Pull / Push / Commit / Merge** — Use `engine-tools-report_progress` + (or normal `git`/PR flow for humans); never push directly from an + agent sandbox. See `CONTRIBUTING.md`. +- [ ] **Issues** — Track work items and link PRs with `Closes #`. +- [ ] **Code** — Run `python -m pytest tests/` (see `pytest.ini`) and the + lint config in `.flake8` before committing. +- [ ] **Deps** — `requirements.txt` (runtime), `requirements-ci.txt` / + `requirements-test.txt` (CI/tests), `frontend/package.json` (UI). + Dependabot is configured in `.github/dependabot.yml`. +- [ ] **Database** — `protocols/database_health_check.py`, + `utils/db_tracker.py` for DB-backed protocol tasks. +- [ ] **Actions** — `.github/workflows/python-ci.yml` and + `.github/workflows/frontend-ci.yml` run CI on push/PR. +- [ ] **Role assignment** — `middleware/security_middleware.py`, + `auth/basic_auth.py` for auth/authorization concerns. + +## MCP capability negotiation baseline + +Per the [MCP specification](https://modelcontextprotocol.io/specification/2026-07-28), +a host/server pair must be able to negotiate capabilities before any other +request. This repo's `mcp_server/main.py` implements the minimum required +surface: + +| Method | Purpose | +| --- | --- | +| `initialize` | Returns `serverInfo` + `capabilities.tools` + `capabilities.resources` | +| `tools/list` | Lists available tools independently of `initialize` | +| `tools/call` | Executes a tool and returns real (non-mocked) results | +| `resources/list` / `resources/read` | Lists/reads MCP resources | +| `notifications/list` / `notifications/subscribe` | Baseline notification support | + +This baseline is exercised live and asserted in +`tests/test_mcp_baseline.py`. Run it with: + +```bash +python -m pytest tests/test_mcp_baseline.py -v +``` + +## Related repo-specific skills + +Two agent skills tailored to this repo live under `.github/skills/`: + +- `mcp-protocol-debugging` — how to exercise and debug the `mcp_server/main.py` + JSON-RPC server directly (capability negotiation, tool calls, error cases). +- `quantum-connector-testing` — how to safely test `dwave_quantum_connector.py` + without requiring a live D-Wave QPU token, consistent with this repo's + "no mocks in production, real APIs only" convention (see + `tests/test_mcp_compliance.py`). diff --git a/tests/test_mcp_baseline.py b/tests/test_mcp_baseline.py new file mode 100644 index 0000000..8badc75 --- /dev/null +++ b/tests/test_mcp_baseline.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +"""Baseline MCP capability negotiation tests. + +These tests prove that the repository's JSON-RPC based MCP server +(`mcp_server/main.py`) implements the minimum handshake required for a host +to negotiate capabilities with the server: `initialize`, `tools/list`, and +`tools/call`. This is the smallest possible verification that MCP is +functional for this repo, per the Model Context Protocol specification +(https://modelcontextprotocol.io/specification). +""" + +import pytest + +from mcp_server.main import MCPServer, MCP_VERSION + + +@pytest.fixture +def mcp_server() -> MCPServer: + """Create a fresh MCP server instance for each test.""" + return MCPServer() + + +@pytest.mark.asyncio +async def test_initialize_declares_capabilities(mcp_server: MCPServer): + """The 'initialize' handshake must advertise server info and capabilities.""" + response = await mcp_server.handle_request( + { + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": {"clientInfo": {"name": "test-client", "version": "1.0"}}, + } + ) + + assert response["jsonrpc"] == "2.0" + assert response["id"] == 1 + assert "error" not in response + + result = response["result"] + assert result["serverInfo"]["mcpVersion"] == MCP_VERSION + assert "tools" in result["capabilities"] + assert "resources" in result["capabilities"] + tool_names = {tool["name"] for tool in result["capabilities"]["tools"]} + assert {"code_analyzer", "protocol_validator", "self_corrector"} <= tool_names + + +@pytest.mark.asyncio +async def test_tools_list_matches_advertised_capabilities(mcp_server: MCPServer): + """'tools/list' must be callable independently of 'initialize'.""" + response = await mcp_server.handle_request( + {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}} + ) + + assert "error" not in response + tool_names = {tool["name"] for tool in response["result"]["tools"]} + assert {"code_analyzer", "protocol_validator", "self_corrector"} <= tool_names + + +@pytest.mark.asyncio +async def test_tools_call_executes_a_real_tool(mcp_server: MCPServer): + """'tools/call' must invoke real tool logic, proving negotiation works end-to-end.""" + response = await mcp_server.handle_request( + { + "jsonrpc": "2.0", + "id": 3, + "method": "tools/call", + "params": { + "name": "code_analyzer", + "arguments": {"code": "def f():\n return 1\n"}, + }, + } + ) + + assert "error" not in response + assert response["result"]["tool"] == "code_analyzer" + + +@pytest.mark.asyncio +async def test_unknown_method_returns_json_rpc_error(mcp_server: MCPServer): + """Unsupported methods must return a well-formed JSON-RPC error, not crash.""" + response = await mcp_server.handle_request( + {"jsonrpc": "2.0", "id": 4, "method": "not/a/real/method", "params": {}} + ) + + assert response["jsonrpc"] == "2.0" + assert response["id"] == 4 + assert "error" in response + assert response["error"]["code"] == -32000