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
12 changes: 12 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.git
.github
.ruff_cache
.venv
__pycache__
*.pyc
.env
.env.*
!.env.example
deploy
images
tests
30 changes: 27 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
APPWRITE_API_KEY=
APPWRITE_PROJECT_ID=
APPWRITE_ENDPOINT=
# Appwrite Cloud base endpoint. The served project's OAuth authorization server
# lives under it at <APPWRITE_ENDPOINT>/oauth2/<APPWRITE_PROJECT_ID>.
APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1

# External URL clients use to reach THIS MCP server. Used to build the canonical
# resource URI (<MCP_PUBLIC_URL>/mcp) and RFC 9728 metadata. For hosted
# deployment, set this to your public HTTPS origin, for example
# https://mcp.appwrite.io.
MCP_PUBLIC_URL=http://localhost:8000

# The single Appwrite project this MCP serves. Defaults to the Cloud console
# project; override only for non-console deployments. Tokens issued by any other
# project's OAuth server are rejected.
# APPWRITE_PROJECT_ID=console

# HTTP bind address.
HOST=0.0.0.0
PORT=8000

# Optional. Enables appwrite_search_docs by embedding incoming docs queries.
# OPENAI_API_KEY=sk-...

# ── Integration tests only (not used by the running server) ─────────────────
# The live integration suite authenticates to the Appwrite API with an API key
# and runs against APPWRITE_PROJECT_ID (above).
# APPWRITE_PROJECT_ID=your-project-id
# APPWRITE_API_KEY=your-api-key
41 changes: 30 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,29 @@ on:
push:

jobs:
format:
name: Format
lint:
name: Lint & format
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"

- name: Set up uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.22"

- name: Install dev dependencies
run: uv sync --group dev

- name: Lint
run: uv run --group dev ruff check src tests

- name: Check formatting
run: uv run --group dev black --check src tests

Expand All @@ -31,22 +36,34 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"

- name: Set up uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.22"

- name: Install dependencies
run: uv sync

- name: Run unit tests
run: uv run python -m unittest discover -s tests/unit -v

docker:
name: Docker build
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Build hosted HTTP image
run: docker build -t appwrite-mcp:ci .

integration:
name: Integration
runs-on: ubuntu-latest
Expand All @@ -57,15 +74,17 @@ jobs:
APPWRITE_ENDPOINT: ${{ secrets.APPWRITE_ENDPOINT }}
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"

- name: Set up uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.11.22"

- name: Install dependencies
run: uv sync --extra integration
Expand Down
74 changes: 74 additions & 0 deletions .github/workflows/production.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Production deployment

on:
release:
types: [published]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false

env:
ENVIRONMENT: production
PROJECT: mcp
DECLARATIVE_OWNER: appwrite-labs
DECLARATIVE_REPOSITORY: assets-applications
REGISTRY_GITHUB: ghcr.io
IMAGE_NAME: appwrite/mcp
TAG: ${{ github.event.release.tag_name || github.sha }}

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout the repo
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6

- name: Login to GitHub Container Registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
registry: ${{ env.REGISTRY_GITHUB }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: .
push: true
tags: ${{ env.REGISTRY_GITHUB }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}

deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Get token for ${{ env.DECLARATIVE_REPOSITORY }}
id: app-token
uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2
with:
app-id: ${{ vars.DECLARATIVE_DEPLOYMENT_GITHUB_APP_ID }}
private-key: ${{ secrets.DECLARATIVE_DEPLOYMENT_GITHUB_APP_PRIVATE_KEY }}
owner: ${{ env.DECLARATIVE_OWNER }}
repositories: ${{ env.DECLARATIVE_REPOSITORY }}

- name: Checkout ${{ env.DECLARATIVE_REPOSITORY }}
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
repository: ${{ env.DECLARATIVE_OWNER }}/${{ env.DECLARATIVE_REPOSITORY }}
token: ${{ steps.app-token.outputs.token }}

- name: Update image tag
run: yq -i '.["mcp"].image.tag = strenv(TAG)' ${{ env.ENVIRONMENT }}/${{ env.PROJECT }}/fra1.yaml

- name: Commit and push
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add ${{ env.ENVIRONMENT }}/${{ env.PROJECT }}/fra1.yaml
if git diff --cached --quiet; then
echo "No changes to commit"
else
git commit -m "chore(${{ env.ENVIRONMENT }}): ${{ env.PROJECT }} image tag to ${{ env.TAG }}"
git push
fi
6 changes: 3 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"

- name: Install build dependencies
run: |
python -m pip install --upgrade pip
pip install build twine
pip install build==1.5.0 twine==6.2.0

- name: Build package
run: python -m build
Expand Down
75 changes: 75 additions & 0 deletions .github/workflows/staging.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Staging deployment

on:
workflow_dispatch:
push:
branches:
- main

concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false

env:
ENVIRONMENT: staging
PROJECT: mcp
DECLARATIVE_OWNER: appwrite-labs
DECLARATIVE_REPOSITORY: assets-applications
REGISTRY_GITHUB: ghcr.io
IMAGE_NAME: appwrite/mcp
TAG: ${{ github.sha }}

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout the repo
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6

- name: Login to GitHub Container Registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
with:
registry: ${{ env.REGISTRY_GITHUB }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: .
push: true
tags: ${{ env.REGISTRY_GITHUB }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}

deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Get token for ${{ env.DECLARATIVE_REPOSITORY }}
id: app-token
uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2
with:
app-id: ${{ vars.DECLARATIVE_DEPLOYMENT_GITHUB_APP_ID }}
private-key: ${{ secrets.DECLARATIVE_DEPLOYMENT_GITHUB_APP_PRIVATE_KEY }}
owner: ${{ env.DECLARATIVE_OWNER }}
repositories: ${{ env.DECLARATIVE_REPOSITORY }}

- name: Checkout ${{ env.DECLARATIVE_REPOSITORY }}
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
repository: ${{ env.DECLARATIVE_OWNER }}/${{ env.DECLARATIVE_REPOSITORY }}
token: ${{ steps.app-token.outputs.token }}

- name: Update image tag
run: yq -i '.["mcp"].image.tag = strenv(TAG)' ${{ env.ENVIRONMENT }}/${{ env.PROJECT }}/fra1.yaml

- name: Commit and push
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add ${{ env.ENVIRONMENT }}/${{ env.PROJECT }}/fra1.yaml
if git diff --cached --quiet; then
echo "No changes to commit"
else
git commit -m "chore(${{ env.ENVIRONMENT }}): ${{ env.PROJECT }} image tag to ${{ env.TAG }}"
git push
fi
22 changes: 22 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM python:3.12-slim AS base

ENV PYTHONUNBUFFERED=1 \
UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy

WORKDIR /app

COPY --from=ghcr.io/astral-sh/uv:0.11.22 /uv /usr/local/bin/uv

COPY pyproject.toml uv.lock README.md ./
COPY src ./src

RUN uv sync --frozen --no-dev

ENV HOST=0.0.0.0 \
PORT=8000 \
APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1

EXPOSE 8000

CMD ["uv", "run", "mcp-server-appwrite"]
Loading
Loading