From ff6cf117f4abc5aeb761e7e09abfed4135a246dd Mon Sep 17 00:00:00 2001 From: jinzcdev Date: Fri, 25 Apr 2025 09:27:35 +0800 Subject: [PATCH 1/4] docs: update the README --- README.md | 48 +++++++++++++++++++++++++++++++++++------------- README_zh-CN.md | 49 ++++++++++++++++++++++++++++++++++++------------- 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 7c19a4d..eea8c39 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,29 @@ npm run build && node build/index.js --site global ### Visual Studio Code Integration -Add the following JSON configuration to your User Settings (JSON) file Code. Access this by pressing `Ctrl/Cmd + Shift + P` and searching for `Preferences: Open User Settings (JSON)`. +Add the following JSON configuration to your User Settings (JSON) file. Access this by pressing `Ctrl/Cmd + Shift + P` and searching for `Preferences: Open User Settings (JSON)`. + +#### Option 1: Using Environment Variables + +```json +{ + "mcp": { + "servers": { + "leetcode": { + "type": "stdio", + "command": "npx", + "args": ["-y", "@jinzcdev/leetcode-mcp-server"], + "env": { + "LEETCODE_SITE": "global", + "LEETCODE_SESSION": "" + } + } + } + } +} +``` + +#### Option 2: Using Command Line Arguments ```json { @@ -90,18 +112,18 @@ Add the following JSON configuration to your User Settings (JSON) file Code. Acc For LeetCode China site, modify the `--site` parameter to `cn`. -## Environment Variables - -The server supports the following environment variables: - -- `LEETCODE_SITE`: LeetCode API endpoint ('global' or 'cn') -- `LEETCODE_SESSION`: LeetCode session cookie for authenticated API access - -**Priority Note**: -Command-line arguments take precedence over environment variables when both are specified. For example: - -- If `LEETCODE_SITE=cn` is set but you run `leetcode-mcp-server --site global`, the server will use `global`. -- If `LEETCODE_SESSION` exists but you provide `--session "new_cookie"`, the command-line session value will be used. +> [!TIP] +> +> The server supports the following environment variables: +> +> - `LEETCODE_SITE`: LeetCode API endpoint ('global' or 'cn') +> - `LEETCODE_SESSION`: LeetCode session cookie for authenticated API access +> +> **Priority Note**: +> Command-line arguments take precedence over environment variables when both are specified. For example: +> +> - If `LEETCODE_SITE=cn` is set but you run `leetcode-mcp-server --site global`, the server will use `global`. +> - If `LEETCODE_SESSION` exists but you provide `--session "new_cookie"`, the command-line session value will be used. ## Available Tools diff --git a/README_zh-CN.md b/README_zh-CN.md index 25ce3bb..a270fbc 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -65,7 +65,29 @@ npm run build && node build/index.js --site global ### Visual Studio Code 集成 -在您的用户设置 (JSON) 文件中添加以下 JSON 配置。通过按 `Ctrl/Cmd + Shift + P` 并搜索 `Preferences: Open User Settings (JSON)` 来访问此文件。 +在您的用户设置 (JSON) 文件中添加以下配置。通过按 `Ctrl/Cmd + Shift + P` 并搜索 `Preferences: Open User Settings (JSON)` 来访问此文件。 + +#### 方式一:使用环境变量 + +```json +{ + "mcp": { + "servers": { + "leetcode": { + "type": "stdio", + "command": "npx", + "args": ["-y", "@jinzcdev/leetcode-mcp-server"], + "env": { + "LEETCODE_SITE": "global", + "LEETCODE_SESSION": "<您的 LEETCODE 会话 COOKIE>" + } + } + } + } +} +``` + +#### 方式二:使用命令行参数 ```json { @@ -90,18 +112,19 @@ npm run build && node build/index.js --site global 对于 LeetCode 中国站点,请将 `--site` 参数修改为 `cn`。 -## 环境变量 - -服务器支持以下环境变量: - -- `LEETCODE_SITE`:LeetCode API 端点('global' 或 'cn') -- `LEETCODE_SESSION`:用于授权 API 访问的 LeetCode 会话 cookie - -**优先级说明**: -当同时指定命令行参数和环境变量时,命令行参数优先。例如: - -- 如果设置了 `LEETCODE_SITE=cn` 但您运行 `leetcode-mcp-server --site global`,服务器将使用 `global`。 -- 如果存在 `LEETCODE_SESSION` 但您提供了 `--session "new_cookie"`,将使用命令行中的会话值。 +> [!TIP] +> +> 服务支持以下环境变量: +> +> - `LEETCODE_SITE`:LeetCode API 端点('global' 或 'cn') +> - `LEETCODE_SESSION`:用于授权 API 访问的 LeetCode 会话 cookie +> +> **优先级说明**: +> +> 当同时指定命令行参数和环境变量时,命令行参数优先。例如: +> +> - 如果设置了 `LEETCODE_SITE=cn` 但您运行 `leetcode-mcp-server --site global`,服务器将使用 `global`。 +> - 如果存在 `LEETCODE_SESSION` 但您提供了 `--session "new_cookie"`,将使用命令行中的参数值。 ## 可用工具 From cfb8df7257a2bee199dacd122e816140563ee4e8 Mon Sep 17 00:00:00 2001 From: jinzcdev Date: Fri, 25 Apr 2025 11:25:10 +0800 Subject: [PATCH 2/4] feat(userStatus): simplify user status response in LeetCode services --- src/leetcode/leetcode-cn-service.ts | 10 +++++++++- src/leetcode/leetcode-global-service.ts | 9 ++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/leetcode/leetcode-cn-service.ts b/src/leetcode/leetcode-cn-service.ts index c2c94de..58f2c47 100644 --- a/src/leetcode/leetcode-cn-service.ts +++ b/src/leetcode/leetcode-cn-service.ts @@ -38,7 +38,15 @@ export class LeetCodeCNService implements LeetCodeBaseService { if (!this.isAuthenticated()) { throw new Error("Authentication required to fetch user status"); } - return await this.leetCodeApi.userStatus(); + return await this.leetCodeApi.userStatus().then((res) => { + return { + isSignedIn: res?.isSignedIn ?? false, + username: res?.username ?? "", + avatar: res?.avatar ?? "", + isAdmin: res?.isAdmin ?? false, + useTranslation: res?.useTranslation ?? false + }; + }); } async fetchUserAllSubmissions(options: { diff --git a/src/leetcode/leetcode-global-service.ts b/src/leetcode/leetcode-global-service.ts index 833cc79..9e7bdbf 100644 --- a/src/leetcode/leetcode-global-service.ts +++ b/src/leetcode/leetcode-global-service.ts @@ -32,7 +32,14 @@ export class LeetCodeGlobalService implements LeetCodeBaseService { if (!this.isAuthenticated()) { throw new Error("Authentication required to fetch user status"); } - return await this.leetCodeApi.whoami(); + return await this.leetCodeApi.whoami().then((res) => { + return { + isSignedIn: res?.isSignedIn ?? false, + username: res?.username ?? "", + avatar: res?.avatar ?? "", + isAdmin: res?.isAdmin ?? false + }; + }); } async fetchUserAllSubmissions(options: { From 285dfd839cd5ef1acca367a837763aa97287faa2 Mon Sep 17 00:00:00 2001 From: jinzcdev Date: Fri, 25 Apr 2025 14:48:25 +0800 Subject: [PATCH 3/4] feat: simplify user profile response and question list structure --- src/leetcode/leetcode-cn-service.ts | 66 +++++++++++++++++++++---- src/leetcode/leetcode-global-service.ts | 33 ++++++++++++- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/src/leetcode/leetcode-cn-service.ts b/src/leetcode/leetcode-cn-service.ts index 58f2c47..e7c4449 100644 --- a/src/leetcode/leetcode-cn-service.ts +++ b/src/leetcode/leetcode-cn-service.ts @@ -121,7 +121,44 @@ export class LeetCodeCNService implements LeetCodeBaseService { } async fetchUserProfile(username: string): Promise { - return await this.leetCodeApi.user(username); + const originalProfile = await this.leetCodeApi.user(username); + + if (!originalProfile || !originalProfile.userProfilePublicProfile) { + return originalProfile; + } + + const publicProfile = originalProfile.userProfilePublicProfile || {}; + const userProfile = publicProfile.profile || {}; + const skillSet = userProfile.skillSet || {}; + + const simplifiedProfile = { + username: userProfile.userSlug, + questionProgress: originalProfile.userProfileUserQuestionProgress, + siteRanking: publicProfile.siteRanking, + profile: { + userSlug: userProfile.userSlug, + realName: userProfile.realName, + userAvatar: userProfile.userAvatar, + globalLocation: userProfile.globalLocation, + school: userProfile.school?.name, + socialAccounts: (userProfile.socialAccounts || []).filter( + (account: any) => !!account.profileUrl + ), + skillSet: { + topics: (skillSet.topics || []).map( + (topic: any) => topic.slug + ), + topicAreaScores: (skillSet.topicAreaScores || []).map( + (item: any) => ({ + slug: item.topicArea?.slug, + score: item.score + }) + ) + } + } + }; + + return simplifiedProfile; } async fetchUserContestRanking( @@ -210,17 +247,28 @@ export class LeetCodeCNService implements LeetCodeBaseService { filters.searchKeywords = searchKeywords; } - const response = await this.leetCodeApi.graphql({ + const { data } = await this.leetCodeApi.graphql({ query: SEARCH_PROBLEMS_QUERY, - variables: { - categorySlug: category, - limit, - skip: offset, - filters - } + variables: { categorySlug: category, limit, skip: offset, filters } }); - return response.data?.problemsetQuestionList; + const questionList = data?.problemsetQuestionList; + if (!questionList) { + return { hasMore: false, total: 0, questions: [] }; + } + + return { + hasMore: questionList.hasMore, + total: questionList.total, + questions: questionList.questions.map((q: any) => ({ + title: q.title, + titleCn: q.titleCn, + titleSlug: q.titleSlug, + difficulty: q.difficulty, + acRate: q.acRate, + topicTags: q.topicTags.map((tag: any) => tag.slug) + })) + }; } async fetchUserProgressQuestionList(options?: { diff --git a/src/leetcode/leetcode-global-service.ts b/src/leetcode/leetcode-global-service.ts index 9e7bdbf..2b00def 100644 --- a/src/leetcode/leetcode-global-service.ts +++ b/src/leetcode/leetcode-global-service.ts @@ -110,6 +110,21 @@ export class LeetCodeGlobalService implements LeetCodeBaseService { async fetchUserProfile(username: string): Promise { const profile = await this.leetCodeApi.user(username); + if (profile && profile.matchedUser) { + const { matchedUser } = profile; + + return { + username: matchedUser.username, + realName: matchedUser.profile.realName, + userAvatar: matchedUser.profile.userAvatar, + countryName: matchedUser.profile.countryName, + githubUrl: matchedUser.githubUrl, + company: matchedUser.profile.company, + school: matchedUser.profile.school, + ranking: matchedUser.profile.ranking, + totalSubmissionNum: matchedUser.submitStats?.totalSubmissionNum + }; + } return profile; } @@ -211,7 +226,23 @@ export class LeetCodeGlobalService implements LeetCodeBaseService { } }); - return response.data?.problemsetQuestionList; + const questionList = response.data?.problemsetQuestionList; + if (!questionList) { + return { + total: 0, + questions: [] + }; + } + return { + total: questionList.total, + questions: questionList.questions.map((question: any) => ({ + title: question.title, + titleSlug: question.titleSlug, + difficulty: question.difficulty, + acRate: question.acRate, + topicTags: question.topicTags.map((tag: any) => tag.slug) + })) + }; } async fetchUserProgressQuestionList(options?: { From f371f2c6f96e79f5c40436ab011fc3a5d7e3c869 Mon Sep 17 00:00:00 2001 From: jinzcdev Date: Fri, 25 Apr 2025 14:50:30 +0800 Subject: [PATCH 4/4] 1.1.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4b73c2..1c0b4aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@jinzcdev/leetcode-mcp-server", - "version": "1.0.1", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@jinzcdev/leetcode-mcp-server", - "version": "1.0.1", + "version": "1.1.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.8.0", diff --git a/package.json b/package.json index e4dea35..792e2a7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@jinzcdev/leetcode-mcp-server", "description": "MCP Server for LeetCode API (supports leetcode.com and leetcode.cn)", - "version": "1.0.1", + "version": "1.1.0", "author": "jinzcdev", "main": "./build/index.js", "keywords": [