--- vcs: system: github owner: christianclaudio repo: mcp-server-espn default_branch: main branch_policy: pr_only --- # AGENTS.md Instructions for AI coding agents (Antigravity, Claude Code, Copilot, Cursor, Windsurf) working on this repository. --- ## 🎯 Project Overview This is `mcp-server-espn` — an enterprise Model Context Protocol (MCP) server providing real-time scores, play-by-play data, rosters, player statistics, betting odds, and prediction market resolution data from ESPN's public APIs. Built on FastMCP 4 Server Composition, it supports stdio and modern Streamable HTTP transports. The expected tool set lives in `scripts/check_tool_contract.py`. --- ## 📚 Canonical Documentation & Live Doc MCPs Before designing, implementing, or updating any MCP tool, always consult the official machine-readable documentation indexes ("the bibles") and live documentation MCP servers: ### Machine-Readable Documentation Indexes (`llms.txt`) | Resource | URL | Focus Areas | | :--- | :--- | :--- | | **FastMCP 4 Framework** | [`https://gofastmcp.com/llms.txt`](https://gofastmcp.com/llms.txt) | Server composition (`mount`), hierarchical middleware, transforms (`ToolTransform`, `ToolSearch`), lifespans, in-memory testing | | **Model Context Protocol (Official)** | [`https://modelcontextprotocol.io/llms.txt`](https://modelcontextprotocol.io/llms.txt) | Wire protocol spec (Spec 2026-07-28), Streamable HTTP framing, tool annotations, elicitation, catalog caching | ### Live Documentation MCP Servers Both ecosystems publish live, queryable Documentation MCP servers exposing full search and doc navigation tools: 1. **FastMCP Documentation Server**: - **Endpoint**: `https://gofastmcp.com/mcp` (SSE / Streamable HTTP) - **Tools**: `search_fast_mcp(query)`, `query_docs_filesystem_fast_mcp(command)`, `submit_feedback(...)` 2. **Anthropic Model Context Protocol Server**: - **Endpoint**: `https://modelcontextprotocol.io/mcp` (SSE / Streamable HTTP) - **Tools**: `search_model_context_protocol(query)`, `query_docs_filesystem_model_context_protocol(command)`, `submit_feedback(...)` --- ## 🏗️ Key Paths - `src/espn_mcp/server.py` — root FastMCP server: mounts the `games`, `teams`, and `news` sub-servers with matching namespaces; resources, prompts. - `src/espn_mcp/tools/{games,teams,news}.py` — domain sub-servers holding every tool; re-exported from `tools/__init__.py`. - `src/espn_mcp/client.py` — async `ESPNClient` (pooling, retries, path encoding, league aliases). `auth.py` — `ESPN_MCP_AUTH_TOKEN` bearer verifier and the tokenless non-localhost HTTP bind refusal. `errors.py` — typed errors and redaction. `middleware.py` — parent and child middleware. `config.py` — Pydantic settings (cache TTLs, profile, ports). - `scripts/check_tool_contract.py` — source of truth for the expected tool set and annotations. Do not hard-code tool counts elsewhere. - `scripts/check_openapi_drift.py`, `scripts/check_conformance.sh` + `conformance-baseline.yml`. - `scripts/release_notes.py` — release body from squash commits since the previous `v*` tag. `scripts/check_version.py` — runs after `uv build` and reads the version from the single wheel in `dist/` (the file that ships, as release.yml's tag check does); fails on `0.0.0` (no git metadata) or `0.0.1.devN` (no reachable tag, a shallow checkout). - `tests/` — offline unit, layered-composition, and protocol tests; `test_e2e_live.py` is opt-in (`uv run pytest -m e2e --no-cov`). - `.github/workflows/` — `ci.yml` (lint, py3.11–3.14 tests, contract/drift/protocol/conformance, build, `docker-build`, `drift-check` (scheduled and manual runs only)), `release.yml` (on a `v*` tag, five jobs: `build` (contents: read) builds with full history, checks the wheel version matches the tag, builds the release notes, checks the packages and the SBOM; `publish` (id-token and contents: write) uploads to PyPI and creates the GitHub Release from `scripts/release_notes.py` with the wheel, sdist and SBOM; `docker` (packages: write) pushes the GHCR image with no buildx provenance or SBOM and exposes its digest; `attest` (the only job with attestations: write) attests `dist/*` and the image digest; `registry` publishes to the MCP Registry last, with the tag version stamped into `server.json`), `dependabot-automerge.yml` (squash auto-merge only for Dependabot PRs whose highest update is minor or patch; major updates wait for a human review; Docker base image updates always wait for review). - `server.json` (MCP Registry metadata), `Dockerfile`, `fastmcp.json`, `pyproject.toml` (console scripts `mcp-server-espn`, which must equal the `server.json` identifier, and `espn-mcp`, which the README configs and Dockerfile run). --- ## ⚡ The Canonical Workflow: Adding a Tool for an Endpoint 1. **Client Method (`src/espn_mcp/client.py`)**: - Add a strictly-typed `async def` method on `ESPNClient`. - Apply league/sport alias normalization (e.g. `nfl` $\rightarrow$ `sport="football", league="nfl"`). - URL path parameters must be sanitized with `quote(str(seg), safe="")`. 2. **Tool Handler (`src/espn_mcp/tools/{games,teams,news}.py`)**: - Register on the domain sub-server with `@_server.tool(...)` and `@espn_tool`. `server.py` mounts each sub-server with its namespace. - Document arguments with clear docstrings and default options. 3. **Annotations & Gating**: - All ESPN tools are read-only (`readOnlyHint=True`, `destructiveHint=False`, `idempotentHint=True`, `openWorldHint=True`). 4. **Pure Offline Testing**: - Add unit tests in `tests/test_server.py` and `tests/test_client.py` using `httpx.MockTransport`. - Zero live network calls during tests. Maintain 100% statement coverage. - Update expected tool count in `scripts/check_tool_contract.py`. --- ## 🛡️ Non-Negotiable Safety & Protocol Rules 1. **Dynamic User-Agent**: - Client headers must dynamically resolve version: `"User-Agent": f"mcp-server-espn/{__version__}"`. 2. **Secret Redaction**: - All errors and logs pass through regex redaction (`_redact_secrets`). PEM blocks run from `-----BEGIN