|
| 1 | +# AI Context & Coding Guidelines |
| 2 | + |
| 3 | +This document provides context, architectural decisions, and coding standards for AI assistants (GitHub Copilot, Cursor, etc.) working on the `mcp-agileday` repository. |
| 4 | + |
| 5 | +## 1. Project Overview |
| 6 | +This is a **Model Context Protocol (MCP) Server** for the **Agileday** platform (SaaS). |
| 7 | +- **Goal:** Bridge Agileday's employee and competence data with LLMs. |
| 8 | +- **Language:** Python 3.11+ |
| 9 | +- **Transport:** Supports both `stdio` (for local Docker/Desktop usage) and `http-streamable` (SSE) via Starlette/Uvicorn. |
| 10 | + |
| 11 | +## 2. Architectural Patterns (CRITICAL) |
| 12 | + |
| 13 | +### The "Raw ASGI" Pattern |
| 14 | +We use **Starlette** for routing, but we **DO NOT** use standard Starlette request/response handlers for the MCP endpoints. |
| 15 | +* **Why:** The `mcp` Python SDK writes responses directly to the ASGI `send` channel. If Starlette also tries to send a `Response` object, the server crashes with a "Double Response" runtime error. |
| 16 | +* **Rule:** When touching `src/agileday_server.py` transport layer, ensure the `MCPHandler` class uses the raw ASGI signature: `async def __call__(self, scope, receive, send)`. |
| 17 | +* **Do Not:** Do not refactor the MCP routes to return standard `starlette.responses.Response` objects unless handling errors *outside* the SDK logic. |
| 18 | + |
| 19 | +### Logic Separation |
| 20 | +To ensure testability, we separate **Tool Definitions** from **Business Logic**. |
| 21 | +* **Pattern:** |
| 22 | + 1. Define the tool in `list_tools`. |
| 23 | + 2. In `call_tool`, immediately delegate to a helper function (e.g., `find_experts_logic`, `profile_logic`). |
| 24 | + 3. The helper function performs the API call and string formatting. |
| 25 | +* **Benefit:** This allows unit tests (`tests/test_agileday_server.py`) to test logic without needing to mock the entire MCP server protocol. |
| 26 | + |
| 27 | +## 3. Tech Stack & Dependencies |
| 28 | +* **Core:** `mcp` (Model Context Protocol SDK) |
| 29 | +* **Web Server:** `starlette`, `uvicorn` |
| 30 | +* **HTTP Client:** `requests` (for synchronous calls inside logic), `httpx` (for testing ASGI) |
| 31 | +* **Testing:** `pytest` |
| 32 | + |
| 33 | +## 4. Testing Guidelines |
| 34 | + |
| 35 | +### Unit Tests (`tests/test_agileday_server.py`) |
| 36 | +* **Scope:** Tests business logic functions (e.g., `find_experts_logic`). |
| 37 | +* **Mocking:** strictly mock `requests.get` using `unittest.mock.patch`. Never make real HTTP calls. |
| 38 | +* **Data:** Create minimal mock JSON responses that match the Agileday API structure (see `openapi.yaml` for reference). |
| 39 | + |
| 40 | +### Transport Tests (`tests/test_transport.py`) |
| 41 | +* **Scope:** Tests the HTTP/SSE endpoints (`/mcp`, `/health`). |
| 42 | +* **Mocking Strategy:** Because the SDK writes to the network socket, mocks must simulate ASGI side effects. |
| 43 | +* **Pattern:** |
| 44 | + ```python |
| 45 | + # Example of required mock behavior for handle_post_message |
| 46 | + async def side_effect(scope, receive, send): |
| 47 | + await send({'type': 'http.response.start', 'status': 202, ...}) |
| 48 | + await send({'type': 'http.response.body', 'body': b'Accepted'}) |
| 49 | + ``` |
| 50 | + |
| 51 | +## 5. API Knowledge (Agileday) |
| 52 | +* **Authentication:** Bearer Token via `AGILEDAY_API_TOKEN`. |
| 53 | +* **Base URL:** `https://{tenant}.agileday.io/api`. |
| 54 | +* **Key Schemas:** |
| 55 | + * **Skills:** Contain `proficiency` (float), `motivation` (float), and `experience` (string/duration). |
| 56 | + * **Employees:** Return lists of skills. |
| 57 | +* **Terminology:** Use "Motivation" (internal data key), but it can be referred to as "Willingness" in user-facing output if requested. |
| 58 | + |
| 59 | +## 6. Directory Structure |
| 60 | +```text |
| 61 | +. |
| 62 | +├── src/ |
| 63 | +│ └── agileday_server.py # SINGLE FILE implementation (keep it simple) |
| 64 | +├── tests/ |
| 65 | +│ ├── test_agileday_server.py # Logic tests |
| 66 | +│ └── test_transport.py # Server/Protocol tests |
| 67 | +├── Dockerfile # Production build (defaults to http transport) |
| 68 | +├── requirements.txt |
| 69 | +└── README.md |
| 70 | + |
| 71 | +``` |
| 72 | + |
| 73 | +## 7. Common Tasks |
| 74 | + |
| 75 | +### Adding a New Tool |
| 76 | + |
| 77 | +- Add the Tool definition in list_tools. |
| 78 | + |
| 79 | +- Add the dispatch logic in call_tool. |
| 80 | + |
| 81 | +- Create a def new_tool_logic(...) -> str function. |
| 82 | + |
| 83 | +- Add a test case in tests/test_agileday_server.py verifying the logic output. |
| 84 | + |
| 85 | +### Improving Search |
| 86 | +When the user asks for "experts", always try to expose: |
| 87 | + |
| 88 | +- Proficiency (Level) |
| 89 | + |
| 90 | +- Experience Duration (if available in API) |
| 91 | + |
| 92 | +- Motivation/Willingness (via the fire icon or text) |
0 commit comments