Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# AGENTS.md

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

## What this repo is

`mcp-server-appwrite` is a [Model Context Protocol](https://modelcontextprotocol.io)
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 at `mcp.appwrite.io/mcp`.
- **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/`:

| File | Responsibility |
| --- | --- |
| `__main__.py` / `server.py` | Entry point, CLI args, transport selection (`--transport stdio\|http`), service registration, low-level MCP server. |
| `http_app.py` | Hosted Streamable-HTTP transport: `/mcp`, RFC 9728 protected-resource metadata, `/healthz`. |
| `auth.py` | OAuth 2.1 resource-server layer — bearer-token validation against the project's Appwrite authorization server. |
| `service.py` | `Service` base class: introspects an Appwrite SDK service and turns its methods into MCP tool definitions. |
| `tool_manager.py` | Registry of all services and their generated tools. |
| `operator.py` | The compact "operator" surface — `appwrite_search_tools`, `appwrite_call_tool`, result/resource storage, write confirmation. |
| `context.py` | `appwrite_get_context` — workspace summary (project, services, account/org for OAuth). |
| `docs_search.py` | In-process semantic docs search (`appwrite_search_docs`) over a prebuilt index. |
| `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`).

### 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

```bash
# 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/mcp
```

## 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)
```bash
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)
```bash
uv run --group dev black --check src tests
```
Run `uv run --group dev black src tests` (without `--check`) to auto-fix.

3. **Unit tests** (`unit` job)
```bash
uv sync
uv run python -m unittest discover -s tests/unit -v
```
Fast, no external services or credentials required.

4. **Docker build** (`docker` job)
```bash
docker build -t appwrite-mcp:ci .
```
The hosted HTTP image must build cleanly.

5. **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:
```bash
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:
```bash
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.

## 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.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,13 @@ The MCP server validates the bearer access token on every request and forwards i

The server starts in a compact workflow so the MCP client only sees a small operator-style surface while the full Appwrite catalog stays internal.

- Up to 3 MCP tools are exposed to the model:
- Up to 4 MCP tools are exposed to the model:
- `appwrite_get_context`
- `appwrite_search_tools`
- `appwrite_call_tool`
- `appwrite_search_docs` — semantic search over the Appwrite documentation (only registered when the docs index and `OPENAI_API_KEY` are present; see [Documentation search](#documentation-search)).
- The full Appwrite tool catalog stays internal and is searched at runtime.
- `appwrite_get_context` gives the client a quick workspace summary. With a local project API key it returns the configured project and readable service totals/samples. With hosted OAuth it also includes account, organization, and discovered project context.
- Large tool outputs are stored as MCP resources and returned as preview text plus a resource URI.
- Mutating hidden tools require `confirm_write=true`.
- Every Appwrite service the installed SDK ships is registered automatically — 25 in total, each becoming a tool-name prefix: `account`, `activities`, `advisor`, `apps`, `avatars`, `backups`, `databases`, `functions`, `graphql`, `health`, `locale`, `messaging`, `oauth2`, `organization`, `presences`, `project`, `proxy`, `sites`, `storage`, `tables_db`, `teams`, `tokens`, `usage`, `users`, and `webhooks`. Which ones a given user can actually call is gated by the scopes their OAuth token was granted (enforced per-route by the Appwrite API), not by the catalog.
Expand Down
Loading
Loading