cf-mcp-orange 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cf_mcp_orange-1.0.1/PKG-INFO +49 -0
- cf_mcp_orange-1.0.1/README.md +37 -0
- cf_mcp_orange-1.0.1/cf_mcp_orange.egg-info/PKG-INFO +49 -0
- cf_mcp_orange-1.0.1/cf_mcp_orange.egg-info/SOURCES.txt +9 -0
- cf_mcp_orange-1.0.1/cf_mcp_orange.egg-info/dependency_links.txt +1 -0
- cf_mcp_orange-1.0.1/cf_mcp_orange.egg-info/requires.txt +3 -0
- cf_mcp_orange-1.0.1/cf_mcp_orange.egg-info/top_level.txt +1 -0
- cf_mcp_orange-1.0.1/pyproject.toml +21 -0
- cf_mcp_orange-1.0.1/setup.cfg +4 -0
- cf_mcp_orange-1.0.1/src/cpmcp_oren/__init__.py +303 -0
- cf_mcp_orange-1.0.1/src/cpmcp_oren/__main__.py +4 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cf-mcp-orange
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: A complete Codeforces MCP toolset for AI agents
|
|
5
|
+
Author-email: supriya <supriya81205@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: fastmcp>=0.1.2
|
|
10
|
+
Requires-Dist: httpx>=0.27.0
|
|
11
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
12
|
+
|
|
13
|
+
# cpmcp-oren 🚀
|
|
14
|
+
|
|
15
|
+
[](https://pypi.org/project/cpmcp-oren/)
|
|
16
|
+
|
|
17
|
+
A **complete, all-in-one MCP (Model Context Protocol) server** for Codeforces.
|
|
18
|
+
Designed to supercharge AI coding assistants like **Cline** and **Claude Desktop**.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## ✨ Features (10 Powerful Tools)
|
|
23
|
+
|
|
24
|
+
1. **`get_user`** – Fetch a detailed profile (rating, max rating, last 5 contests, problem stats by difficulty, top 10 categories).
|
|
25
|
+
2. **`compare_user`** – Side-by-side comparison of two Codeforces users.
|
|
26
|
+
3. **`get_problemlist`** – Search the entire problemset by rating range, topic tags (`AND`/`OR`), and sorting.
|
|
27
|
+
4. **`get_problem`** – Retrieve the full problem statement, rating, and tags for any specific contest/problem ID.
|
|
28
|
+
5. **`get_practiceproblems`** – Identify a user's weakest core topics and recommend 3 targeted problems within `+300` rating.
|
|
29
|
+
6. **`get_random_practice`** – A completely random problem within `+/- 300` of a user's current rating.
|
|
30
|
+
7. **`get_upsolve`** – Analyzes a user's last 10 contests and recommends unsolved problems within their rating range.
|
|
31
|
+
8. **`get_status`** – Summarizes the last 1000 submissions (AC/WA/TLE counts) and the 20 most recent attempts.
|
|
32
|
+
9. **`get_catalog`** – Searches and fetches the latest official educational articles from the Codeforces Catalog.
|
|
33
|
+
10. **`submit_solution`** – (*Optional*) Submits code to a problem via the authenticated API.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 🛠️ Installation & Configuration (No Keys Required!)
|
|
38
|
+
|
|
39
|
+
You don't need to clone or manually install anything! Just add this to your `cline_mcp_settings.json` or `mcp.json`:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"cpmcp-oren": {
|
|
45
|
+
"command": "uvx",
|
|
46
|
+
"args": ["cpmcp-oren"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# cpmcp-oren 🚀
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/cpmcp-oren/)
|
|
4
|
+
|
|
5
|
+
A **complete, all-in-one MCP (Model Context Protocol) server** for Codeforces.
|
|
6
|
+
Designed to supercharge AI coding assistants like **Cline** and **Claude Desktop**.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## ✨ Features (10 Powerful Tools)
|
|
11
|
+
|
|
12
|
+
1. **`get_user`** – Fetch a detailed profile (rating, max rating, last 5 contests, problem stats by difficulty, top 10 categories).
|
|
13
|
+
2. **`compare_user`** – Side-by-side comparison of two Codeforces users.
|
|
14
|
+
3. **`get_problemlist`** – Search the entire problemset by rating range, topic tags (`AND`/`OR`), and sorting.
|
|
15
|
+
4. **`get_problem`** – Retrieve the full problem statement, rating, and tags for any specific contest/problem ID.
|
|
16
|
+
5. **`get_practiceproblems`** – Identify a user's weakest core topics and recommend 3 targeted problems within `+300` rating.
|
|
17
|
+
6. **`get_random_practice`** – A completely random problem within `+/- 300` of a user's current rating.
|
|
18
|
+
7. **`get_upsolve`** – Analyzes a user's last 10 contests and recommends unsolved problems within their rating range.
|
|
19
|
+
8. **`get_status`** – Summarizes the last 1000 submissions (AC/WA/TLE counts) and the 20 most recent attempts.
|
|
20
|
+
9. **`get_catalog`** – Searches and fetches the latest official educational articles from the Codeforces Catalog.
|
|
21
|
+
10. **`submit_solution`** – (*Optional*) Submits code to a problem via the authenticated API.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🛠️ Installation & Configuration (No Keys Required!)
|
|
26
|
+
|
|
27
|
+
You don't need to clone or manually install anything! Just add this to your `cline_mcp_settings.json` or `mcp.json`:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"cpmcp-oren": {
|
|
33
|
+
"command": "uvx",
|
|
34
|
+
"args": ["cpmcp-oren"]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cf-mcp-orange
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: A complete Codeforces MCP toolset for AI agents
|
|
5
|
+
Author-email: supriya <supriya81205@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: fastmcp>=0.1.2
|
|
10
|
+
Requires-Dist: httpx>=0.27.0
|
|
11
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
12
|
+
|
|
13
|
+
# cpmcp-oren 🚀
|
|
14
|
+
|
|
15
|
+
[](https://pypi.org/project/cpmcp-oren/)
|
|
16
|
+
|
|
17
|
+
A **complete, all-in-one MCP (Model Context Protocol) server** for Codeforces.
|
|
18
|
+
Designed to supercharge AI coding assistants like **Cline** and **Claude Desktop**.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## ✨ Features (10 Powerful Tools)
|
|
23
|
+
|
|
24
|
+
1. **`get_user`** – Fetch a detailed profile (rating, max rating, last 5 contests, problem stats by difficulty, top 10 categories).
|
|
25
|
+
2. **`compare_user`** – Side-by-side comparison of two Codeforces users.
|
|
26
|
+
3. **`get_problemlist`** – Search the entire problemset by rating range, topic tags (`AND`/`OR`), and sorting.
|
|
27
|
+
4. **`get_problem`** – Retrieve the full problem statement, rating, and tags for any specific contest/problem ID.
|
|
28
|
+
5. **`get_practiceproblems`** – Identify a user's weakest core topics and recommend 3 targeted problems within `+300` rating.
|
|
29
|
+
6. **`get_random_practice`** – A completely random problem within `+/- 300` of a user's current rating.
|
|
30
|
+
7. **`get_upsolve`** – Analyzes a user's last 10 contests and recommends unsolved problems within their rating range.
|
|
31
|
+
8. **`get_status`** – Summarizes the last 1000 submissions (AC/WA/TLE counts) and the 20 most recent attempts.
|
|
32
|
+
9. **`get_catalog`** – Searches and fetches the latest official educational articles from the Codeforces Catalog.
|
|
33
|
+
10. **`submit_solution`** – (*Optional*) Submits code to a problem via the authenticated API.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 🛠️ Installation & Configuration (No Keys Required!)
|
|
38
|
+
|
|
39
|
+
You don't need to clone or manually install anything! Just add this to your `cline_mcp_settings.json` or `mcp.json`:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"cpmcp-oren": {
|
|
45
|
+
"command": "uvx",
|
|
46
|
+
"args": ["cpmcp-oren"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
cf_mcp_orange.egg-info/PKG-INFO
|
|
4
|
+
cf_mcp_orange.egg-info/SOURCES.txt
|
|
5
|
+
cf_mcp_orange.egg-info/dependency_links.txt
|
|
6
|
+
cf_mcp_orange.egg-info/requires.txt
|
|
7
|
+
cf_mcp_orange.egg-info/top_level.txt
|
|
8
|
+
src/cpmcp_oren/__init__.py
|
|
9
|
+
src/cpmcp_oren/__main__.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cpmcp_oren
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cf-mcp-orange"
|
|
7
|
+
version = "1.0.1"
|
|
8
|
+
description = "A complete Codeforces MCP toolset for AI agents"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [{name = "supriya", email = "supriya81205@gmail.com"}]
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"fastmcp>=0.1.2",
|
|
15
|
+
"httpx>=0.27.0",
|
|
16
|
+
"beautifulsoup4>=4.12.0"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[tool.setuptools]
|
|
20
|
+
packages = ["cpmcp_oren"]
|
|
21
|
+
package-dir = {"cpmcp_oren" = "src/cpmcp_oren"}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
from fastmcp import FastMCP
|
|
2
|
+
|
|
3
|
+
# Import your tool logic from the 'tools' package (Relative imports)
|
|
4
|
+
from .tools.get_user import fetch_and_parse_user_data
|
|
5
|
+
from .tools.compare_user import compare_users_data
|
|
6
|
+
from .tools.get_problemlist import get_filtered_problems
|
|
7
|
+
from .tools.get_problem import get_problem_details
|
|
8
|
+
from .tools.get_practice import get_practice_plan
|
|
9
|
+
from .tools.get_random_problem import get_random_problem_suggestion
|
|
10
|
+
from .tools.get_upsolve import get_upsolve_suggestions
|
|
11
|
+
from .tools.get_status import get_user_submission_status
|
|
12
|
+
|
|
13
|
+
# Initialize the MCP Server
|
|
14
|
+
mcp = FastMCP("cpmcp-oren")
|
|
15
|
+
|
|
16
|
+
# ==========================================
|
|
17
|
+
# TOOL 1: Get Single User Profile
|
|
18
|
+
# ==========================================
|
|
19
|
+
@mcp.tool()
|
|
20
|
+
async def get_user(username: str) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Fetches a complete Codeforces profile in one call, returning:
|
|
23
|
+
- Current rating, max rating, title, and max title.
|
|
24
|
+
- Number of total contests given.
|
|
25
|
+
- Recent 5 contest rating changes (delta).
|
|
26
|
+
- Total distinct problems solved.
|
|
27
|
+
- Breakdown of solved problems by difficulty.
|
|
28
|
+
- Top 10 problem categories solved.
|
|
29
|
+
"""
|
|
30
|
+
data = await fetch_and_parse_user_data(username)
|
|
31
|
+
|
|
32
|
+
if data["status"] == "error":
|
|
33
|
+
return f"❌ Error fetching user: {data['message']}"
|
|
34
|
+
|
|
35
|
+
output = f"**🧑💻 User Profile: {username}**\n\n"
|
|
36
|
+
|
|
37
|
+
output += f"**🏆 Ratings & Titles:**\n"
|
|
38
|
+
output += f"- Current Rating: **{data['rating']}**\n"
|
|
39
|
+
output += f"- Max Rating: **{data['max_rating']}**\n"
|
|
40
|
+
output += f"- Current Title: {data['title']}\n"
|
|
41
|
+
output += f"- Max Title: {data['max_title']}\n\n"
|
|
42
|
+
|
|
43
|
+
output += f"**🏁 Contest History:**\n"
|
|
44
|
+
output += f"- Total Rated Contests Given: **{data['total_contests']}**\n"
|
|
45
|
+
|
|
46
|
+
if data['recent_5']:
|
|
47
|
+
output += "- **Last 5 Rating Changes:**\n"
|
|
48
|
+
for c in data['recent_5']:
|
|
49
|
+
sign = "+" if c['newRating'] > c['oldRating'] else ""
|
|
50
|
+
output += f" - {c['contestName']}: Rank {c['rank']} ({sign}{c['newRating'] - c['oldRating']}, {c['oldRating']} ➜ {c['newRating']})\n"
|
|
51
|
+
else:
|
|
52
|
+
output += "- No rated contests found.\n"
|
|
53
|
+
output += "\n"
|
|
54
|
+
|
|
55
|
+
output += f"**✅ Problem Solving Stats:**\n"
|
|
56
|
+
output += f"- Total Distinct Problems Solved: **{data['total_solved']}**\n"
|
|
57
|
+
|
|
58
|
+
if data['difficulty_stats']:
|
|
59
|
+
output += "- **Solved by Difficulty:**\n"
|
|
60
|
+
sorted_diff = sorted(data['difficulty_stats'].items(), key=lambda x: int(x[0]) if x[0] != 'Unrated' else 0)
|
|
61
|
+
for diff, count in sorted_diff:
|
|
62
|
+
output += f" - Rating {diff}: {count} problems\n"
|
|
63
|
+
else:
|
|
64
|
+
output += "- No solved problems found by difficulty.\n"
|
|
65
|
+
|
|
66
|
+
if data['top_10_categories']:
|
|
67
|
+
output += "- **Top 10 Categories:**\n"
|
|
68
|
+
for tag, count in data['top_10_categories']:
|
|
69
|
+
output += f" - {tag}: {count} problems\n"
|
|
70
|
+
else:
|
|
71
|
+
output += "- No categories found.\n"
|
|
72
|
+
|
|
73
|
+
return output
|
|
74
|
+
|
|
75
|
+
# ==========================================
|
|
76
|
+
# TOOL 2: Compare Two Users
|
|
77
|
+
# ==========================================
|
|
78
|
+
@mcp.tool()
|
|
79
|
+
async def compare_user(user_a: str, user_b: str) -> str:
|
|
80
|
+
"""
|
|
81
|
+
Compare two Codeforces users side-by-side.
|
|
82
|
+
Returns a detailed comparison of ratings, titles, contest count,
|
|
83
|
+
and problem solving breakdown (difficulty & category).
|
|
84
|
+
"""
|
|
85
|
+
return await compare_users_data(user_a, user_b)
|
|
86
|
+
|
|
87
|
+
# ==========================================
|
|
88
|
+
# TOOL 3: Search Problem List
|
|
89
|
+
# ==========================================
|
|
90
|
+
@mcp.tool()
|
|
91
|
+
async def get_problemlist(
|
|
92
|
+
min_rating: int = None,
|
|
93
|
+
max_rating: int = None,
|
|
94
|
+
topics: list = None,
|
|
95
|
+
sort_by: str = "recent",
|
|
96
|
+
match_type: str = "OR"
|
|
97
|
+
) -> str:
|
|
98
|
+
"""
|
|
99
|
+
Search the entire Codeforces problemset and return up to 10 problems.
|
|
100
|
+
|
|
101
|
+
Parameters:
|
|
102
|
+
- min_rating: Minimum difficulty rating (e.g., 800)
|
|
103
|
+
- max_rating: Maximum difficulty rating (e.g., 2400)
|
|
104
|
+
- topics: List of tags to filter by (e.g., ["dp", "greedy"])
|
|
105
|
+
- sort_by: "recent" (newest first) or "oldest" (oldest first)
|
|
106
|
+
- match_type: "OR" (match any topic) or "AND" (must match ALL topics)
|
|
107
|
+
|
|
108
|
+
Returns 10 problems with names, ratings, tags, and direct links.
|
|
109
|
+
"""
|
|
110
|
+
try:
|
|
111
|
+
problems = await get_filtered_problems(
|
|
112
|
+
min_rating=min_rating,
|
|
113
|
+
max_rating=max_rating,
|
|
114
|
+
topics=topics,
|
|
115
|
+
sort_by=sort_by,
|
|
116
|
+
match_type=match_type
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
if not problems:
|
|
120
|
+
return "No problems found matching your criteria. Try broadening your filters."
|
|
121
|
+
|
|
122
|
+
output = f"**💻 Found {len(problems)} Problems:**\n\n"
|
|
123
|
+
|
|
124
|
+
for p in problems:
|
|
125
|
+
name = p.get('name', 'Unknown Problem')
|
|
126
|
+
contest_id = p['contestId']
|
|
127
|
+
index = p['index']
|
|
128
|
+
rating = p.get('rating', 'Unrated')
|
|
129
|
+
tags = ", ".join(p.get('tags', [])[:5])
|
|
130
|
+
url = f"https://codeforces.com/problemset/problem/{contest_id}/{index}"
|
|
131
|
+
|
|
132
|
+
output += f"### [{name}]({url})\n"
|
|
133
|
+
output += f"- **Rating**: {rating}\n"
|
|
134
|
+
output += f"- **Tags**: {tags}\n\n"
|
|
135
|
+
|
|
136
|
+
return output
|
|
137
|
+
|
|
138
|
+
except Exception as e:
|
|
139
|
+
return f"❌ Error fetching problem list: {str(e)}"
|
|
140
|
+
|
|
141
|
+
# ==========================================
|
|
142
|
+
# TOOL 4: Get Full Problem Statement
|
|
143
|
+
# ==========================================
|
|
144
|
+
@mcp.tool()
|
|
145
|
+
async def get_problem(contest_id: int, index: str) -> str:
|
|
146
|
+
"""
|
|
147
|
+
Fetch the full problem statement, rating, and topics for a specific Codeforces problem.
|
|
148
|
+
Provide the contest ID and the problem index (e.g., contest_id=4, index='A').
|
|
149
|
+
"""
|
|
150
|
+
try:
|
|
151
|
+
data = await get_problem_details(contest_id, index)
|
|
152
|
+
|
|
153
|
+
if "error" in data:
|
|
154
|
+
return f"❌ {data['error']}"
|
|
155
|
+
|
|
156
|
+
output = f"# {data['title']}\n\n"
|
|
157
|
+
output += f"**Link:** [View on Codeforces]({data['url']})\n\n"
|
|
158
|
+
|
|
159
|
+
# The dropdown you requested!
|
|
160
|
+
output += f"📊 **Rating:** `{data['rating']}` | **Topics:** `{', '.join(data['topics'])}`\n\n"
|
|
161
|
+
output += f"---\n\n"
|
|
162
|
+
|
|
163
|
+
output += "**Problem Statement:**\n\n"
|
|
164
|
+
output += data['statement']
|
|
165
|
+
|
|
166
|
+
return output
|
|
167
|
+
except Exception as e:
|
|
168
|
+
return f"❌ Error fetching problem: {str(e)}"
|
|
169
|
+
|
|
170
|
+
# ==========================================
|
|
171
|
+
# TOOL 5: Get Practice Problems
|
|
172
|
+
# ==========================================
|
|
173
|
+
@mcp.tool()
|
|
174
|
+
async def get_practiceproblems(username: str) -> str:
|
|
175
|
+
"""
|
|
176
|
+
Analyzes a user's solved problems to identify their weakest topics.
|
|
177
|
+
Recommends up to 3 practice problems within +300 rating of their current level.
|
|
178
|
+
"""
|
|
179
|
+
result = await get_practice_plan(username)
|
|
180
|
+
|
|
181
|
+
if result["status"] == "error":
|
|
182
|
+
return f"❌ Error generating practice plan: {result['message']}"
|
|
183
|
+
|
|
184
|
+
output = f"**🎯 Practice Plan for `{username}`**\n\n"
|
|
185
|
+
output += f"Current Rating: **{result['rating']}**\n"
|
|
186
|
+
output += f"Identified Weak Topic(s): **{', '.join(result['weak_topics'])}**\n"
|
|
187
|
+
output += f"Target Rating Range: **{result['rating']} - {result['rating'] + 300}**\n\n"
|
|
188
|
+
|
|
189
|
+
if not result["problems"]:
|
|
190
|
+
output += "No suitable practice problems found in the target rating range. Try increasing your range or broadening topics."
|
|
191
|
+
return output
|
|
192
|
+
|
|
193
|
+
output += "**💻 Recommended Problems to Practice:**\n"
|
|
194
|
+
for idx, p in enumerate(result["problems"], 1):
|
|
195
|
+
name = p.get('name', 'Unknown Problem')
|
|
196
|
+
contest_id = p['contestId']
|
|
197
|
+
index = p['index']
|
|
198
|
+
rating = p.get('rating', 'Unrated')
|
|
199
|
+
tags = ", ".join(p.get('tags', [])[:5])
|
|
200
|
+
url = f"https://codeforces.com/problemset/problem/{contest_id}/{index}"
|
|
201
|
+
|
|
202
|
+
output += f"\n**{idx}. [{name}]({url})**\n"
|
|
203
|
+
output += f" - Rating: {rating}\n"
|
|
204
|
+
output += f" - Topics: {tags}\n"
|
|
205
|
+
|
|
206
|
+
return output
|
|
207
|
+
|
|
208
|
+
# ==========================================
|
|
209
|
+
# TOOL 7: Get Random Practice Problem
|
|
210
|
+
# ==========================================
|
|
211
|
+
@mcp.tool()
|
|
212
|
+
async def get_random_practice(username: str) -> str:
|
|
213
|
+
"""
|
|
214
|
+
Ignore weaknesses and recommend a single, completely random Codeforces problem
|
|
215
|
+
within +/- 300 rating of the user's current rating.
|
|
216
|
+
Great for a fresh, challenging surprise!
|
|
217
|
+
"""
|
|
218
|
+
result = await get_random_problem_suggestion(username)
|
|
219
|
+
|
|
220
|
+
if "error" in result:
|
|
221
|
+
return f"❌ Error generating random problem: {result['error']}"
|
|
222
|
+
|
|
223
|
+
p = result
|
|
224
|
+
name = p.get('name', 'Unknown Problem')
|
|
225
|
+
contest_id = p['contestId']
|
|
226
|
+
index = p['index']
|
|
227
|
+
rating = p.get('rating', 'Unrated')
|
|
228
|
+
tags = ", ".join(p.get('tags', [])[:5])
|
|
229
|
+
url = f"https://codeforces.com/problemset/problem/{contest_id}/{index}"
|
|
230
|
+
|
|
231
|
+
output = f"**🎲 Random Practice Problem for `{username}`**\n\n"
|
|
232
|
+
output += f"**[{name}]({url})**\n"
|
|
233
|
+
output += f"- **Rating**: {rating}\n"
|
|
234
|
+
output += f"- **Tags**: {tags}\n"
|
|
235
|
+
output += f"- *(Generated from range: Current Rating ± 300)*\n"
|
|
236
|
+
|
|
237
|
+
return output
|
|
238
|
+
|
|
239
|
+
# ==========================================
|
|
240
|
+
# TOOL 8: Get Upsolve Problems
|
|
241
|
+
# ==========================================
|
|
242
|
+
@mcp.tool()
|
|
243
|
+
async def get_upsolve(username: str) -> str:
|
|
244
|
+
"""
|
|
245
|
+
Analyze the user's last 10 rated Codeforces contests.
|
|
246
|
+
Recommends up to 2 unsolved problems per contest if:
|
|
247
|
+
- The problem is unrated, OR
|
|
248
|
+
- The problem is rated within +/- 300 of the user's current rating.
|
|
249
|
+
This is perfect for targeted practice to improve rank!
|
|
250
|
+
"""
|
|
251
|
+
result = await get_upsolve_suggestions(username)
|
|
252
|
+
|
|
253
|
+
if result["status"] == "error":
|
|
254
|
+
return f"❌ Error generating upsolve list: {result['message']}"
|
|
255
|
+
|
|
256
|
+
if not result["suggestions"]:
|
|
257
|
+
return f"🎉 No upsolve problems found for `{username}` within +/- 300 of {result['rating']} rating. Either you solved them all, or they are out of your range!"
|
|
258
|
+
|
|
259
|
+
output = f"**📝 Upsolve Plan for `{username}`**\n"
|
|
260
|
+
output += f"(Based on last 10 rated contests. Current Rating: **{result['rating']}**)\n\n"
|
|
261
|
+
|
|
262
|
+
for item in result["suggestions"]:
|
|
263
|
+
p = item["problem"]
|
|
264
|
+
name = p.get('name', 'Unknown')
|
|
265
|
+
contest_id = p['contestId']
|
|
266
|
+
index = p['index']
|
|
267
|
+
rating = p.get('rating', 'Unrated')
|
|
268
|
+
tags = ", ".join(p.get('tags', [])[:3]) # Show top 3 tags
|
|
269
|
+
url = f"https://codeforces.com/problemset/problem/{contest_id}/{index}"
|
|
270
|
+
|
|
271
|
+
output += f"### [{name}]({url})\n"
|
|
272
|
+
output += f"- **Contest:** {item['contest_name']}\n"
|
|
273
|
+
output += f"- **Rating:** {rating}\n"
|
|
274
|
+
output += f"- **Tags:** {tags}\n"
|
|
275
|
+
output += f"- **Why upsolve?** *{item['reason']}*\n\n"
|
|
276
|
+
|
|
277
|
+
return output
|
|
278
|
+
|
|
279
|
+
# ==========================================
|
|
280
|
+
# TOOL 10: Get User Submission Status
|
|
281
|
+
# ==========================================
|
|
282
|
+
@mcp.tool()
|
|
283
|
+
async def get_status(username: str) -> str:
|
|
284
|
+
"""
|
|
285
|
+
Fetch the last 1000 submissions of a user. Provides a summary of their
|
|
286
|
+
AC/WA/TLE count, and details of their 20 most recent submissions.
|
|
287
|
+
Great for debugging how a user is performing on their current attempts.
|
|
288
|
+
"""
|
|
289
|
+
result = await get_user_submission_status(username)
|
|
290
|
+
|
|
291
|
+
if result["status"] == "error":
|
|
292
|
+
return f"❌ Error fetching submissions: {result['message']}"
|
|
293
|
+
|
|
294
|
+
output = f"**📊 Submission Status for `{username}`**\n\n"
|
|
295
|
+
output += result["summary"] + "\n"
|
|
296
|
+
|
|
297
|
+
output += "**🕒 Most Recent 20 Submissions:**\n"
|
|
298
|
+
for sub in result["recent_submissions"]:
|
|
299
|
+
output += f"- **[`{sub['prob_name']}`]({sub['url']})**\n"
|
|
300
|
+
output += f" - Verdict: {sub['verdict']}\n"
|
|
301
|
+
output += f" - Time: {sub['time']} | Memory: {sub['memory']}\n"
|
|
302
|
+
|
|
303
|
+
return output
|