@iflow-mcp/leetcode-mcp-server
Version:
MCP Server for LeetCode API (supports leetcode.com and leetcode.cn)
393 lines • 16.4 kB
JavaScript
import { z } from "zod";
import { PROGRAMMING_LANGS } from "../../common/constants.js";
import { ToolRegistry } from "./tool-registry.js";
/**
* User tool registry class that handles registration of LeetCode user-related tools.
* This class manages tools for accessing user profiles, submissions, and progress data.
*/
export class UserToolRegistry extends ToolRegistry {
registerCommon() {
// User profile tool
this.server.tool("get_user_profile", "Retrieves profile information about a LeetCode user, including user stats, solved problems, and profile details", {
username: z
.string()
.describe("LeetCode username to retrieve profile information for")
}, async ({ username }) => {
const data = await this.leetcodeService.fetchUserProfile(username);
return {
content: [
{
type: "text",
text: JSON.stringify({
username: username,
profile: data
})
}
]
};
});
}
registerGlobal() {
// Recent submissions tool (Global-specific)
this.server.tool("get_recent_submissions", "Retrieves a user's recent submissions on LeetCode Global, including both accepted and failed submissions with detailed metadata", {
username: z
.string()
.describe("LeetCode username to retrieve recent submissions for"),
limit: z
.number()
.optional()
.default(10)
.describe("Maximum number of submissions to return (optional, defaults to server-defined limit)")
}, async ({ username, limit }) => {
try {
const data = await this.leetcodeService.fetchUserRecentSubmissions(username, limit);
return {
content: [
{
type: "text",
text: JSON.stringify({
username,
submissions: data
})
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Failed to fetch recent submissions",
message: error.message
})
}
]
};
}
});
// Recent accepted submissions tool (Global-specific)
this.server.tool("get_recent_ac_submissions", "Retrieves a user's recent accepted (AC) submissions on LeetCode Global, focusing only on successfully completed problems", {
username: z
.string()
.describe("LeetCode username to retrieve recent accepted submissions for"),
limit: z
.number()
.optional()
.default(10)
.describe("Maximum number of accepted submissions to return (optional, defaults to server-defined limit)")
}, async ({ username, limit }) => {
try {
const data = await this.leetcodeService.fetchUserRecentACSubmissions(username, limit);
return {
content: [
{
type: "text",
text: JSON.stringify({
username,
submissions: data
})
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Failed to fetch recent submissions",
message: error.message
})
}
]
};
}
});
}
registerChina() {
// User recent AC submissions tool (CN-specific)
this.server.tool("get_recent_ac_submissions", "Retrieves a user's recent accepted (AC) submissions on LeetCode China, with details about each successfully solved problem", {
username: z
.string()
.describe("LeetCode China username to retrieve recent accepted submissions for"),
limit: z
.number()
.optional()
.default(10)
.describe("Maximum number of accepted submissions to return (optional, defaults to server-defined limit)")
}, async ({ username, limit }) => {
try {
const data = await this.leetcodeService.fetchUserRecentACSubmissions(username, limit);
return {
content: [
{
type: "text",
text: JSON.stringify({
username,
acSubmissions: data
})
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Failed to fetch recent AC submissions",
message: error.message
})
}
]
};
}
});
}
/**
* Registers common tools that require authentication and are available on both Global and CN platforms.
*/
registerAuthenticatedCommon() {
// User status tool (requires authentication)
this.server.tool("get_user_status", "Retrieves the current user's status on LeetCode, including login status, premium membership details, and user information (requires authentication)", async () => {
try {
const status = await this.leetcodeService.fetchUserStatus();
return {
content: [
{
type: "text",
text: JSON.stringify({
status: status
})
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({ error: error.message })
}
]
};
}
});
// Submission detail tool (requires authentication)
this.server.tool("get_problem_submission_report", "Retrieves detailed information about a specific LeetCode submission by its ID, including source code, runtime stats, and test results (requires authentication)", {
id: z
.number()
.describe("The numerical submission ID to retrieve detailed information for")
}, async ({ id }) => {
try {
const submissionDetail = await this.leetcodeService.fetchUserSubmissionDetail(id);
return {
content: [
{
type: "text",
text: JSON.stringify({
submissionId: id,
detail: submissionDetail
})
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({ error: error.message })
}
]
};
}
});
// User progress questions tool (requires authentication)
this.server.tool("get_problem_progress", "Retrieves the current user's problem-solving status with filtering options, including detailed solution history for attempted or solved questions (requires authentication)", {
offset: z
.number()
.default(0)
.describe("The number of questions to skip for pagination purposes"),
limit: z
.number()
.default(100)
.describe("The maximum number of questions to return in a single request"),
questionStatus: z
.enum(["ATTEMPTED", "SOLVED"])
.optional()
.describe("Filter by question status: 'ATTEMPTED' for questions that have been tried but not necessarily solved, 'SOLVED' for questions that have been successfully completed"),
difficulty: z
.array(z.string())
.optional()
.describe("Filter by difficulty levels as an array (e.g., ['EASY', 'MEDIUM', 'HARD']); if not provided, questions of all difficulty levels will be returned")
}, async ({ offset, limit, questionStatus, difficulty }) => {
try {
const filters = {
offset,
limit,
questionStatus,
difficulty
};
const progressQuestions = await this.leetcodeService.fetchUserProgressQuestionList(filters);
return {
content: [
{
type: "text",
text: JSON.stringify({
filters,
questions: progressQuestions
})
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Failed to fetch user progress questions",
message: error.message
})
}
]
};
}
});
}
/**
* Registers tools specific to the Global LeetCode site that require authentication.
*/
registerAuthenticatedGlobal() {
// Global user submissions tool (requires authentication)
this.server.tool("get_all_submissions", "Retrieves a paginated list of the current user's submissions for a specific problem or all problems on LeetCode Global, with detailed submission metadata (requires authentication)", {
limit: z
.number()
.default(20)
.describe("Maximum number of submissions to return per page (typically defaults to 20 if not specified)"),
offset: z
.number()
.default(0)
.describe("Number of submissions to skip for pagination purposes"),
questionSlug: z
.string()
.optional()
.describe("Optional problem identifier (slug) to filter submissions for a specific problem (e.g., 'two-sum'); if omitted, returns submissions across all problems")
}, async ({ questionSlug, limit, offset }) => {
try {
const submissions = await this.leetcodeService.fetchUserAllSubmissions({
offset,
limit,
questionSlug
});
return {
content: [
{
type: "text",
text: JSON.stringify({
problem: questionSlug,
submissions: submissions
})
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Failed to fetch user submissions",
message: error.message
})
}
]
};
}
});
}
/**
* Registers tools specific to the China LeetCode site that require authentication.
*/
registerAuthenticatedChina() {
// China user submissions tool (requires authentication, enhanced version with more parameters)
this.server.tool("get_all_submissions", "Retrieves a list of the current user's submissions on LeetCode China with extensive filtering options, including pagination support via lastKey parameter (requires authentication)", {
limit: z
.number()
.default(20)
.describe("Maximum number of submissions to return per page (typically defaults to 20 if not specified)"),
offset: z
.number()
.default(0)
.describe("Number of submissions to skip for pagination purposes"),
questionSlug: z
.string()
.optional()
.describe("Optional problem identifier (slug) to filter submissions for a specific problem (e.g., 'two-sum'); if omitted, returns submissions across all problems"),
lang: z
.enum(PROGRAMMING_LANGS)
.optional()
.describe("Programming language filter to show only submissions in a specific language (e.g., 'python3', 'java', 'cpp')"),
status: z
.enum(["AC", "WA"])
.optional()
.describe("Submission status filter (e.g., 'AC' for Accepted, 'WA' for Wrong Answer) to show only submissions with that status; if omitted, returns all submissions"),
lastKey: z
.string()
.optional()
.describe("Pagination token from a previous request used to retrieve the next page of results")
}, async ({ questionSlug, limit, offset, lang, status, lastKey }) => {
try {
const submissions = await this.leetcodeService.fetchUserAllSubmissions({
offset,
limit,
questionSlug,
lang,
status,
lastKey
});
return {
content: [
{
type: "text",
text: JSON.stringify(submissions)
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Failed to fetch user submissions",
message: error.message
})
}
]
};
}
});
}
}
/**
* Registers all user-related tools with the MCP server.
*
* @param server - The MCP server instance to register tools with
* @param leetcodeService - The LeetCode service implementation to use for API calls
*/
export function registerUserTools(server, leetcodeService) {
const registry = new UserToolRegistry(server, leetcodeService);
registry.registerTools();
}
//# sourceMappingURL=user-tools.js.map