accoding-mcp-server-test-new 0.1.0 → 0.1.1

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.
package/README.md CHANGED
@@ -29,8 +29,12 @@ accoding-mcp-server/
29
29
  │ │ ├── resource-registry.ts # 资源注册基类
30
30
  │ │ ├── problem-resources.ts # 题目资源
31
31
  │ │ └── contest-resources.ts # 比赛资源
32
+ │ ├── transport/ # 传输层
33
+ │ │ ├── http-server.ts # HTTP 服务器实现
34
+ │ │ └── mcp-server-factory.ts # MCP Server 创建工厂
32
35
  │ ├── utils/
33
- │ │ └── logger.ts # 日志工具
36
+ │ │ ├── logger.ts # 日志工具
37
+ │ │ └── http-utils.ts # HTTP 工具函数(CORS、cookie解析)
34
38
  │ └── index.ts # 程序入口
35
39
  ├── package.json
36
40
  ├── tsconfig.json
@@ -41,8 +45,11 @@ accoding-mcp-server/
41
45
 
42
46
  1. **工厂模式**:使用 `AccodingServiceFactory` 创建服务实例
43
47
  2. **注册模式**:使用 `RegistryBase` 统一管理工具和资源的注册
44
- 3. **分层架构**:
45
- - 入口层 (`index.ts`) - 解析参数、创建服务器、注册组件
48
+ 3. **传输层抽象**:支持 stdio 和 HTTP 两种传输模式
49
+ 4. **会话隔离**:HTTP 模式下每个连接独立的会话和服务实例
50
+ 5. **分层架构**:
51
+ - 入口层 (`index.ts`) - 解析参数、选择传输模式
52
+ - 传输层 (`transport/`) - HTTP 服务器实现和会话管理
46
53
  - 服务层 (`accoding/`) - 定义接口、实现业务逻辑
47
54
  - API 层 (`accoding/api/`) - RESTful API 调用封装
48
55
  - 工具/资源层 (`mcp/`) - MCP 工具和资源的注册
@@ -56,17 +63,84 @@ npm install
56
63
  # 编译
57
64
  npm run build
58
65
 
59
- # 运行
60
- node build/index.js
66
+ # stdio 模式运行(默认,用于本地开发)
67
+ node build/index.js --session "your-cookie"
61
68
 
62
- # 使用认证运行
63
- node build/index.js --token <your-token>
69
+ # HTTP 模式运行(用于服务器部署)
70
+ node build/index.js --mode http --port 3000
64
71
  ```
65
72
 
73
+ ### 传输模式说明
74
+
75
+ #### stdio 模式(默认)
76
+ - 使用标准输入输出进行通信
77
+ - 适合本地开发和 CLI 工具集成
78
+ - 通过 `--session` 参数传递 Accoding session cookie
79
+
80
+ #### HTTP 模式
81
+ - 通过 HTTP POST 请求进行通信
82
+ - 支持多客户端并发访问
83
+ - 每个连接独立的会话隔离
84
+ - Session cookie 通过 HTTP 请求头传递:
85
+ - `X-Accoding-Session` header(优先级最高)
86
+ - `Cookie` header(自动解析)
87
+
66
88
  ## 环境变量
67
89
 
68
- - `ACCODING_SESSION_TOKEN`: Accoding 会话令牌(用于认证请求)
69
- - `ACCODING_API_BASE_URL`: Accoding API 基础 URL(默认为 `https://api.accoding.com`)
90
+ - `ACCODING_SESSION`: Accoding 会话 Cookie(stdio 模式使用,HTTP 模式从请求头获取)
91
+
92
+ ## HTTP 模式使用说明
93
+
94
+ ### 启动服务器
95
+
96
+ ```bash
97
+ node build/index.js --mode http --port 3000
98
+ ```
99
+
100
+ ### 客户端请求示例
101
+
102
+ #### 1. 初始化连接
103
+
104
+ ```bash
105
+ curl -X POST http://localhost:3000/mcp \
106
+ -H "Content-Type: application/json" \
107
+ -H "X-Accoding-Session: your-session-cookie" \
108
+ -d '{
109
+ "jsonrpc": "2.0",
110
+ "id": "1",
111
+ "method": "initialize",
112
+ "params": {}
113
+ }'
114
+ ```
115
+
116
+ 响应会包含 `mcp-session-id` header,后续请求需要使用此 ID。
117
+
118
+ #### 2. 调用工具
119
+
120
+ ```bash
121
+ curl -X POST http://localhost:3000/mcp \
122
+ -H "Content-Type: application/json" \
123
+ -H "mcp-session-id: <从初始化响应中获取的session-id>" \
124
+ -H "X-Accoding-Session: your-session-cookie" \
125
+ -d '{
126
+ "jsonrpc": "2.0",
127
+ "id": "2",
128
+ "method": "tools/call",
129
+ "params": {
130
+ "name": "get_contest_problems",
131
+ "arguments": {
132
+ "contestId": "12345"
133
+ }
134
+ }
135
+ }'
136
+ ```
137
+
138
+ ### 会话隔离
139
+
140
+ - 每个 HTTP 连接都有独立的会话 ID
141
+ - 每个会话都有独立的 `AccodingService` 实例
142
+ - 不同客户端的 session cookie 互不影响
143
+ - 连接关闭后自动清理会话资源
70
144
 
71
145
  ## 待实现功能
72
146
 
