|
|
import { z } from "zod"; |
|
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; |
|
|
import { safeToolHandler } from "./base"; |
|
|
import * as userService from "../../leetcode/services/user"; |
|
|
import * as contestService from "../../leetcode/services/contest"; |
|
|
import * as problemService from "../../leetcode/services/problem"; |
|
|
import * as discussionService from "../../leetcode/services/discussion"; |
|
|
|
|
|
export function register(mcp: McpServer): void { |
|
|
|
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_rating", |
|
|
"Fetches the LeetCode contest rating for a user, including their global ranking, attended contests count, and rating percentile.", |
|
|
{ username: z.string().describe("The LeetCode username to look up") }, |
|
|
safeToolHandler(({ username }) => userService.getUserRating(username)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_profile", |
|
|
"Fetches the complete LeetCode profile for a user, including basic info, company, school, skills, and social links.", |
|
|
{ username: z.string().describe("The LeetCode username to look up") }, |
|
|
safeToolHandler(({ username }) => userService.getUserProfile(username)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_details", |
|
|
"Fetches detailed user information from LeetCode including profile data, submission stats, and problem solving summary.", |
|
|
{ username: z.string().describe("The LeetCode username to look up") }, |
|
|
safeToolHandler(({ username }) => userService.getUserDetails(username)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_badges", |
|
|
"Fetches all badges earned by a LeetCode user, including annual badges and special achievements.", |
|
|
{ username: z.string().describe("The LeetCode username to look up") }, |
|
|
safeToolHandler(({ username }) => userService.getUserBadges(username)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_solved", |
|
|
"Fetches the solved problems count for a LeetCode user, broken down by difficulty (Easy, Medium, Hard).", |
|
|
{ username: z.string().describe("The LeetCode username to look up") }, |
|
|
safeToolHandler(({ username }) => userService.getUserSolved(username)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_contest_ranking", |
|
|
"Fetches detailed contest ranking information for a LeetCode user, including rating, ranking, and top percentage.", |
|
|
{ username: z.string().describe("The LeetCode username to look up") }, |
|
|
safeToolHandler(({ username }) => userService.getUserContest(username)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_contest_history", |
|
|
"Fetches the complete contest participation history for a LeetCode user, including each contest's ranking and rating changes.", |
|
|
{ username: z.string().describe("The LeetCode username to look up") }, |
|
|
safeToolHandler(({ username }) => userService.getUserContestHistory(username)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_submissions", |
|
|
"Fetches recent submissions for a LeetCode user, including problem title, status, language, and timestamp.", |
|
|
{ |
|
|
username: z.string().describe("The LeetCode username to look up"), |
|
|
limit: z.number().optional().default(20).describe("Maximum number of submissions to return (default: 20)") |
|
|
}, |
|
|
safeToolHandler(({ username, limit }) => userService.getUserSubmission(username, limit)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_ac_submissions", |
|
|
"Fetches recent accepted (AC) submissions for a LeetCode user, filtered to only show successful solutions.", |
|
|
{ |
|
|
username: z.string().describe("The LeetCode username to look up"), |
|
|
limit: z.number().optional().default(20).describe("Maximum number of AC submissions to return (default: 20)") |
|
|
}, |
|
|
safeToolHandler(({ username, limit }) => userService.getUserAcSubmission(username, limit)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_calendar", |
|
|
"Fetches the submission calendar/heatmap data for a LeetCode user for a specific year, showing daily activity.", |
|
|
{ |
|
|
username: z.string().describe("The LeetCode username to look up"), |
|
|
year: z.number().describe("The year to fetch calendar data for (e.g., 2024)") |
|
|
}, |
|
|
safeToolHandler(({ username, year }) => userService.getUserCalendar(username, year)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_skill_stats", |
|
|
"Fetches the skill/tag statistics for a LeetCode user, showing proficiency in different problem categories like DP, Graph, etc.", |
|
|
{ username: z.string().describe("The LeetCode username to look up") }, |
|
|
safeToolHandler(({ username }) => userService.getUserSkill(username)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_languages", |
|
|
"Fetches the programming language statistics for a LeetCode user, showing problems solved per language.", |
|
|
{ username: z.string().describe("The LeetCode username to look up") }, |
|
|
safeToolHandler(({ username }) => userService.getUserLanguage(username)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_progress", |
|
|
"Fetches the overall problem-solving progress for a LeetCode user across all categories.", |
|
|
{ username: z.string().describe("The LeetCode username to look up") }, |
|
|
safeToolHandler(({ username }) => userService.getUserProgress(username)) |
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_contest_ranking_info", |
|
|
"Fetches detailed contest ranking information including rating, global rank, and percentile for a user.", |
|
|
{ username: z.string().describe("The LeetCode username to look up") }, |
|
|
safeToolHandler(({ username }) => contestService.getContestRankingInfo(username)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_contest_histogram", |
|
|
"Fetches the contest rating distribution histogram showing how many users are at each rating level.", |
|
|
{}, |
|
|
safeToolHandler(() => contestService.getContestHistogram()) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_all_contests", |
|
|
"Fetches a list of all LeetCode contests including past weekly and biweekly contests with their details.", |
|
|
{}, |
|
|
safeToolHandler(() => contestService.getAllContests()) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_upcoming_contests", |
|
|
"Fetches a list of upcoming LeetCode contests that haven't started yet.", |
|
|
{}, |
|
|
safeToolHandler(() => contestService.getUpcomingContests()) |
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_daily_problem", |
|
|
"Fetches today's LeetCode Daily Challenge problem with its details, difficulty, and constraints.", |
|
|
{ |
|
|
raw: z.boolean().optional().default(false).describe("Return raw API response if true (default: false)") |
|
|
}, |
|
|
safeToolHandler(({ raw }) => problemService.getDailyProblem(raw)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_problem", |
|
|
"Fetches detailed information about a specific LeetCode problem by its title slug (e.g., 'two-sum').", |
|
|
{ |
|
|
titleSlug: z.string().describe("The URL slug of the problem (e.g., 'two-sum', 'add-two-numbers')"), |
|
|
raw: z.boolean().optional().default(false).describe("Return raw API response if true (default: false)") |
|
|
}, |
|
|
safeToolHandler(({ titleSlug, raw }) => problemService.getSelectProblem(titleSlug, raw)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_problems", |
|
|
"Fetches a filtered list of LeetCode problems with pagination, difficulty filter, and category support.", |
|
|
{ |
|
|
limit: z.number().optional().default(20).describe("Number of problems to return"), |
|
|
skip: z.number().optional().default(0).describe("Number of problems to skip (for pagination)"), |
|
|
categorySlug: z.string().optional().describe("Category slug to filter by (e.g., 'algorithms', 'database')"), |
|
|
filters: z.object({ |
|
|
difficulty: z.string().optional().describe("Difficulty filter: 'EASY', 'MEDIUM', or 'HARD'"), |
|
|
tags: z.array(z.string()).optional().describe("Array of tag slugs to filter by") |
|
|
}).optional().describe("Optional filters object") |
|
|
}, |
|
|
safeToolHandler((params) => problemService.getProblems(params)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_official_solution", |
|
|
"Fetches the official LeetCode solution/editorial for a specific problem.", |
|
|
{ |
|
|
titleSlug: z.string().describe("The URL slug of the problem (e.g., 'two-sum')") |
|
|
}, |
|
|
safeToolHandler(({ titleSlug }) => problemService.getOfficialSolution(titleSlug)) |
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_trending_discussions", |
|
|
"Fetches the top trending discussions from LeetCode's discussion forum.", |
|
|
{ |
|
|
first: z.number().optional().default(10).describe("Number of trending discussions to return (default: 10)") |
|
|
}, |
|
|
safeToolHandler(({ first }) => discussionService.getTrendingDiscuss(first)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_discussion_topic", |
|
|
"Fetches a specific discussion topic by its ID, including the full content and metadata.", |
|
|
{ |
|
|
topicId: z.number().describe("The numeric ID of the discussion topic") |
|
|
}, |
|
|
safeToolHandler(({ topicId }) => discussionService.getDiscussTopic(topicId)) |
|
|
); |
|
|
|
|
|
|
|
|
mcp.tool( |
|
|
"leetcode_get_discussion_comments", |
|
|
"Fetches comments/replies on a specific LeetCode discussion topic.", |
|
|
{ |
|
|
topicId: z.number().describe("The numeric ID of the discussion topic"), |
|
|
orderBy: z.string().optional().default("hot").describe("Order by: 'hot', 'newest', or 'oldest'"), |
|
|
pageNo: z.number().optional().default(1).describe("Page number for pagination"), |
|
|
numPerPage: z.number().optional().default(10).describe("Number of comments per page") |
|
|
}, |
|
|
safeToolHandler((params) => discussionService.getDiscussComments(params)) |
|
|
); |
|
|
} |
|
|
|