# AI Context & Coding Guidelines
This document provides context, architectural decisions, and coding standards for AI assistants (GitHub Copilot, Cursor, etc.) working on the `mcp-agileday` repository.
## 1. Project Overview
This is a **Model Context Protocol (MCP) Server** for the **Agileday** platform (SaaS).
- **Goal:** Bridge Agileday's employee and competence data with LLMs.
- **Language:** Python 3.11+
- **Transport:** Supports both `stdio` (for local Docker/Desktop usage) and `http-streamable` (SSE) via Starlette/Uvicorn.
## 2. Architectural Patterns (CRITICAL)
### The "Raw ASGI" Pattern
We use **Starlette** for routing, but we **DO NOT** use standard Starlette request/response handlers for the MCP endpoints.
* **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.
* **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)`.
* **Do Not:** Do not refactor the MCP routes to return standard `starlette.responses.Response` objects unless handling errors *outside* the SDK logic.
### Logic Separation
To ensure testability, we separate **Tool Definitions** from **Business Logic**.
* **Pattern:**
1. Define the tool in `list_tools`.
2. In `call_tool`, immediately delegate to a helper function (e.g., `find_experts_logic`, `profile_logic`).
3. The helper function performs the API call and string formatting.
* **Benefit:** This allows unit tests (`tests/test_agileday_server.py`) to test logic without needing to mock the entire MCP server protocol.
## 3. Tech Stack & Dependencies
* **Core:** `mcp` (Model Context Protocol SDK)
* **Web Server:** `starlette`, `uvicorn`
* **HTTP Client:** `requests` (for synchronous calls inside logic), `httpx` (for testing ASGI)
* **Testing:** `pytest`
## 4. Testing Guidelines
### Unit Tests (`tests/test_agileday_server.py`)
* **Scope:** Tests business logic functions (e.g., `find_experts_logic`).
* **Mocking:** strictly mock `requests.get` using `unittest.mock.patch`. Never make real HTTP calls.
* **Data:** Create minimal mock JSON responses that match the Agileday API structure (see `openapi.yaml` for reference).
### Transport Tests (`tests/test_transport.py`)
* **Scope:** Tests the HTTP/SSE endpoints (`/mcp`, `/health`).
* **Mocking Strategy:** Because the SDK writes to the network socket, mocks must simulate ASGI side effects.
* **Pattern:**
```python
# Example of required mock behavior for handle_post_message
async def side_effect(scope, receive, send):
await send({'type': 'http.response.start', 'status': 202, ...})
await send({'type': 'http.response.body', 'body': b'Accepted'})
```
## 5. API Knowledge (Agileday)
* **Authentication:** Bearer Token via `AGILEDAY_API_TOKEN`.
* **Base URL:** `https://{tenant}.agileday.io/api`.
* **Key Schemas:**
* **Skills:** Contain `proficiency` (float), `motivation` (float), and `experience` (string/duration).
* **Employees:** Return lists of skills.
* **Terminology:** Use "Motivation" (internal data key), but it can be referred to as "Willingness" in user-facing output if requested.
## 6. Directory Structure
```text
.
├── src/
│ └── agileday_server.py # SINGLE FILE implementation (keep it simple)
├── tests/
│ ├── test_agileday_server.py # Logic tests
│ └── test_transport.py # Server/Protocol tests
├── Dockerfile # Production build (defaults to http transport)
├── requirements.txt
└── README.md
```
## 7. Common Tasks
### Adding a New Tool
- Add the Tool definition in list_tools.
- Add the dispatch logic in call_tool.
- Create a def new_tool_logic(...) -> str function.
- Add a test case in tests/test_agileday_server.py verifying the logic output.
### Improving Search
When the user asks for "experts", always try to expose:
- Proficiency (Level)
- Experience Duration (if available in API)
- Motivation/Willingness (via the fire icon or text)