The CTFd MCP Server enables AI assistants to interact with CTFd (Capture The Flag) platforms as a regular user for browsing, solving, and managing challenges.
Core Capabilities:
List challenges - Retrieve visible challenges with optional filters for category and unsolved status
View challenge details - Fetch comprehensive information including descriptions (HTML and plain text), metadata, attachment URLs, and solved status
Submit flags - Attempt flag submissions and receive immediate feedback on correctness
Manage dynamic containers - Start and stop Docker/Kubernetes-based instances with unified API that auto-detects backend plugins (ctfd-whale, ctfd-owl, or k8s)
Access challenge resources - Read markdown snapshots via MCP resources with metadata, descriptions, attachment URLs, and connection information
Authentication Support:
Username/password login
API token authentication
Session cookie authentication
Optional CSRF token support for ctfd-owl plugin
Additional Features:
Direct attachment download via absolute URLs
Comprehensive error handling for authentication failures, rate limiting, and API errors
Configurable timeouts for API requests
Allows users to manage the lifecycle of dynamic Docker-based challenge containers, including starting and stopping instances via plugins such as ctfd-whale and ctfd-owl.
Provides tools to manage Kubernetes-backed challenge instances, enabling the starting and stopping of containerized environments for specific CTF challenges.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@CTFd MCP Serverlist the unsolved web challenges"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.
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) orCTFD_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:
Install
From PyPI (recommended):
uvx ctfd-mcp --helpFrom source checkout (no install):
uvx --from . ctfd-mcp --help
Run MCP server (stdio)
Cursor and Claude MCP config example
Codex MCP config example
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 justcontainer_id, owl/k8s needchallenge_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/containersreturns 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; setCTFD_CSRF_TOKENif 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 includesk8s(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: setCTFD_SESSIONfrom thesessioncookie after logging in.The client now supports logging in with
CTFD_USERNAMEandCTFD_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:
Telegram: @ismailgaleev
Jabber: ismailgaleev@chat.merlok.ru
Email: umbra2728@gmail.com
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 devLint/format:
uv run ruff check .anduv run ruff format .Tests:
uv run pytestPre-commit:
uv run pre-commit install(seeCONTRIBUTING.md)
License
Apache-2.0. See LICENSE.