@@ -6,6 +6,35 @@ export interface AccodingBaseService {
6
6
  * 根据比赛ID获取比赛题目详情
7
7
  */
8
8
  getContestProblems(contestId: string): Promise<any>;
9
+ /**
10
+ * 获取当前登录用户信息
11
+ * 需要有效的 session cookie
12
+ */
13
+ getUserInfo(): Promise<any>;
14
+ /**
15
+ * 获取班级列表
16
+ * 根据班级名称和课程名称搜索班级,如果都为空则返回全部班级
17
+ * 需要有效的 session cookie
18
+ */
19
+ getClassList(className?: string, courseName?: string): Promise<any>;
20
+ /**
21
+ * 获取当前用户加入的班级列表
22
+ * 返回当前登录用户加入的所有班级信息
23
+ * 需要有效的 session cookie
24
+ */
25
+ getMyClassList(): Promise<any>;
26
+ /**
27
+ * 获取用户提交统计
28
+ * 返回各种提交状态的数量:AC(通过)、CE(编译错误)、PE(输出错误)、OE(其他错误)、WA(未通过)、TLE(超时)
29
+ * 需要有效的 session cookie
30
+ */
31
+ getUserSubmissionStats(): Promise<any>;
32
+ /**
33
+ * 获取用户本周提交情况
34
+ * 返回7天中每天的提交情况,每天包含6种状态的数量(按顺序:AC、CE、WA、PE、TLE、OE)
35
+ * 需要有效的 session cookie
36
+ */
37
+ getUserWeeklySubmissions(): Promise<any>;
9
38
  /**
10
39
  * 检查当前服务是否有有效的认证凭证
11
40
  */
@@ -6,5 +6,10 @@ export declare class AccodingServiceImpl implements AccodingBaseService {
6
6
  private session?;
7
7
  constructor(session?: string);
8
8
  getContestProblems(contestId: string): Promise<any>;
9
+ getUserInfo(): Promise<any>;
10
+ getClassList(className?: string, courseName?: string): Promise<any>;
11
+ getMyClassList(): Promise<any>;
12
+ getUserSubmissionStats(): Promise<any>;
13
+ getUserWeeklySubmissions(): Promise<any>;
9
14
  isAuthenticated(): boolean;
10
15
  }
@@ -1,3 +1,4 @@
1
+ import * as userApi from "./api/user-api.js";
1
2
  import * as contestApi from "./api/contest-api.js";
2
3
  /**
3
4
  * Accoding服务实现
@@ -11,6 +12,26 @@ export class AccodingServiceImpl {
11
12
  // 调用真实的contestApi获取比赛题目
12
13
  return await contestApi.getContestProblems(contestId, this.session);
13
14
  }
15
+ async getUserInfo() {
16
+ // 调用真实的userApi获取用户信息
17
+ return await userApi.getUserInfo(this.session);
18
+ }
19
+ async getClassList(className, courseName) {
20
+ // 调用真实的userApi获取班级列表
21
+ return await userApi.getClassList(className, courseName, this.session);
22
+ }
23
+ async getMyClassList() {
24
+ // 调用真实的userApi获取我的班级列表
25
+ return await userApi.getMyClassList(this.session);
26
+ }
27
+ async getUserSubmissionStats() {
28
+ // 调用真实的userApi获取用户提交统计
29
+ return await userApi.getUserSubmissionStats(this.session);
30
+ }
31
+ async getUserWeeklySubmissions() {
32
+ // 调用真实的userApi获取用户本周提交情况
33
+ return await userApi.getUserWeeklySubmissions(this.session);
34
+ }
14
35
  isAuthenticated() {
15
36
  return !!this.session;
16
37
  }
@@ -1 +1 @@
1
- {"version":3,"file":"accoding-service-impl.js","sourceRoot":"","sources":["../../src/accoding/accoding-service-impl.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,UAAU,MAAM,sBAAsB,CAAC;AAGnD;;GAEG;AACH,MAAM,OAAO,mBAAmB;IACpB,OAAO,CAAU;IAEzB,YAAY,OAAgB;QACxB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,SAAiB;QACtC,wBAAwB;QACxB,OAAO,MAAM,UAAU,CAAC,kBAAkB,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACxE,CAAC;IAGD,eAAe;QACX,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;IAC1B,CAAC;CACJ"}
1
+ {"version":3,"file":"accoding-service-impl.js","sourceRoot":"","sources":["../../src/accoding/accoding-service-impl.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,OAAO,MAAM,mBAAmB,CAAC;AAC7C,OAAO,KAAK,UAAU,MAAM,sBAAsB,CAAC;AAGnD;;GAEG;AACH,MAAM,OAAO,mBAAmB;IACpB,OAAO,CAAU;IAEzB,YAAY,OAAgB;QACxB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,SAAiB;QACtC,wBAAwB;QACxB,OAAO,MAAM,UAAU,CAAC,kBAAkB,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACxE,CAAC;IAED,KAAK,CAAC,WAAW;QACb,qBAAqB;QACrB,OAAO,MAAM,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,SAAkB,EAAE,UAAmB;QACtD,qBAAqB;QACrB,OAAO,MAAM,OAAO,CAAC,YAAY,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3E,CAAC;IAED,KAAK,CAAC,cAAc;QAChB,uBAAuB;QACvB,OAAO,MAAM,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,sBAAsB;QACxB,uBAAuB;QACvB,OAAO,MAAM,OAAO,CAAC,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,wBAAwB;QAC1B,yBAAyB;QACzB,OAAO,MAAM,OAAO,CAAC,wBAAwB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChE,CAAC;IAED,eAAe;QACX,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;IAC1B,CAAC;CACJ"}
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Accoding平台比赛相关API调用
3
3
  */
4
- const API_BASE_URL = "https://accoding.buaa.edu.cn";
4
+ //const API_BASE_URL = "https://accoding.buaa.edu.cn";
5
+ const API_BASE_URL = "https://http://10.251.252.33";
5
6
  /**
6
7
  * 向Accoding API发起HTTP请求
7
8
  */
@@ -12,7 +13,7 @@ async function apiRequest(endpoint, options = {}, session) {
12
13
  ...options.headers
13
14
  };
14
15
  if (session) {
15
- headers["Cookie"] = session;
16
+ headers["Authorization"] = session;
16
17
  }
17
18
  const response = await fetch(url, {
18
19
  ...options,
@@ -1 +1 @@
1
- {"version":3,"file":"contest-api.js","sourceRoot":"","sources":["../../../src/accoding/api/contest-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,YAAY,GAAG,8BAA8B,CAAC;AAEpD;;GAEG;AACH,KAAK,UAAU,UAAU,CACrB,QAAgB,EAChB,UAAuB,EAAE,EACzB,OAAgB;IAEhB,MAAM,GAAG,GAAG,GAAG,YAAY,GAAG,QAAQ,EAAE,CAAC;IACzC,MAAM,OAAO,GAA2B;QACpC,cAAc,EAAE,kBAAkB;QAClC,GAAI,OAAO,CAAC,OAAkC;KACjD,CAAC;IAEF,IAAI,OAAO,EAAE,CAAC;QACV,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC;IAChC,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC9B,GAAG,OAAO;QACV,OAAO;KACV,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACzE,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACzD,IAAI,WAAW,IAAI,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAC1D,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACjC,CAAC;SAAM,CAAC;QACJ,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,kCAAkC,WAAW,EAAE,CAAC,CAAC;IACrE,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,SAAiB,EAAE,OAAgB;IACxE,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,iBAAiB,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;IAE/F,YAAY;IACZ,IAAI,CAAC,WAAW,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChE,OAAO,EAAE,CAAC;IACd,CAAC;IAED,aAAa;IACb,OAAO,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAY,EAAE,EAAE;QAC7C,IAAI,kBAAkB,GAAa,EAAE,CAAC;QACtC,IAAI,CAAC;YACD,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;gBACvB,MAAM,WAAW,GAAG,OAAO,OAAO,CAAC,YAAY,KAAK,QAAQ;oBACxD,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC;oBAClC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;gBAC3B,kBAAkB,GAAG,WAAW,CAAC,mBAAmB,IAAI,EAAE,CAAC;YAC/D,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,eAAe;YACf,kBAAkB,GAAG,EAAE,CAAC;QAC5B,CAAC;QAED,OAAO;YACH,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,KAAK,EAAE,OAAO,CAAC,oBAAoB,EAAE,KAAK,IAAI,GAAG;YACjD,KAAK,EAAE,OAAO,CAAC,oBAAoB,EAAE,KAAK,IAAI,CAAC;YAC/C,kBAAkB,EAAE,kBAAkB;SACzC,CAAC;IACN,CAAC,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"contest-api.js","sourceRoot":"","sources":["../../../src/accoding/api/contest-api.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,8 @@
2
2
  * Accoding平台题目相关API调用
3
3
  * 预留文件,当前无需实现具体功能
4
4
  */
5
- const API_BASE_URL = "https://accoding.buaa.edu.cn";
5
+ //const API_BASE_URL = "https://accoding.buaa.edu.cn";
6
+ const API_BASE_URL = "https://http://10.251.252.33";
6
7
  /**
7
8
  * 向Accoding API发起HTTP请求
8
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"problem-api.js","sourceRoot":"","sources":["../../../src/accoding/api/problem-api.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,YAAY,GAAG,8BAA8B,CAAC;AAEpD;;GAEG;AACH,KAAK,UAAU,UAAU,CACrB,QAAgB,EAChB,UAAuB,EAAE,EACzB,OAAgB;IAEhB,MAAM,GAAG,GAAG,GAAG,YAAY,GAAG,QAAQ,EAAE,CAAC;IACzC,MAAM,OAAO,GAA2B;QACpC,cAAc,EAAE,kBAAkB;QAClC,GAAI,OAAO,CAAC,OAAkC;KACjD,CAAC;IAEF,IAAI,OAAO,EAAE,CAAC;QACV,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC;IAChC,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC9B,GAAG,OAAO;QACV,OAAO;KACV,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,YAAY,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC;;AAED,iCAAiC"}
1
+ {"version":3,"file":"problem-api.js","sourceRoot":"","sources":["../../../src/accoding/api/problem-api.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,sDAAsD;AACtD,MAAM,YAAY,GAAG,8BAA8B,CAAC;AAEpD;;GAEG;AACH,KAAK,UAAU,UAAU,CACrB,QAAgB,EAChB,UAAuB,EAAE,EACzB,OAAgB;IAEhB,MAAM,GAAG,GAAG,GAAG,YAAY,GAAG,QAAQ,EAAE,CAAC;IACzC,MAAM,OAAO,GAA2B;QACpC,cAAc,EAAE,kBAAkB;QAClC,GAAI,OAAO,CAAC,OAAkC;KACjD,CAAC;IAEF,IAAI,OAAO,EAAE,CAAC;QACV,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC;IAChC,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC9B,GAAG,OAAO;QACV,OAAO;KACV,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,YAAY,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC;;AAED,iCAAiC"}
@@ -1,5 +1,29 @@
1
1
  /**
2
- * Accoding平台用户相关API调用
3
- * 预留文件,当前无需实现具体功能
2
+ * 获取当前登录用户信息
3
+ * 需要有效的 session cookie
4
4
  */
5
- export {};
5
+ export declare function getUserInfo(session?: string): Promise<any>;
6
+ /**
7
+ * 获取班级列表
8
+ * 根据班级名称和课程名称搜索班级,如果都为空则返回全部班级
9
+ * 需要有效的 session cookie
10
+ */
11
+ export declare function getClassList(className?: string, courseName?: string, session?: string): Promise<any>;
12
+ /**
13
+ * 获取当前用户加入的班级列表
14
+ * 返回当前登录用户加入的所有班级信息
15
+ * 需要有效的 session cookie
16
+ */
17
+ export declare function getMyClassList(session?: string): Promise<any>;
18
+ /**
19
+ * 获取用户提交统计
20
+ * 返回各种提交状态的数量:AC(通过)、CE(编译错误)、PE(输出错误)、OE(其他错误)、WA(未通过)、TLE(超时)
21
+ * 需要有效的 session cookie
22
+ */
23
+ export declare function getUserSubmissionStats(session?: string): Promise<any>;
24
+ /**
25
+ * 获取用户本周提交情况
26
+ * 返回7天中每天的提交情况,每天包含6种状态的数量(按顺序:AC、CE、WA、PE、TLE、OE)
27
+ * 需要有效的 session cookie
28
+ */
29
+ export declare function getUserWeeklySubmissions(session?: string): Promise<any>;
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Accoding平台用户相关API调用
3
- * 预留文件,当前无需实现具体功能
4
3
  */
5
- const API_BASE_URL = "https://accoding.buaa.edu.cn";
4
+ //const API_BASE_URL = "https://accoding.buaa.edu.cn";
5
+ const API_BASE_URL = "http://10.251.252.33";
6
6
  /**
7
7
  * 向Accoding API发起HTTP请求
8
8
  */
@@ -13,17 +13,102 @@ async function apiRequest(endpoint, options = {}, session) {
13
13
  ...options.headers
14
14
  };
15
15
  if (session) {
16
- headers["Cookie"] = session;
16
+ headers["Authorization"] = session;
17
17
  }
18
18
  const response = await fetch(url, {
19
19
  ...options,
20
20
  headers
21
21
  });
22
22
  if (!response.ok) {
23
- throw new Error(`API请求失败: ${response.status} ${response.statusText}`);
23
+ const errorText = await response.text().catch(() => response.statusText);
24
+ throw new Error(`API请求失败: ${response.status} ${errorText}`);
24
25
  }
25
- return await response.json();
26
+ const contentType = response.headers.get("content-type");
27
+ if (contentType && contentType.includes("application/json")) {
28
+ return await response.json();
29
+ }
30
+ else {
31
+ const text = await response.text();
32
+ throw new Error(`API返回了非JSON内容: ${contentType}`);
33
+ }
34
+ }
35
+ /**
36
+ * 获取当前登录用户信息
37
+ * 需要有效的 session cookie
38
+ */
39
+ export async function getUserInfo(session) {
40
+ const response = await apiRequest("/api/user/info", { method: "GET" }, session);
41
+ // 检查响应格式
42
+ if (response.status !== 200) {
43
+ throw new Error(`获取用户信息失败: ${response.msg || "未知错误"}`);
44
+ }
45
+ // 返回用户数据
46
+ return response.data || {};
47
+ }
48
+ /**
49
+ * 获取班级列表
50
+ * 根据班级名称和课程名称搜索班级,如果都为空则返回全部班级
51
+ * 需要有效的 session cookie
52
+ */
53
+ export async function getClassList(className, courseName, session) {
54
+ // 构建查询参数
55
+ const params = new URLSearchParams();
56
+ if (className) {
57
+ params.append("class_name", className);
58
+ }
59
+ if (courseName) {
60
+ params.append("course_name", courseName);
61
+ }
62
+ const queryString = params.toString();
63
+ const endpoint = queryString ? `/api/class/list?${queryString}` : "/api/class/list";
64
+ const response = await apiRequest(endpoint, { method: "GET" }, session);
65
+ // 检查响应格式
66
+ if (response.status !== 200) {
67
+ throw new Error(`获取班级列表失败: ${response.msg || "未知错误"}`);
68
+ }
69
+ // 返回班级列表数据
70
+ return response.data || [];
71
+ }
72
+ /**
73
+ * 获取当前用户加入的班级列表
74
+ * 返回当前登录用户加入的所有班级信息
75
+ * 需要有效的 session cookie
76
+ */
77
+ export async function getMyClassList(session) {
78
+ const response = await apiRequest("/api/class/myList", { method: "GET" }, session);
79
+ // 检查响应格式
80
+ if (response.status !== 200) {
81
+ throw new Error(`获取我的班级列表失败: ${response.msg || "未知错误"}`);
82
+ }
83
+ // 返回班级列表数据
84
+ return response.data || [];
85
+ }
86
+ /**
87
+ * 获取用户提交统计
88
+ * 返回各种提交状态的数量:AC(通过)、CE(编译错误)、PE(输出错误)、OE(其他错误)、WA(未通过)、TLE(超时)
89
+ * 需要有效的 session cookie
90
+ */
91
+ export async function getUserSubmissionStats(session) {
92
+ const response = await apiRequest("/api/user/sub-type", { method: "GET" }, session);
93
+ // 检查响应格式
94
+ if (response.status !== 200) {
95
+ throw new Error(`获取用户提交统计失败: ${response.msg || "未知错误"}`);
96
+ }
97
+ // 返回统计数据
98
+ return response.data || {};
99
+ }
100
+ /**
101
+ * 获取用户本周提交情况
102
+ * 返回7天中每天的提交情况,每天包含6种状态的数量(按顺序:AC、CE、WA、PE、TLE、OE)
103
+ * 需要有效的 session cookie
104
+ */
105
+ export async function getUserWeeklySubmissions(session) {
106
+ const response = await apiRequest("/api/user/sub-week", { method: "GET" }, session);
107
+ // 检查响应格式
108
+ if (response.status !== 200) {
109
+ throw new Error(`获取用户本周提交情况失败: ${response.msg || "未知错误"}`);
110
+ }
111
+ // 返回本周提交数据
112
+ return response.data || [];
26
113
  }
27
- export {};
28
- // 此文件暂时没有导出的API函数,后续需要用户相关功能时再实现
29
114
  //# sourceMappingURL=user-api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"user-api.js","sourceRoot":"","sources":["../../../src/accoding/api/user-api.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,YAAY,GAAG,8BAA8B,CAAC;AAEpD;;GAEG;AACH,KAAK,UAAU,UAAU,CACrB,QAAgB,EAChB,UAAuB,EAAE,EACzB,OAAgB;IAEhB,MAAM,GAAG,GAAG,GAAG,YAAY,GAAG,QAAQ,EAAE,CAAC;IACzC,MAAM,OAAO,GAA2B;QACpC,cAAc,EAAE,kBAAkB;QAClC,GAAI,OAAO,CAAC,OAAkC;KACjD,CAAC;IAEF,IAAI,OAAO,EAAE,CAAC;QACV,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC;IAChC,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC9B,GAAG,OAAO;QACV,OAAO;KACV,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,YAAY,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC;;AAED,iCAAiC"}
1
+ {"version":3,"file":"user-api.js","sourceRoot":"","sources":["../../../src/accoding/api/user-api.ts"],"names":[],"mappings":"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"}
package/build/index.js CHANGED
@@ -1,28 +1,26 @@
1
1
  #!/usr/bin/env node
2
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
3
  import minimist from "minimist";
5
- import { readFileSync } from "node:fs";
6
- import { dirname, join } from "node:path";
7
- import { fileURLToPath } from "node:url";
8
4
  import { AccodingServiceFactory } from "./accoding/accoding-service-factory.js";
9
- import { registerProblemResources } from "./mcp/resources/problem-resources.js";
10
- import { registerContestResources } from "./mcp/resources/contest-resources.js";
11
- import { registerContestTools } from "./mcp/tools/contest-tools.js";
12
- import { registerProblemTools } from "./mcp/tools/problem-tools.js";
13
- import { registerSubmissionTools } from "./mcp/tools/submission-tools.js";
14
- import { registerUserTools } from "./mcp/tools/user-tools.js";
5
+ import { startHttpServer } from "./transport/http-server.js";
6
+ import { createMCPServer } from "./transport/mcp-server-factory.js";
15
7
  import logger from "./utils/logger.js";
16
8
  /**
17
9
  * 解析并验证命令行参数
18
10
  */
19
11
  function parseArgs() {
20
12
  const args = minimist(process.argv.slice(2), {
21
- string: ["session"],
13
+ string: ["session", "mode", "port"],
22
14
  boolean: ["help"],
23
15
  alias: {
24
16
  s: "session",
17
+ m: "mode",
18
+ p: "port",
25
19
  h: "help"
20
+ },
21
+ default: {
22
+ mode: "stdio",
23
+ port: "3000"
26
24
  }
27
25
  });
28
26
  if (args.help) {
@@ -32,46 +30,51 @@ Accoding MCP Server - 北航Accoding平台MCP服务
32
30
  用法: accoding-mcp-server [选项]
33
31
 
34
32
  选项:
35
- --session, -s <cookie> Accoding平台会话Cookie
33
+ --session, -s <cookie> Accoding平台会话Cookie(stdio模式使用)
34
+ --mode, -m <mode> 传输模式: stdio(默认)或 http
35
+ --port, -p <port> HTTP模式端口号(默认: 3000)
36
36
  --help, -h 显示帮助信息
37
+
38
+ 示例:
39
+ # stdio 模式(默认)
40
+ accoding-mcp-server --session "your-cookie"
41
+
42
+ # HTTP 模式
43
+ accoding-mcp-server --mode http --port 3000
37
44
  `);
38
45
  process.exit(0);
39
46
  }
47
+ const mode = args.mode === "http" ? "http" : "stdio";
48
+ const port = mode === "http" ? parseInt(args.port, 10) || 3000 : undefined;
40
49
  return {
41
- session: args.session || process.env.ACCODING_SESSION
50
+ session: args.session || process.env.ACCODING_SESSION,
51
+ mode,
52
+ port
42
53
  };
43
54
  }
44
55
  /**
45
- * 获取package.json中的项目元数据
56
+ * 启动 stdio 模式的 MCP 服务器
46
57
  */
47
- function getPackageJson() {
48
- const __filename = fileURLToPath(import.meta.url);
49
- const __dirname = dirname(__filename);
50
- const packageJSONPath = join(__dirname, "..", "package.json");
51
- const packageJSON = JSON.parse(readFileSync(packageJSONPath, "utf-8"));
52
- return packageJSON;
58
+ async function startStdioServer(session) {
59
+ const accodingService = AccodingServiceFactory.createService(session);
60
+ const server = createMCPServer(accodingService);
61
+ const transport = new StdioServerTransport();
62
+ await server.connect(transport);
63
+ logger.info("Accoding MCP Server (stdio模式) 已启动");
53
64
  }
54
65
  /**
55
- * 主函数:初始化并启动Accoding MCP服务器
66
+ * 主函数:根据模式选择启动方式
56
67
  */
57
68
  async function main() {
58
69
  const options = parseArgs();
59
- const packageJSON = getPackageJson();
60
- const server = new McpServer({
61
- name: "Accoding MCP Server",
62
- version: packageJSON.version
63
- });
64
- const accodingService = AccodingServiceFactory.createService(options.session);
65
- // 注册所有工具和资源(为后续扩展预留)
66
- registerProblemTools(server, accodingService);
67
- registerUserTools(server, accodingService);
68
- registerContestTools(server, accodingService);
69
- registerSubmissionTools(server, accodingService);
70
- registerProblemResources(server, accodingService);
71
- registerContestResources(server, accodingService);
72
- const transport = new StdioServerTransport();
73
- await server.connect(transport);
74
- logger.info("Accoding MCP Server 已启动");
70
+ if (options.mode === "http") {
71
+ // HTTP 模式:启动 HTTP 服务器
72
+ await startHttpServer(options.port);
73
+ }
74
+ else {
75
+ // stdio 模式:使用标准输入输出
76
+ await startStdioServer(options.session);
77
+ }
75
78
  }
76
79
  main().catch((error) => {
77
80
  logger.error("启动Accoding MCP Server失败: %s", error);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,MAAM,MAAM,mBAAmB,CAAC;AAEvC;;GAEG;AACH,SAAS,SAAS;IACd,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;QACzC,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC;QACnC,OAAO,EAAE,CAAC,MAAM,CAAC;QACjB,KAAK,EAAE;YACH,CAAC,EAAE,SAAS;YACZ,CAAC,EAAE,MAAM;YACT,CAAC,EAAE,MAAM;YACT,CAAC,EAAE,MAAM;SACZ;QACD,OAAO,EAAE;YACL,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,MAAM;SACf;KACJ,CAAC,CAAC;IAEH,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;SAiBX,CAAC,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IACrD,MAAM,IAAI,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IAE3E,OAAO;QACH,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB;QACrD,IAAI;QACJ,IAAI;KACP,CAAC;AACN,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,gBAAgB,CAAC,OAAgB;IAC5C,MAAM,eAAe,GACjB,sBAAsB,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAElD,MAAM,MAAM,GAAG,eAAe,CAAC,eAAe,CAAC,CAAC;IAEhD,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,IAAI;IACf,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;IAE5B,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC1B,sBAAsB;QACtB,MAAM,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;SAAM,CAAC;QACJ,oBAAoB;QACpB,MAAM,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;AACL,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACnB,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;IACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"}
@@ -6,7 +6,9 @@ import { ToolRegistry } from "./tool-registry.js";
6
6
  */
7
7
  export class ContestToolRegistry extends ToolRegistry {
8
8
  registerCommon() {
9
- this.registerGetContestProblems();
9
+ //注册
10
+ //测试用
11
+ //this.registerGetContestProblems();
10
12
  }
11
13
  registerAuthenticated() {
12
14
  // 需要认证的比赛工具可以在这里注册
@@ -1 +1 @@
1
- {"version":3,"file":"contest-tools.js","sourceRoot":"","sources":["../../../src/mcp/tools/contest-tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;;GAGG;AACH,MAAM,OAAO,mBAAoB,SAAQ,YAAY;IACvC,cAAc;QACpB,IAAI,CAAC,0BAA0B,EAAE,CAAC;IACtC,CAAC;IAES,qBAAqB;QAC3B,mBAAmB;IACvB,CAAC;IAED;;OAEG;IACK,0BAA0B;QAC9B,IAAI,CAAC,MAAM,CAAC,IAAI,CACZ,sBAAsB,EACtB,oBAAoB,EACpB;YACI,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;SAC9C,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;YACpB,IAAI,CAAC;gBACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;gBAE1E,OAAO;oBACH,OAAO,EAAE;wBACL;4BACI,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACjB,SAAS,EAAE,SAAS;gCACpB,QAAQ,EAAE,QAAQ;gCAClB,KAAK,EAAE,QAAQ,CAAC,MAAM;6BACzB,EAAE,IAAI,EAAE,CAAC,CAAC;yBACd;qBACJ;iBACJ,CAAC;YACN,CAAC;YAAC,OAAO,KAAU,EAAE,CAAC;gBAClB,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;gBACpD,OAAO;oBACH,OAAO,EAAE;wBACL;4BACI,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACjB,KAAK,EAAE,gCAAgC;gCACvC,OAAO,EAAE,YAAY;gCACrB,SAAS,EAAE,SAAS;6BACvB,CAAC;yBACL;qBACJ;oBACD,OAAO,EAAE,IAAI;iBAChB,CAAC;YACN,CAAC;QACL,CAAC,CACJ,CAAC;IACN,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAChC,MAAiB,EACjB,eAAoC;IAEpC,MAAM,QAAQ,GAAG,IAAI,mBAAmB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAClE,QAAQ,CAAC,aAAa,EAAE,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"contest-tools.js","sourceRoot":"","sources":["../../../src/mcp/tools/contest-tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;;GAGG;AACH,MAAM,OAAO,mBAAoB,SAAQ,YAAY;IACvC,cAAc;QACpB,IAAI;QACJ,KAAK;QACL,oCAAoC;IACxC,CAAC;IAES,qBAAqB;QAC3B,mBAAmB;IACvB,CAAC;IAED;;OAEG;IACK,0BAA0B;QAC9B,IAAI,CAAC,MAAM,CAAC,IAAI,CACZ,sBAAsB,EACtB,oBAAoB,EACpB;YACI,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;SAC9C,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;YACpB,IAAI,CAAC;gBACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;gBAE1E,OAAO;oBACH,OAAO,EAAE;wBACL;4BACI,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACjB,SAAS,EAAE,SAAS;gCACpB,QAAQ,EAAE,QAAQ;gCAClB,KAAK,EAAE,QAAQ,CAAC,MAAM;6BACzB,EAAE,IAAI,EAAE,CAAC,CAAC;yBACd;qBACJ;iBACJ,CAAC;YACN,CAAC;YAAC,OAAO,KAAU,EAAE,CAAC;gBAClB,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;gBACpD,OAAO;oBACH,OAAO,EAAE;wBACL;4BACI,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACjB,KAAK,EAAE,gCAAgC;gCACvC,OAAO,EAAE,YAAY;gCACrB,SAAS,EAAE,SAAS;6BACvB,CAAC;yBACL;qBACJ;oBACD,OAAO,EAAE,IAAI;iBAChB,CAAC;YACN,CAAC;QACL,CAAC,CACJ,CAAC;IACN,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAChC,MAAiB,EACjB,eAAoC;IAEpC,MAAM,QAAQ,GAAG,IAAI,mBAAmB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAClE,QAAQ,CAAC,aAAa,EAAE,CAAC;AAC7B,CAAC"}
@@ -8,6 +8,26 @@ import { ToolRegistry } from "./tool-registry.js";
8
8
  export declare class UserToolRegistry extends ToolRegistry {
9
9
  protected registerCommon(): void;
10
10
  protected registerAuthenticated(): void;
11
+ /**
12
+ * 注册获取用户信息的工具
13
+ */
14
+ private registerGetUserInfo;
15
+ /**
16
+ * 注册获取班级列表的工具
17
+ */
18
+ private registerGetClassList;
19
+ /**
20
+ * 注册获取我的班级列表的工具
21
+ */
22
+ private registerGetMyClassList;
23
+ /**
24
+ * 注册获取用户提交统计的工具
25
+ */
26
+ private registerGetUserSubmissionStats;
27
+ /**
28
+ * 注册获取用户本周提交情况的工具
29
+ */
30
+ private registerGetUserWeeklySubmissions;
11
31
  }
12
32
  /**
13
33
  * 注册所有用户相关工具
@@ -1,3 +1,4 @@
1
+ import { z } from "zod";
1
2
  import { ToolRegistry } from "./tool-registry.js";
2
3
  /**
3
4
  * 用户工具注册器
@@ -8,7 +9,224 @@ export class UserToolRegistry extends ToolRegistry {
8
9
  // 不需要认证的用户工具可以在这里注册
9
10
  }
10
11
  registerAuthenticated() {
11
- // 需要认证的用户工具可以在这里注册
12
+ this.registerGetUserInfo();
13
+ this.registerGetClassList();
14
+ this.registerGetMyClassList();
15
+ this.registerGetUserSubmissionStats();
16
+ this.registerGetUserWeeklySubmissions();
17
+ }
18
+ /**
19
+ * 注册获取用户信息的工具
20
+ */
21
+ registerGetUserInfo() {
22
+ this.server.tool("get_user_info", "获取当前登录用户的详细信息,包括昵称、邮箱、学校、学号、专业等", {}, async () => {
23
+ try {
24
+ const userInfo = await this.accodingService.getUserInfo();
25
+ return {
26
+ content: [
27
+ {
28
+ type: "text",
29
+ text: JSON.stringify({
30
+ userInfo: userInfo
31
+ }, null, 2)
32
+ }
33
+ ]
34
+ };
35
+ }
36
+ catch (error) {
37
+ const errorMessage = error.message || String(error);
38
+ return {
39
+ content: [
40
+ {
41
+ type: "text",
42
+ text: JSON.stringify({
43
+ error: "Failed to get user info",
44
+ message: errorMessage
45
+ })
46
+ }
47
+ ],
48
+ isError: true
49
+ };
50
+ }
51
+ });
52
+ }
53
+ /**
54
+ * 注册获取班级列表的工具
55
+ */
56
+ registerGetClassList() {
57
+ this.server.tool("get_class_list", "根据班级名称和课程名称搜索班级列表,如果两个参数都为空则返回全部班级", {
58
+ className: z.string().optional().describe("班级名称,用于搜索过滤(可选)"),
59
+ courseName: z.string().optional().describe("课程名称,用于搜索过滤(可选)")
60
+ }, async ({ className, courseName }) => {
61
+ try {
62
+ const classList = await this.accodingService.getClassList(className, courseName);
63
+ return {
64
+ content: [
65
+ {
66
+ type: "text",
67
+ text: JSON.stringify({
68
+ classes: classList,
69
+ count: classList.length,
70
+ filters: {
71
+ className: className || null,
72
+ courseName: courseName || null
73
+ }
74
+ }, null, 2)
75
+ }
76
+ ]
77
+ };
78
+ }
79
+ catch (error) {
80
+ const errorMessage = error.message || String(error);
81
+ return {
82
+ content: [
83
+ {
84
+ type: "text",
85
+ text: JSON.stringify({
86
+ error: "Failed to get class list",
87
+ message: errorMessage,
88
+ filters: {
89
+ className: className || null,
90
+ courseName: courseName || null
91
+ }
92
+ })
93
+ }
94
+ ],
95
+ isError: true
96
+ };
97
+ }
98
+ });
99
+ }
100
+ /**
101
+ * 注册获取我的班级列表的工具
102
+ */
103
+ registerGetMyClassList() {
104
+ this.server.tool("get_my_class_list", "获取当前登录用户加入的所有班级列表,包括班级ID、课程ID、班级名称、课程名称、成员数量、考试数量等信息", {}, async () => {
105
+ try {
106
+ const classList = await this.accodingService.getMyClassList();
107
+ return {
108
+ content: [
109
+ {
110
+ type: "text",
111
+ text: JSON.stringify({
112
+ myClasses: classList,
113
+ count: classList.length
114
+ }, null, 2)
115
+ }
116
+ ]
117
+ };
118
+ }
119
+ catch (error) {
120
+ const errorMessage = error.message || String(error);
121
+ return {
122
+ content: [
123
+ {
124
+ type: "text",
125
+ text: JSON.stringify({
126
+ error: "Failed to get my class list",
127
+ message: errorMessage
128
+ })
129
+ }
130
+ ],
131
+ isError: true
132
+ };
133
+ }
134
+ });
135
+ }
136
+ /**
137
+ * 注册获取用户提交统计的工具
138
+ */
139
+ registerGetUserSubmissionStats() {
140
+ this.server.tool("get_user_submission_stats", "获取当前登录用户的提交统计信息,包括各种提交状态的数量:AC(通过)、CE(编译错误)、PE(输出错误)、OE(其他错误)、WA(未通过)、TLE(超时)", {}, async () => {
141
+ try {
142
+ const stats = await this.accodingService.getUserSubmissionStats();
143
+ return {
144
+ content: [
145
+ {
146
+ type: "text",
147
+ text: JSON.stringify({
148
+ submissionStats: stats,
149
+ description: {
150
+ AC: "通过 (Accepted)",
151
+ CE: "编译错误 (Compilation Error)",
152
+ PE: "输出错误 (Presentation Error)",
153
+ OE: "其他错误 (Other Error)",
154
+ WA: "未通过 (Wrong Answer)",
155
+ TLE: "超时 (Time Limit Exceeded)"
156
+ }
157
+ }, null, 2)
158
+ }
159
+ ]
160
+ };
161
+ }
162
+ catch (error) {
163
+ const errorMessage = error.message || String(error);
164
+ return {
165
+ content: [
166
+ {
167
+ type: "text",
168
+ text: JSON.stringify({
169
+ error: "Failed to get user submission stats",
170
+ message: errorMessage
171
+ })
172
+ }
173
+ ],
174
+ isError: true
175
+ };
176
+ }
177
+ });
178
+ }
179
+ /**
180
+ * 注册获取用户本周提交情况的工具
181
+ */
182
+ registerGetUserWeeklySubmissions() {
183
+ this.server.tool("get_user_weekly_submissions", "获取当前登录用户本周(最近7天)的提交情况,返回每天6种状态的数量(按顺序:AC、CE、WA、PE、TLE、OE)", {}, async () => {
184
+ try {
185
+ const weeklyData = await this.accodingService.getUserWeeklySubmissions();
186
+ // 格式化数据,添加日期和状态说明
187
+ const daysOfWeek = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
188
+ const statusLabels = ["AC(通过)", "CE(编译错误)", "WA(未通过)", "PE(输出错误)", "TLE(超时)", "OE(其他错误)"];
189
+ const formattedData = weeklyData.map((dayData, index) => {
190
+ const dayStats = {};
191
+ dayData.forEach((count, statusIndex) => {
192
+ dayStats[statusLabels[statusIndex]] = count;
193
+ });
194
+ return {
195
+ day: daysOfWeek[index],
196
+ stats: dayStats,
197
+ raw: dayData
198
+ };
199
+ });
200
+ return {
201
+ content: [
202
+ {
203
+ type: "text",
204
+ text: JSON.stringify({
205
+ weeklySubmissions: formattedData,
206
+ rawData: weeklyData,
207
+ statusOrder: statusLabels,
208
+ note: "数据按时间顺序从周日到周六,每天包含6种状态的数量"
209
+ }, null, 2)
210
+ }
211
+ ]
212
+ };
213
+ }
214
+ catch (error) {
215
+ const errorMessage = error.message || String(error);
216
+ return {
217
+ content: [
218
+ {
219
+ type: "text",
220
+ text: JSON.stringify({
221
+ error: "Failed to get user weekly submissions",
222
+ message: errorMessage
223
+ })
224
+ }
225
+ ],
226
+ isError: true
227
+ };
228
+ }
229
+ });
12
230
  }
13
231
  }
14
232
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"user-tools.js","sourceRoot":"","sources":["../../../src/mcp/tools/user-tools.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;;GAGG;AACH,MAAM,OAAO,gBAAiB,SAAQ,YAAY;IACpC,cAAc;QACpB,oBAAoB;IACxB,CAAC;IAES,qBAAqB;QAC3B,mBAAmB;IACvB,CAAC;CACJ;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC7B,MAAiB,EACjB,eAAoC;IAEpC,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC/D,QAAQ,CAAC,aAAa,EAAE,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"user-tools.js","sourceRoot":"","sources":["../../../src/mcp/tools/user-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * 启动 HTTP 服务器
3
+ */
4
+ export declare function startHttpServer(port?: number): Promise<void>;
@@ -0,0 +1,192 @@
1
+ import express from "express";
2
+ import { randomUUID } from "node:crypto";
3
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
4
+ import { AccodingServiceFactory } from "../accoding/accoding-service-factory.js";
5
+ import { extractSessionFromRequest, setCommonHeaders } from "../utils/http-utils.js";
6
+ import logger from "../utils/logger.js";
7
+ import { createMCPServer } from "./mcp-server-factory.js";
8
+ /**
9
+ * 会话管理器:存储和管理所有活跃的会话
10
+ */
11
+ class SessionManager {
12
+ sessions = new Map();
13
+ /**
14
+ * 创建新会话(但不立即 connect)
15
+ */
16
+ createSession(sessionCookie) {
17
+ // 为每个会话创建独立的 AccodingService
18
+ const accodingService = AccodingServiceFactory.createService(sessionCookie);
19
+ // 创建 MCP Server
20
+ const server = createMCPServer(accodingService);
21
+ // 生成 sessionId(使用 UUID)
22
+ const sessionId = randomUUID();
23
+ // 创建 StreamableHTTPServerTransport
24
+ const transport = new StreamableHTTPServerTransport({
25
+ sessionIdGenerator: () => sessionId
26
+ });
27
+ // 监听 transport 关闭事件,清理会话
28
+ transport.onclose = () => {
29
+ this.removeSession(sessionId);
30
+ logger.info(`会话已关闭: ${sessionId}`);
31
+ };
32
+ const sessionInfo = {
33
+ transport,
34
+ server,
35
+ accodingService,
36
+ sessionId,
37
+ createdAt: new Date(),
38
+ initialized: false
39
+ };
40
+ this.sessions.set(sessionId, sessionInfo);
41
+ logger.info(`新会话已创建: ${sessionId}, cookie 长度: ${sessionCookie?.length || 0}`);
42
+ return sessionInfo;
43
+ }
44
+ /**
45
+ * 获取会话
46
+ */
47
+ getSession(sessionId) {
48
+ return this.sessions.get(sessionId);
49
+ }
50
+ /**
51
+ * 移除会话
52
+ */
53
+ removeSession(sessionId) {
54
+ const session = this.sessions.get(sessionId);
55
+ if (session) {
56
+ this.sessions.delete(sessionId);
57
+ logger.info(`会话已移除: ${sessionId}`);
58
+ }
59
+ }
60
+ /**
61
+ * 获取活跃会话数量
62
+ */
63
+ getSessionCount() {
64
+ return this.sessions.size;
65
+ }
66
+ }
67
+ /**
68
+ * 确保请求的 Accept header 正确设置
69
+ */
70
+ function ensureAcceptHeader(req) {
71
+ const acceptHeader = req.headers.accept;
72
+ const requiredAccept = "application/json, text/event-stream";
73
+ if (!acceptHeader ||
74
+ !acceptHeader.includes("application/json") ||
75
+ !acceptHeader.includes("text/event-stream")) {
76
+ // 修改请求对象的 headers
77
+ req.headers.accept = requiredAccept;
78
+ logger.debug(`自动设置 Accept header: ${requiredAccept}`);
79
+ }
80
+ }
81
+ /**
82
+ * 启动 HTTP 服务器
83
+ */
84
+ export async function startHttpServer(port = 3000) {
85
+ const app = express();
86
+ const sessionManager = new SessionManager();
87
+ // 添加 JSON 解析中间件
88
+ app.use(express.json());
89
+ // 处理 OPTIONS 请求(CORS 预检)
90
+ app.options("/mcp", (req, res) => {
91
+ setCommonHeaders(res);
92
+ res.status(204).end();
93
+ });
94
+ // 处理 GET /mcp 请求(可选,用于 SSE 通知)
95
+ app.get("/mcp", (req, res) => {
96
+ setCommonHeaders(res);
97
+ res.status(405)
98
+ .set("Allow", "POST")
99
+ .json({
100
+ error: "Method Not Allowed",
101
+ message: "当前服务器不支持 GET 方法,仅支持 POST 方法"
102
+ });
103
+ });
104
+ // 处理 POST /mcp 请求
105
+ app.post("/mcp", async (req, res) => {
106
+ setCommonHeaders(res);
107
+ // 确保 Accept header 正确
108
+ ensureAcceptHeader(req);
109
+ try {
110
+ const body = req.body;
111
+ const method = body?.method;
112
+ const sessionId = req.headers["mcp-session-id"];
113
+ const session = sessionId ? sessionManager.getSession(sessionId) : undefined;
114
+ // 处理初始化请求
115
+ if (!session && method === "initialize") {
116
+ // 从请求中提取 session cookie
117
+ const sessionCookie = extractSessionFromRequest(req);
118
+ if (sessionCookie) {
119
+ logger.info("从请求中提取到 session cookie" + sessionCookie);
120
+ }
121
+ else {
122
+ logger.warn("未从请求中提取到 session cookie,将使用未认证模式");
123
+ }
124
+ // 创建新会话(但不立即 connect)
125
+ const newSession = sessionManager.createSession(sessionCookie);
126
+ try {
127
+ // 关键:先连接 server 到 transport
128
+ // 这必须在 handleRequest 之前完成
129
+ await newSession.server.connect(newSession.transport);
130
+ logger.debug(`Server 已连接到 transport: ${newSession.sessionId}`);
131
+ // 现在处理 initialize 请求
132
+ // handleRequest 内部会调用 server 的 initialize 方法
133
+ res.setHeader("mcp-session-id", newSession.sessionId);
134
+ await newSession.transport.handleRequest(req, res, body);
135
+ // 标记会话已初始化
136
+ newSession.initialized = true;
137
+ // 在响应头中返回 sessionId
138
+ logger.info(`会话初始化成功: ${newSession.sessionId}`);
139
+ }
140
+ catch (connectError) {
141
+ // 如果连接或初始化失败,清理会话
142
+ sessionManager.removeSession(newSession.sessionId);
143
+ throw connectError;
144
+ }
145
+ return;
146
+ }
147
+ // 处理非初始化请求
148
+ if (session) {
149
+ // 确保会话已初始化
150
+ if (!session.initialized) {
151
+ logger.warn(`会话未初始化,尝试重新初始化: ${session.sessionId}`);
152
+ try {
153
+ await session.server.connect(session.transport);
154
+ session.initialized = true;
155
+ }
156
+ catch (error) {
157
+ logger.error(`重新初始化会话失败: ${error.message}`);
158
+ res.status(400).json({
159
+ error: "Session Not Initialized",
160
+ message: "会话未正确初始化"
161
+ });
162
+ return;
163
+ }
164
+ }
165
+ // 会话存在且已初始化,处理请求
166
+ await session.transport.handleRequest(req, res, body);
167
+ return;
168
+ }
169
+ // 没有会话且不是初始化请求
170
+ res.status(400).json({
171
+ error: "Invalid Request",
172
+ message: "非法的 SessionId 或者非初始化操作"
173
+ });
174
+ }
175
+ catch (error) {
176
+ logger.error(`处理请求时出错: ${error.message}`);
177
+ logger.error(error);
178
+ if (!res.headersSent) {
179
+ res.status(500).json({
180
+ error: "Internal Server Error",
181
+ message: error.message || "服务器内部错误"
182
+ });
183
+ }
184
+ }
185
+ });
186
+ // 启动服务器
187
+ app.listen(port, () => {
188
+ logger.info(`Accoding MCP Server (HTTP模式) 已启动,监听端口: ${port}`);
189
+ logger.info(`访问地址: http://localhost:${port}/mcp`);
190
+ });
191
+ }
192
+ //# sourceMappingURL=http-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-server.js","sourceRoot":"","sources":["../../src/transport/http-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { AccodingBaseService } from "../accoding/accoding-base-service.js";
3
+ /**
4
+ * 创建并配置 MCP Server 实例
5
+ * 为每个会话创建独立的 MCP Server,确保会话隔离
6
+ */
7
+ export declare function createMCPServer(accodingService: AccodingBaseService): McpServer;
@@ -0,0 +1,48 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { readFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { registerProblemResources } from "../mcp/resources/problem-resources.js";
6
+ import { registerContestResources } from "../mcp/resources/contest-resources.js";
7
+ import { registerContestTools } from "../mcp/tools/contest-tools.js";
8
+ import { registerProblemTools } from "../mcp/tools/problem-tools.js";
9
+ import { registerSubmissionTools } from "../mcp/tools/submission-tools.js";
10
+ import { registerUserTools } from "../mcp/tools/user-tools.js";
11
+ import logger from "../utils/logger.js";
12
+ /**
13
+ * 获取 package.json 中的项目元数据
14
+ */
15
+ function getPackageJson() {
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = dirname(__filename);
18
+ const packageJSONPath = join(__dirname, "..", "..", "package.json");
19
+ const packageJSON = JSON.parse(readFileSync(packageJSONPath, "utf-8"));
20
+ return packageJSON;
21
+ }
22
+ /**
23
+ * 创建并配置 MCP Server 实例
24
+ * 为每个会话创建独立的 MCP Server,确保会话隔离
25
+ */
26
+ export function createMCPServer(accodingService) {
27
+ const packageJSON = getPackageJson();
28
+ const server = new McpServer({
29
+ name: "Accoding MCP Server",
30
+ version: packageJSON.version
31
+ });
32
+ //注册所有工具和资源
33
+ try {
34
+ registerProblemTools(server, accodingService);
35
+ registerUserTools(server, accodingService);
36
+ registerContestTools(server, accodingService);
37
+ registerSubmissionTools(server, accodingService);
38
+ registerProblemResources(server, accodingService);
39
+ registerContestResources(server, accodingService);
40
+ logger.debug("所有工具和资源已注册");
41
+ }
42
+ catch (error) {
43
+ logger.error(`注册工具和资源时出错: ${error.message}`);
44
+ throw error;
45
+ }
46
+ return server;
47
+ }
48
+ //# sourceMappingURL=mcp-server-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server-factory.js","sourceRoot":"","sources":["../../src/transport/mcp-server-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC;;GAEG;AACH,SAAS,cAAc;IACnB,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACtC,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;IACpE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;IACvE,OAAO,WAAW,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,eAAoC;IAChE,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QACzB,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,WAAW,CAAC,OAAO;KAC/B,CAAC,CAAC;IAEH,WAAW;IACX,IAAI,CAAC;QACD,oBAAoB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QAC9C,iBAAiB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QAC3C,oBAAoB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QAC9C,uBAAuB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QAEjD,wBAAwB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QAClD,wBAAwB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QAElD,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QAClB,MAAM,CAAC,KAAK,CAAC,eAAe,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7C,MAAM,KAAK,CAAC;IAChB,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { IncomingMessage } from "node:http";
2
+ import { ServerResponse } from "node:http";
3
+ /**
4
+ * 设置 CORS 响应头
5
+ */
6
+ export declare function setCommonHeaders(res: ServerResponse): void;
7
+ /**
8
+ * 从 HTTP 请求中提取 Accoding session cookie
9
+ * 优先级:
10
+ * 1. X-Accoding-Session header(直接传递 cookie 字符串)
11
+ * 2. Cookie header 中解析(查找包含 session 相关的 cookie)
12
+ * 3. 返回 undefined(使用默认行为)
13
+ */
14
+ export declare function extractSessionFromRequest(req: IncomingMessage): string | undefined;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * 设置 CORS 响应头
3
+ */
4
+ export function setCommonHeaders(res) {
5
+ // 允许哪些域(Origin)可以访问该服务
6
+ res.setHeader("Access-Control-Allow-Origin", "*");
7
+ // 允许客户端在跨域请求中使用哪些 HTTP 方法
8
+ res.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, OPTIONS");
9
+ // 指定客户端请求时允许携带的自定义请求头
10
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, mcp-session-id, X-Accoding-Session");
11
+ // 允许前端 JS 访问响应中的哪些自定义头
12
+ res.setHeader("Access-Control-Expose-Headers", "mcp-session-id");
13
+ }
14
+ /**
15
+ * 从 HTTP 请求中提取 Accoding session cookie
16
+ * 优先级:
17
+ * 1. X-Accoding-Session header(直接传递 cookie 字符串)
18
+ * 2. Cookie header 中解析(查找包含 session 相关的 cookie)
19
+ * 3. 返回 undefined(使用默认行为)
20
+ */
21
+ export function extractSessionFromRequest(req) {
22
+ // 优先级1: 从 X-Accoding-Session header 获取
23
+ const customHeader = req.headers["x-accoding-session"];
24
+ if (customHeader && typeof customHeader === "string") {
25
+ return customHeader;
26
+ }
27
+ // 优先级2: 从 Cookie header 中解析
28
+ const cookieHeader = req.headers.cookie;
29
+ if (cookieHeader && typeof cookieHeader === "string") {
30
+ // 解析 Cookie header(格式:key1=value1; key2=value2)
31
+ const cookies = cookieHeader.split(";").map((cookie) => cookie.trim());
32
+ // 查找可能的 session cookie(常见的命名:sessionid, session, accoding_session 等)
33
+ for (const cookie of cookies) {
34
+ const [key, value] = cookie.split("=").map((s) => s.trim());
35
+ if (key &&
36
+ value &&
37
+ (key.toLowerCase().includes("session") ||
38
+ key.toLowerCase().includes("token") ||
39
+ key.toLowerCase().includes("auth"))) {
40
+ // 返回完整的 cookie 字符串(保持原始格式)
41
+ return cookieHeader;
42
+ }
43
+ }
44
+ // 如果没有找到特定的 session cookie,但存在 Cookie header,返回整个 Cookie header
45
+ // 这样可以让 Accoding API 自己解析需要的 cookie
46
+ return cookieHeader;
47
+ }
48
+ // 优先级3: 返回 undefined,使用默认行为
49
+ return undefined;
50
+ }
51
+ //# sourceMappingURL=http-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-utils.js","sourceRoot":"","sources":["../../src/utils/http-utils.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAmB;IAChD,uBAAuB;IACvB,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;IAClD,0BAA0B;IAC1B,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,4BAA4B,CAAC,CAAC;IAC5E,sBAAsB;IACtB,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,kDAAkD,CAAC,CAAC;IAClG,uBAAuB;IACvB,GAAG,CAAC,SAAS,CAAC,+BAA+B,EAAE,gBAAgB,CAAC,CAAC;AACrE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAoB;IAC1D,uCAAuC;IACvC,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACvD,IAAI,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;QACnD,OAAO,YAAY,CAAC;IACxB,CAAC;IAED,4BAA4B;IAC5B,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;IACxC,IAAI,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;QACnD,gDAAgD;QAChD,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAEvE,qEAAqE;QACrE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YAC5D,IACI,GAAG;gBACH,KAAK;gBACL,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC;oBAClC,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC;oBACnC,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EACzC,CAAC;gBACC,2BAA2B;gBAC3B,OAAO,YAAY,CAAC;YACxB,CAAC;QACL,CAAC;QAED,gEAAgE;QAChE,oCAAoC;QACpC,OAAO,YAAY,CAAC;IACxB,CAAC;IAED,4BAA4B;IAC5B,OAAO,SAAS,CAAC;AACrB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "accoding-mcp-server-test-new",
3
3
  "description": "MCP Server for Accoding API-test",
4
- "version": "0.1.0",
4
+ "version": "0.1.1",
5
5
  "author": "",
6
6
  "main": "./build/index.js",
7
7
  "keywords": [
@@ -26,11 +26,13 @@
26
26
  "license": "MIT",
27
27
  "dependencies": {
28
28
  "@modelcontextprotocol/sdk": "^1.8.0",
29
+ "express": "^4.18.2",
29
30
  "minimist": "^1.2.8",
30
31
  "pino": "^9.6.0",
31
32
  "zod": "^3.24.2"
32
33
  },
33
34
  "devDependencies": {
35
+ "@types/express": "^4.17.21",
34
36
  "@types/minimist": "^1.2.5",
35
37
  "@types/node": "^22.14.0",
36
38
  "prettier": "^3.5.3",