An MCP server for browsing CSES problems, submissions, and submitting solutions
Project description
CSES MCP Server
Your AI assistant's gateway to CSES (Code Submission Evaluation System) ๐งฉ
๐ค What is this?
cses-mcp is a Python-based MCP (Model Context Protocol) server that lets
an MCP-compatible client (like Claude Desktop or Claude Code) act on your
behalf on CSES: browsing problems, reading problem statements, submitting
solutions, and checking submission history/verdicts.
As far as I know, CSES has no public API, so all data access is via scraping authenticated HTML pages and posting to existing web forms using your own session cookie.
Why?
CSES is one of the best free problem sets for learning competitive programming, but working through it usually means a lot of manual tab- switching: reading a statement, writing code elsewhere, coming back to submit, then digging through a verdict page to figure out what went wrong. This project turns that whole loop into a conversation โ ask an AI assistant to find you a problem, explain the statement, help you reason through an approach, submit your solution, and diagnose a wrong verdict, all without leaving the chat. The goal isn't to solve problems for you (though it can) โ it's to make the feedback loop tight enough that you actually learn faster: get hints instead of full solutions when you want them, understand why a submission got Wrong Answer or Time Limit Exceeded, and use past-attempt history to see your own progress over time.
๐ Quick Start
Pick whichever fits โ Option A if you just want to use it, Option B if you're editing the code.
Option A โ Copy-paste (no local clone)
Paste this straight into your MCP client config โ uvx fetches and runs
the published package, no clone, no uv sync, no .env file to manage:
{
"mcpServers": {
"cses-mcp": {
"command": "uvx",
"args": ["qhung-cses-mcp@latest"],
"env": {
"PHPSESSID": "<your CSES session cookie>"
}
}
}
}
Requirements:
uvinstalled (uvxships with it):# macOS / Linux curl -LsSf https://astral.sh/uv/install.sh | sh # Windows powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
- Your
PHPSESSIDpasted into theenvblock above โ see Getting your CSES session cookie.
Restart your MCP client and the tools are available โ that's it.
Not on PyPI yet, or want to track the repo directly instead?
Point --from at a plain HTTPS archive of the repo instead of the
published package โ still no git clone, no uv sync, and (unlike a
git+ URL) no local git executable required at all, which matters if
your MCP client runs in a sandboxed environment (e.g. Claude Desktop's
Windows package can't execute git even when it's installed and on
PATH):
{
"mcpServers": {
"cses-mcp": {
"command": "uvx",
"args": ["--from", "https://github.com/qhung23125005/CSES-MCP/archive/refs/heads/master.tar.gz", "qhung-cses-mcp"],
"env": {
"PHPSESSID": "<your CSES session cookie>"
}
}
}
}
A --from git+https://github.com/qhung23125005/CSES-MCP URL also works if
you want it to always resolve via git instead, but that requires a working
local git that your MCP client's process can actually execute โ see
Option B
if neither of these is reliable in your environment.
Option B โ Manual clone (for local development / editing the code)
- Prerequisites โ Python 3.12+ and
uv(see the install commands above). - Clone and install:
git clone https://github.com/qhung23125005/CSES-MCP.git cd CSES-MCP uv sync --all-extras cp .env.example .env
- Add your
PHPSESSIDto.envโ see Getting your CSES session cookie. - Run it standalone (for manual testing / the MCP Inspector):
make run # STDIO transport (default) make run-debug # STDIO with debug logging make run-sse # SSE transport on port 8000 make run-inspector # Run with the MCP Inspector (browser UI to call tools manually)
- Or connect an MCP client to the cloned copy:
{ "mcpServers": { "cses-mcp": { "command": "uv", "args": ["run", "--project", "/path/to/CSES-MCP", "python", "-m", "cses_mcp.main"] } } }
--projectpinsuvto this project's environment regardless of the working directory the client spawns from โ more reliable than acwdsetting..envis resolved relative to the project root automatically (seesrc/cses_mcp/config/settings.py), so noenvblock is needed in the client config โ though you can still use one instead of.envif you prefer, the same way Option A does.
Getting your CSES session cookie
The server authenticates as you using your CSES session cookie
(PHPSESSID) โ no tool ever takes a password or cookie as an argument, so
an AI assistant calling these tools never sees or handles the credential
itself.
- Log into cses.fi in your browser.
- Open DevTools (
F12) โ Application (Chrome/Edge) or Storage (Firefox) โ Cookies โhttps://cses.fi. - Copy the
PHPSESSIDvalue โ into the client config'senvblock (Option A) or into.env(Option B).
CSES sessions can expire; if tools start returning CSES_NOT_AUTHENTICATED,
re-copy a fresh cookie.
You're ready! Start issuing prompts via your MCP client โ see ๐ฌ Example Prompts for Claude.
๐ ๏ธ Available Tools
(Input parameters are strings unless otherwise specified)
fetch_categories(no auth): List every CSES problem category name (e.g. "Introductory Problems", "Sorting and Searching").- Returns: List of category name strings.
fetch_problems(auth required): List problems, optionally filtered by category/status, with your solve status for each.category(optional string): Exact category name, case/whitespace-insensitive (usefetch_categoriesto look one up).status(optional string): One of"completed","not accepted","not completed".- Returns: List of
{name, link, status, category}.
fetch_problem_statement(no auth): Fetch a single problem's statement.url(string): Absolute problem page URL, e.g.https://cses.fi/problemset/task/1068.- Returns:
{Title, Limit: {time_limit, memory_limit}, description, sections: {input, output, constraints, example}}.
submit_code(auth required): Submit a solution to a task for judging.task_id(string): The CSES task id, e.g."1068".filename(string): Used only to infer language from its extension, e.g."solution.py". Supported:.asm .c .cpp/.cc .hs .java .js .pas .py .rb .rs .scala.code(string): The full source code, in-memory โ no filesystem access needed.- Returns:
{task_id, submission_id, redirect}.
fetch_submission(auth required): Fetch one submission's details, per-test verdicts, and submitted code.submission_id(string): Fromsubmit_code's response or a result URL.- Returns:
{task, sender, submission_time, language, status, result, tests: [{test, verdict, time}], code}.
fetch_submission_list(auth required): List your past submissions for a task, most recent first.task_id(string): The CSES task id, e.g."2134".- Returns: List of
{submission_id, time, lang, code_time, code_size, result}.
Every tool returns a single error dict ({"error": true, "error_code": ..., "message": ..., ...})
on failure instead of raising โ check for "error": true before treating a
result as success. "auth required" tools need PHPSESSID set (see
Getting your CSES session cookie);
the others scrape public pages and work without one.
๐ฌ Example Prompts for Claude
Once connected, try prompts like:
- "What CSES problem categories are there?"
- "List all problems in Sorting and Searching that I haven't solved yet."
- "Give me the statement for Weird Algorithm."
- "Submit this Python solution to task 1068:
<code>." - "Was my last submission to Path Queries II accepted? If not, what verdict did it get?"
- "Show me the code I submitted for submission 17833364."
- "How many times have I submitted to task 2134, and what were the results?"
- "Find an easy problem I haven't solved in Dynamic Programming, show me the statement, and help me write a solution."
- "In my latest submission for Path Queries II, I got both WA and TLE. Identify the cause."
- "Complete the remaining unsolved problems in the Tree Algorithms category for me to use as reference."
- "Can you give me a hint on how to solve <problem name>?"
๐งช Testing
make test # Run tests (mocked HTTP, no network, no credentials needed)
make test-cov # Run tests with coverage
make lint # Ruff lint
make format # Ruff format/fix
Each tool's tests also include an opt-in live smoke test that hits the
real cses.fi site using your .env cookie โ skipped by default, run with:
RUN_LIVE_TESTS=1 uv run pytest -k live
๐ Project Structure
src/cses_mcp/
โโโ __init__.py
โโโ main.py # CLI entry point
โโโ server.py # Core server implementation
โโโ client.py # Ad-hoc script for exercising the server locally
โโโ config/
โ โโโ settings.py # Configuration management (pydantic-settings)
โโโ tools/ # MCP tools
โ โโโ fetch_categories_tools.py
โ โโโ fetch_problems_tools.py
โ โโโ submission_tools.py
โโโ resources/ # MCP resources (scaffolded, empty)
โโโ prompts/ # MCP prompts (scaffolded, empty)
โโโ handlers/
โ โโโ error_handler.py # Centralized tool error handling
โโโ utils/
โโโ logger.py
โโโ validation.py
tests/
โโโ fixtures/ # Saved sample CSES HTML pages used by unit tests
โโโ unit/ # Mocked-HTTP tests + opt-in live smoke tests
โโโ integration/ # In-process MCP server/tool-registration tests
Adding Tools, Resources, and Prompts
- Create a new file under
src/cses_mcp/tools/(orresources/,prompts/) usingfrom ..server import mcpand the relevant@mcp.tool/@mcp.resource/@mcp.promptdecorator. - Register the module by importing it in that package's
__init__.py.
โ๏ธ Configuration
Copy .env.example to .env and customize. See
src/cses_mcp/config/settings.py for all
available settings (server name, log level/file, debug mode, CSES base URL,
request timeout, PHPSESSID). Settings are read from real environment
variables first, .env second โ so an MCP client can also inject
PHPSESSID via its own "env" config block instead of a committed .env
file.
๐ Security
PHPSESSIDlives only in server-side config (.envor process env) and is never a tool argument โ no tool call can expose it in conversation history, and error responses redact it from logged tool args.- Session cookies/credentials must never be logged, printed in tool output, or transmitted anywhere except cses.fi.
- Requests to CSES should be sequential and rate-limited, not parallelized.
๐ License
This project is licensed under the MIT License โ see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file qhung_cses_mcp-0.1.0.tar.gz.
File metadata
- Download URL: qhung_cses_mcp-0.1.0.tar.gz
- Upload date:
- Size: 93.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc77ef8875e3fe03584ef6367a71784312058b3fa7ebf4d220934fdb87f5b7db
|
|
| MD5 |
2b4ee3cd0bb669a07a3a53252a51e04d
|
|
| BLAKE2b-256 |
a5c47ff070a87511982043cfc94d96041e3d51fc8966b5352dba6b4375269d75
|
Provenance
The following attestation bundles were made for qhung_cses_mcp-0.1.0.tar.gz:
Publisher:
release.yml on qhung23125005/CSES-MCP
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qhung_cses_mcp-0.1.0.tar.gz -
Subject digest:
dc77ef8875e3fe03584ef6367a71784312058b3fa7ebf4d220934fdb87f5b7db - Sigstore transparency entry: 2084565994
- Sigstore integration time:
-
Permalink:
qhung23125005/CSES-MCP@9dab1761355e861b7633e8999061451017c4de80 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/qhung23125005
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9dab1761355e861b7633e8999061451017c4de80 -
Trigger Event:
release
-
Statement type:
File details
Details for the file qhung_cses_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: qhung_cses_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 23.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2607869d5a8ae7a802834b00084e0b81e7605d01c7111a2f76d944593a7132a6
|
|
| MD5 |
268af9b2fc65b205210a360f4132349b
|
|
| BLAKE2b-256 |
fe2446a582f94f4cdcf63d0936fe4dd4e3159e605b4ab02e54998282eee68660
|
Provenance
The following attestation bundles were made for qhung_cses_mcp-0.1.0-py3-none-any.whl:
Publisher:
release.yml on qhung23125005/CSES-MCP
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qhung_cses_mcp-0.1.0-py3-none-any.whl -
Subject digest:
2607869d5a8ae7a802834b00084e0b81e7605d01c7111a2f76d944593a7132a6 - Sigstore transparency entry: 2084565998
- Sigstore integration time:
-
Permalink:
qhung23125005/CSES-MCP@9dab1761355e861b7633e8999061451017c4de80 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/qhung23125005
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9dab1761355e861b7633e8999061451017c4de80 -
Trigger Event:
release
-
Statement type: