Labsco
umbra2728 logo

ctfd-mcp

โ˜… 25

from umbra2728

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

๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅโœ“ VerifiedAccount requiredNeeds API keys

CTFd MCP server (user scope)

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

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.

Support / feedback

If something breaks or you have questions, reach out:

Testing

  • Run uv run pytest.
  • 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 pytest
  • Pre-commit: uv run pre-commit install (see CONTRIBUTING.md)