AGENTS.md

July 16, 2026 · View on GitHub

Guidance for AI agents and human contributors working in this repository.

What this repo is

mcp-server-appwrite is a Model Context Protocol server for Appwrite. It exposes Appwrite's API to MCP clients as a small set of operator-style tools, supporting two deployments from one codebase:

  • Cloud (hosted HTTP): a Starlette ASGI app that acts as an OAuth 2.1 Resource Server. It validates the client's bearer token and forwards it to the Appwrite REST API. Served primarily at mcp.appwrite.io/, with /mcp also available as a conventional alias.
  • Self-hosted (stdio): runs locally and authenticates with a project API key (APPWRITE_PROJECT_ID, APPWRITE_API_KEY, APPWRITE_ENDPOINT).

Python ≥ 3.12, packaged with hatchling, managed with uv.

Architecture

Source lives in src/mcp_server_appwrite/:

FileResponsibility
__main__.py / server.pyEntry point, CLI args, transport selection (--transport stdio|http), service registration, low-level MCP server.
http_app.pyHosted Streamable-HTTP transport: / plus the /mcp alias, RFC 9728 protected-resource metadata, /healthz.
auth.pyOAuth 2.1 resource-server layer — bearer-token validation against the project's Appwrite authorization server.
flags.pyCentral registry of tester/testing flags (CLI arg + env var per flag). Enabling and testing them is documented in docs/flags.md.
service.pyService base class: introspects an Appwrite SDK service and turns its methods into MCP tool definitions.
tool_manager.pyRegistry of all services and their generated tools.
operator.pyThe compact "operator" surface — appwrite_search_tools, appwrite_call_tool, result/resource storage, write confirmation.
context.pyappwrite_get_context — workspace summary (project, services, account/org for OAuth).
docs_search.pyIn-process semantic docs search (appwrite_search_docs) over a prebuilt index.
telemetry.pyOpenTelemetry metrics layer (OTLP/HTTP). No-op unless an OTLP endpoint is configured and the transport is http.
data/Committed docs index artifact (docs_index.npz, docs_index_meta.json), shipped in the wheel/image.

scripts/build_docs_index.py rebuilds the docs index (requires OPENAI_API_KEY).

Telemetry (metrics)

telemetry.py emits OpenTelemetry metrics (prefixed mcp.) over OTLP/HTTP. It is a no-op unless the transport is http and OTEL_EXPORTER_OTLP_ENDPOINT is set, so stdio and unconfigured servers stay silent. In the cluster that endpoint is the Alloy collector, which adds the deployment.* labels and forwards upstream — the app needs no credentials. Dashboards live in the dashboards repo under MCP/.

Tool surface (key design point)

The server boots in a compact workflow: the client sees up to 4 tools (appwrite_get_context, appwrite_search_tools, appwrite_call_tool, and optionally appwrite_search_docs), while the full Appwrite catalog (25 services) stays internal and is searched at runtime. Mutating hidden tools require confirm_write=true. Large outputs are stored as MCP resources and returned as a preview + resource URI.

Local development

# Install uv, then sync deps
uv sync                      # runtime deps
uv sync --group dev          # + black, ruff (lint/format)
uv sync --extra integration  # + integration-test deps

# Run hosted HTTP transport
MCP_PUBLIC_URL=http://localhost:8000 APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1 \
  uv run mcp-server-appwrite --transport http

# Run self-hosted stdio transport
APPWRITE_ENDPOINT=http://localhost:9501/v1 \
APPWRITE_PROJECT_ID=<id> APPWRITE_API_KEY=<key> \
  uv run mcp-server-appwrite

# Or via Docker (hosted HTTP/OAuth)
docker compose up --build    # compose.yaml; endpoint at http://localhost:8000/

Pre-PR checklist

Run these locally before opening a PR. They mirror the CI workflow (.github/workflows/ci.yml), which runs on every pull request and on pushes to main. All four jobs must pass.

  1. Lint (lint job)

    uv sync --group dev
    uv run --group dev ruff check src tests
    

    Ruff config: target-version = py312, rules E, F, W, I (import sorting), with E501 (line length) delegated to black.

  2. Format (lint job)

    uv run --group dev black --check src tests
    

    Run uv run --group dev black src tests (without --check) to auto-fix.

  3. Type check (lint job)

    uv run --group dev pyright
    

    Pyright config lives in pyproject.toml ([tool.pyright]): basic mode over src/, Python 3.12, resolving against the project .venv.

  4. Unit tests (unit job)

    uv sync
    uv run python -m unittest discover -s tests/unit -v
    

    Fast, no external services or credentials required.

  5. Docker build (docker job)

    docker build -t appwrite-mcp:ci .
    

    The hosted HTTP image must build cleanly.

  6. Integration tests (integration job) — CI runs these only for pushes and for PRs from branches on the same repo (not forks). They create and delete real Appwrite resources, so they need live credentials and are skipped when absent:

    uv sync --extra integration
    APPWRITE_PROJECT_ID=<id> APPWRITE_API_KEY=<key> APPWRITE_ENDPOINT=<url> \
      uv run --extra integration python -m unittest discover -s tests/integration -v
    

CI environment versions

CI pins Python 3.12 and uv 0.11.22. Match these locally if you hit version-specific differences.

If you change the docs index

Rebuilding src/mcp_server_appwrite/data/ requires OPENAI_API_KEY. Re-run the build script and commit the refreshed artifact:

OPENAI_API_KEY=sk-... uv run python scripts/build_docs_index.py

Other workflows

  • publish.yml — package publishing.
  • staging.yml / production.yml — deployment (publishes images to Docker Hub).

These are not gated on PRs the way ci.yml is, but be mindful when touching the Dockerfile, pyproject.toml version, or deployment config.

Release metadata

Release versions come from Git tags. Publish GitHub releases with tags in the form vMAJOR.MINOR.PATCH (for example, v0.8.8). Python package builds derive their version from the tag via Hatch VCS, and server.json is generated from server.template.json during the publish workflow before sending metadata to the MCP Registry.

Do not hand-edit a version in pyproject.toml, constants.py, or server.json. For local registry metadata checks, render the generated file explicitly:

uv run python scripts/render_server_json.py 0.8.8

Conventions

  • Keep the exposed tool surface small — new Appwrite capabilities should flow through the operator/catalog mechanism, not become top-level tools.
  • New SDK services are registered automatically; you generally don't hand-write tool definitions.
  • Match existing style: black formatting, ruff-clean imports, type hints, module docstrings explaining intent (see auth.py, http_app.py, docs_search.py).
  • Add unit tests under tests/unit/ for any non-trivial logic; add integration coverage under tests/integration/ when touching real API behavior.