@iflow-mcp/leetcode-mcp-server
Version:
MCP Server for LeetCode API (supports leetcode.com and leetcode.cn)
226 lines • 11.4 kB
JavaScript
import { z } from "zod";
import { ToolRegistry } from "./tool-registry.js";
/**
* Solution tool registry class that handles registration of LeetCode solution-related tools.
* This class manages tools for accessing solutions, filtering solutions, and reading solution details.
*/
export class SolutionToolRegistry extends ToolRegistry {
registerGlobal() {
// Problem solutions listing tool (Global-specific)
this.server.tool("list_problem_solutions", "Retrieves a list of community solutions for a specific LeetCode problem, including only metadata like topicId. To view the full content of a solution, use the 'get_problem_solution' tool with the topicId returned by this tool.", {
questionSlug: z
.string()
.describe("The URL slug/identifier of the problem to retrieve solutions for (e.g., 'two-sum', 'add-two-numbers'). This is the same string that appears in the LeetCode problem URL after '/problems/'"),
limit: z
.number()
.optional()
.default(10)
.describe("Maximum number of solutions to return per request. Used for pagination and controlling response size. Default is 20 if not specified. Must be a positive integer."),
skip: z
.number()
.optional()
.describe("Number of solutions to skip before starting to collect results. Used in conjunction with 'limit' for implementing pagination. Default is 0 if not specified. Must be a non-negative integer."),
orderBy: z
.enum(["HOT", " MOST_RECENT", "MOST_VOTES"])
.default("HOT")
.optional()
.describe("Sorting criteria for the returned solutions. 'DEFAULT' sorts by LeetCode's default algorithm (typically a combination of recency and popularity), 'MOST_VOTES' sorts by the number of upvotes (highest first), and 'MOST_RECENT' sorts by publication date (newest first)."),
userInput: z
.string()
.optional()
.describe("Search term to filter solutions by title, content, or author name. Case insensitive. Useful for finding specific approaches or algorithms mentioned in solutions."),
tagSlugs: z
.array(z.string())
.optional()
.default([])
.describe("Array of tag identifiers to filter solutions by programming languages (e.g., 'python', 'java') or problem algorithm/data-structure tags (e.g., 'dynamic-programming', 'recursion'). Only solutions tagged with at least one of the specified tags will be returned.")
}, async ({ questionSlug, limit, skip, orderBy, userInput, tagSlugs }) => {
try {
const options = {
limit,
skip,
orderBy,
userInput,
tagSlugs
};
const data = await this.leetcodeService.fetchQuestionSolutionArticles(questionSlug, options);
return {
content: [
{
type: "text",
text: JSON.stringify({
questionSlug,
solutionArticles: data
})
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Failed to fetch solutions",
message: error.message
})
}
]
};
}
});
// Solution article detail tool (Global-specific)
this.server.tool("get_problem_solution", "Retrieves the complete content and metadata of a specific solution, including the full article text, author information, and related navigation links", {
topicId: z
.string()
.describe("The unique topic ID of the solution to retrieve. This ID can be obtained from the 'topicId' field in the response of the 'list_problem_solutions' tool. Format is typically a string of numbers and letters that uniquely identifies the solution in LeetCode's database.")
}, async ({ topicId }) => {
try {
const data = await this.leetcodeService.fetchSolutionArticleDetail(topicId);
return {
content: [
{
type: "text",
text: JSON.stringify({
topicId,
solution: data
})
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Failed to fetch solution detail",
message: error.message
})
}
]
};
}
});
}
registerChina() {
// Problem solutions listing tool (CN-specific)
this.server.tool("list_problem_solutions", "Retrieves a list of community solutions for a specific LeetCode problem, including only metadata like article slug. To view the full content of a solution, use the 'get_problem_solution' tool with the slug returned by this tool.", {
questionSlug: z
.string()
.describe("The URL slug/identifier of the problem to retrieve solutions for (e.g., 'two-sum', 'add-two-numbers'). This is the same string that appears in the LeetCode problem URL after '/problems/'"),
limit: z
.number()
.min(1)
.optional()
.default(10)
.describe("Maximum number of solutions to return per request. Used for pagination and controlling response size. Default is 20 if not specified. Must be a positive integer. If not provided or set to a very large number, the system may still apply internal limits."),
skip: z
.number()
.optional()
.describe("Number of solutions to skip before starting to collect results. Used in conjunction with 'limit' for implementing pagination. Default is 0 if not specified. Must be a non-negative integer."),
orderBy: z
.enum([
"DEFAULT",
"MOST_UPVOTE",
"HOT",
"NEWEST_TO_OLDEST",
"OLDEST_TO_NEWEST"
])
.default("DEFAULT")
.optional()
.describe("Sorting criteria for the returned solutions. 'DEFAULT' uses the default algorithm, 'MOST_UPVOTE' sorts by the number of upvotes (highest first), 'HOT' prioritizes trending solutions with recent engagement, 'NEWEST_TO_OLDEST' sorts by publication date (newest first), and 'OLDEST_TO_NEWEST' sorts by publication date (oldest first)."),
userInput: z
.string()
.optional()
.describe("Search term to filter solutions by title, content, or author name. Case insensitive. Useful for finding specific approaches or algorithms mentioned in solutions."),
tagSlugs: z
.array(z.string())
.optional()
.default([])
.describe("Array of tag identifiers to filter solutions by programming languages (e.g., 'python', 'java') or problem algorithm/data-structure approaches (e.g., 'dynamic-programming', 'recursion'). Only solutions tagged with at least one of the specified tags will be returned.")
}, async ({ questionSlug, limit, skip, orderBy, userInput, tagSlugs }) => {
try {
const options = {
limit,
skip,
orderBy,
userInput,
tagSlugs
};
const data = await this.leetcodeService.fetchQuestionSolutionArticles(questionSlug, options);
return {
content: [
{
type: "text",
text: JSON.stringify({
questionSlug,
solutionArticles: data
})
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Failed to fetch solutions",
message: error.message
})
}
]
};
}
});
// Solution article detail tool (CN-specific)
this.server.tool("get_problem_solution", "Retrieves the complete content and metadata of a specific solution, including the full article text, author information, and related navigation links", {
slug: z
.string()
.describe("The unique slug/identifier of the solution to retrieve. This slug can be obtained from the 'node.slug' field in the response of the 'list_problem_solutions' tool. A URL-friendly slug string to identify solutions.")
}, async ({ slug }) => {
try {
const data = await this.leetcodeService.fetchSolutionArticleDetail(slug);
return {
content: [
{
type: "text",
text: JSON.stringify({
slug,
solution: data
})
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
error: "Failed to fetch solution detail",
message: error.message
})
}
]
};
}
});
}
}
/**
* Registers all solution-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 registerSolutionTools(server, leetcodeService) {
const registry = new SolutionToolRegistry(server, leetcodeService);
registry.registerTools();
}
//# sourceMappingURL=solution-tools.js.map