Skip to main content

MCP server for CTFd that lets regular users browse challenges, manage dynamic instances, and submit flags.

Project description

CTFd MCP server (user scope)

GitHub Release License Python Issues

MCP server that lets a regular CTFd user list challenges, read details, start/stop dynamic docker instances, and submit flags.

Requirements

  • Python 3.13 (managed by uv).
  • Environment variables (choose one auth method):
  • CTFD_URL (e.g. https://ctfd.example.com)
  • CTFD_TOKEN (user token, not admin) or CTFD_SESSION (session cookie if tokens are disabled).
    • CTFD_CSRF_TOKEN (optional, only if the server/plugin requires CSRF for ctfd-owl).

You can store them in a .env file in the repo root:

CTFD_URL=https://ctfd.example.com/
CTFD_USERNAME=your_username
CTFD_PASSWORD=your_password
# or, if you prefer to use a token:
# CTFD_TOKEN=your_ctfd_api_token_here
# or, if tokens are disabled:
# CTFD_SESSION=your_session_token_here
# and, if the owl plugin enforces CSRF:
# CTFD_CSRF_TOKEN=your_csrf_token_here

Install

  • From PyPI (recommended): uvx ctfd-mcp --help
  • From source checkout (no install): uvx --from . ctfd-mcp --help

Run MCP server (stdio)

# installed from PyPI
uvx ctfd-mcp
# from local checkout
uvx --from . ctfd-mcp

Cursor and Claude MCP config example

{
  "mcpServers": {
    "ctfd-mcp": {
      "command": "uvx",
      "args": ["ctfd-mcp"],
      "env": {
        "CTFD_URL": "https://ctfd.example.com",
        "CTFD_TOKEN": "your_user_token"
      }
    }
  }
}

Codex MCP config example

[mcp_servers.ctfd-mcp]
command = "uvx"
args = ["ctfd-mcp"]

[mcp_servers.ctfd-mcp.env]
CTFD_URL = "https://ctfd.example.com"
CTFD_TOKEN = "your_user_token"

Exposed tools

  • list_challenges(category?, only_unsolved?) — list visible challenges, optional category/unsolved filter.
  • challenge_details(challenge_id) — description (HTML + description_text), metadata, attachment URLs, solved status.
  • submit_flag(challenge_id, flag) — attempt a flag; returns status/message.
  • start_container(challenge_id) — unified start; auto-detects dynamic_docker, ctfd-owl or k8s /api/v1/k8s.
  • stop_container(container_id?, challenge_id?) — unified stop; whale can be stopped with just container_id, owl/k8s need challenge_id.

Attachments are returned as absolute URLs in files; the client/host can fetch them directly.

MCP resources

  • resource://ctfd/challenges/{challenge_id} — markdown snapshot of a challenge (metadata, description, attachment URLs, connection info if present).

Error handling

  • Missing env/config -> clear MCP error.
  • 401/403 -> auth failed, check token or session cookie.
  • 404 -> not found (or dynamic container API missing).
  • 429 -> rate limited (Retry-After if present).
  • Other HTTP/API errors -> surfaced as MCP errors with CTFd message/status.

Notes and troubleshooting

  • Dynamic containers require the ctfd-whale (dynamic_docker) plugin on the target CTFd; otherwise /api/v1/containers returns 404.
  • Owl challenges (dynamic_check_docker) use a different endpoint: /plugins/ctfd-owl/container?challenge_id=<id>. They usually require a session cookie, and some setups require a CSRF token; set CTFD_CSRF_TOKEN if needed.
  • Some events expose Kubernetes-backed instances at /api/v1/k8s/{get,create,delete} with multipart form data; the client will try these when the challenge type includes k8s (or when a dynamic_docker endpoint is missing).
  • If the server redirects you to /login (302) when using a token, switch to a browser session cookie: set CTFD_SESSION from the session cookie after logging in.
  • The client now supports logging in with CTFD_USERNAME and CTFD_PASSWORD; these fields take precedence over stale tokens/sessions.
  • Auth priority: username/password first, then token, then session cookie. Lower-priority credentials are ignored when a higher-priority option is present.

Support / feedback

If something breaks or you have questions, reach out:

Testing

  • Run uv run python -m tests.test_ctfd_client (requires a real CTFD_URL plus token or username/password) to exercise challenge fetching/submission flows.
  • Timeouts are configurable via env: CTFD_TIMEOUT (total), CTFD_CONNECT_TIMEOUT, CTFD_READ_TIMEOUT (seconds). Defaults are 20s total / 10s connect / 15s read.

Development

  • Dev dependencies: uv sync --group dev
  • Lint/format: uv run ruff check . and uv run ruff format .
  • Tests: uv run python -m unittest discover -s tests
  • Pre-commit: uv run pre-commit install (see CONTRIBUTING.md)

License

Apache-2.0. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ctfd_mcp-0.1.1.tar.gz (54.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ctfd_mcp-0.1.1-py3-none-any.whl (22.0 kB view details)

Uploaded Python 3

File details

Details for the file ctfd_mcp-0.1.1.tar.gz.

File metadata

  • Download URL: ctfd_mcp-0.1.1.tar.gz
  • Upload date:
  • Size: 54.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ctfd_mcp-0.1.1.tar.gz
Algorithm Hash digest
SHA256 a2392d2570cc6c63446471daacba44cb2e096f173c07b6513fa094ee7913142b
MD5 ba6d085ede4813f0242d29ebb21c0669
BLAKE2b-256 b718b5204496e1158c41f76c9b14c999fe0a110989b81c0c5f46c0d0a50d220e

See more details on using hashes here.

Provenance

The following attestation bundles were made for ctfd_mcp-0.1.1.tar.gz:

Publisher: publish.yml on umbra2728/ctfd-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ctfd_mcp-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: ctfd_mcp-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 22.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ctfd_mcp-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8d312cae5d1980ae85ef207d868f4f0149597f2a8f8d747a8a28183045cba4b8
MD5 988724edbb1bb5b5447c3f2fc6a9c63c
BLAKE2b-256 1bdb5fa6d13377bccc8e71d1e7ddc162875b8c57909c4550ef10c3fb38516f0e

See more details on using hashes here.

Provenance

The following attestation bundles were made for ctfd_mcp-0.1.1-py3-none-any.whl:

Publisher: publish.yml on umbra2728/ctfd-mcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page