@cnbcool/mcp-server 0.4.0 → 0.4.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,36 @@
1
+ ## 0.4.0 (May 8, 2025)
2
+
3
+ ### New Features
4
+
5
+ - Supports streamable http server
6
+ - Supports backwards compatibility, sse http server
7
+ - Supports list, update and create pull requests
8
+ - Supports get current repository information
9
+
10
+ ## 0.3.3 (May 7, 2025)
11
+
12
+ ### Bug Fixes
13
+
14
+ - Format repository creation response
15
+ - Format group creation response
16
+
17
+ ## 0.3.2 (May 6, 2025)
18
+
19
+ ### New Features
20
+
21
+ - Supports list groups with various filters
22
+ - Supports list sub-groups with various filters
23
+ - Supports get group details
24
+ - Supports create group
25
+ - Supports create repository
26
+
27
+ ## 0.3.1 (April 23, 2025)
28
+
29
+ ### New Features
30
+
31
+ - Bump to @modelcontextprotocol/sdk 1.10.2
32
+ - Add zod preprocess, convert null to undefined
33
+
1
34
  ## 0.3.0 (April 23, 2025)
2
35
 
3
36
  ### New Features
package/README_zh.md ADDED
@@ -0,0 +1,64 @@
1
+ # CNB MCP Server
2
+
3
+ CNB(https://cnb.cool) 支持 MCP 协议的 MCP Server
4
+
5
+ ## 使用方法
6
+
7
+ ### STDIO
8
+
9
+ ```json
10
+ {
11
+ "mcpServers": {
12
+ "cnb": {
13
+ "command": "npx",
14
+ "args": ["-y", "-p", "@cnbcool/mcp-server", "cnb-mcp-stdio"],
15
+ "env": {
16
+ "API_BASE_URL": "<BASE_URL>", // 可选,默认值: https://api.cnb.cool
17
+ "API_TOKEN": "<YOUR_TOKEN>"
18
+ }
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ ## 环境要求
25
+
26
+ 1. node >= 18
27
+
28
+ ## 开发指南
29
+
30
+ 1. `npm install`
31
+ 2. `npx openapi-typescript@5.4.2 https://api.cnb.cool/swagger.json -o src/schema.d.ts`
32
+ 3. 复制 `.env.example` 并重命名为 `.env` 并填写相应值
33
+ 4. `npm run build`
34
+ 5. `npx @modelcontextprotocol/inspector node dist/stdio.js`
35
+
36
+ > @modelcontextprotocol/inspector 需要 Node.js: ^22.7.5
37
+
38
+ > https://github.com/modelcontextprotocol/inspector?tab=readme-ov-file#requirements
39
+
40
+ ## 预览方法
41
+
42
+ 1. npm run build
43
+ 2. 设置 mcpServers 配置:
44
+
45
+ ```json
46
+ {
47
+ "mcpServers": {
48
+ "cnb": {
49
+ "command": "node",
50
+ "args": ["/path/to/cnbcool/mcp-server/dist/stdio.js"],
51
+ "env": {
52
+ "API_BASE_URL": "<BASE_URL>", // optional, defualt vaule: https://api.cnb.cool
53
+ "API_TOKEN": "<YOUR_TOKEN>"
54
+ }
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ ### 使用CodeBuddy自动根据swagger.json补充接口MCP Tools
61
+
62
+ 1. `wget https://api.cnb.cool/swagger.json -O ./swagger.json`
63
+ 2. `npx split-swagger s ./swagger.json ./swagger`
64
+ 3. 参考prompt.txt中提示词,调整为有变动的json文件,然后让ai代码助手执行
@@ -0,0 +1,21 @@
1
+ export function formatTextToolResult(text, toolName) {
2
+ return {
3
+ content: [
4
+ {
5
+ type: 'text',
6
+ text
7
+ }
8
+ ]
9
+ };
10
+ }
11
+ export function formatToolError(error, toolName) {
12
+ return {
13
+ content: [
14
+ {
15
+ type: 'text',
16
+ text: `Error ${toolName}:\n${error instanceof Error ? error.message : String(error)}`
17
+ }
18
+ ],
19
+ isError: true
20
+ };
21
+ }
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { createGroup, getGroup, listGroups, listSubGroups } from '../api/group.js';
3
+ import { formatTextToolResult, formatToolError } from '../helpers/formatToolResult.js';
3
4
  export default function registerGroupTools(server) {
4
5
  server.tool('list-groups', '获取当前用户拥有权限的顶层组织列表', {
5
6
  page: z.number().default(1).describe('第几页,从1开始'),
@@ -10,26 +11,11 @@ export default function registerGroupTools(server) {
10
11
  .describe('最小仓库权限')
11
12
  }, async ({ page, page_size, search, role }) => {
12
13
  try {
13
- const repos = await listGroups({ page, page_size, search, role });
14
- return {
15
- content: [
16
- {
17
- type: 'text',
18
- text: JSON.stringify(repos, null, 2)
19
- }
20
- ]
21
- };
14
+ const groups = await listGroups({ page, page_size, search, role });
15
+ return formatTextToolResult(JSON.stringify(groups, null, 2), 'list-groups');
22
16
  }
23
17
  catch (error) {
24
- return {
25
- content: [
26
- {
27
- type: 'text',
28
- text: `Error listing repositories: ${error instanceof Error ? error.message : String(error)}`
29
- }
30
- ],
31
- isError: true
32
- };
18
+ return formatToolError(error, 'list-groups');
33
19
  }
34
20
  });
35
21
  server.tool('list-sub-groups', '查询当前用户在指定组织下拥有指定权限的子组织列表', {
@@ -40,51 +26,21 @@ export default function registerGroupTools(server) {
40
26
  }, async ({ group, page, page_size, access }) => {
41
27
  try {
42
28
  const subGroups = await listSubGroups(group, { page, page_size, access });
43
- return {
44
- content: [
45
- {
46
- type: 'text',
47
- text: JSON.stringify(subGroups, null, 2)
48
- }
49
- ]
50
- };
29
+ return formatTextToolResult(JSON.stringify(subGroups, null, 2), 'list-sub-groups');
51
30
  }
52
31
  catch (error) {
53
- return {
54
- content: [
55
- {
56
- type: 'text',
57
- text: `Error listing repositories: ${error instanceof Error ? error.message : String(error)}`
58
- }
59
- ],
60
- isError: true
61
- };
32
+ return formatToolError(error, 'list-sub-groups');
62
33
  }
63
34
  });
64
35
  server.tool('get-group', '获取指定组织信息', {
65
36
  group: z.string().describe('组织路径')
66
37
  }, async ({ group }) => {
67
38
  try {
68
- const data = await getGroup(group);
69
- return {
70
- content: [
71
- {
72
- type: 'text',
73
- text: JSON.stringify(data, null, 2)
74
- }
75
- ]
76
- };
39
+ const groupInfo = await getGroup(group);
40
+ return formatTextToolResult(JSON.stringify(groupInfo, null, 2), 'get-group');
77
41
  }
78
42
  catch (error) {
79
- return {
80
- content: [
81
- {
82
- type: 'text',
83
- text: `Error getting repository: ${error instanceof Error ? error.message : String(error)}`
84
- }
85
- ],
86
- isError: true
87
- };
43
+ return formatToolError(error, 'get-group');
88
44
  }
89
45
  });
90
46
  server.tool('create-group', '创建新组织', {
@@ -97,25 +53,10 @@ export default function registerGroupTools(server) {
97
53
  }, async ({ path, description, remark, bind_domain }) => {
98
54
  try {
99
55
  const data = await createGroup({ path, description, remark, bind_domain });
100
- return {
101
- content: [
102
- {
103
- type: 'text',
104
- text: JSON.stringify(data, null, 2)
105
- }
106
- ]
107
- };
56
+ return formatTextToolResult(JSON.stringify(data, null, 2), 'create-group');
108
57
  }
109
58
  catch (error) {
110
- return {
111
- content: [
112
- {
113
- type: 'text',
114
- text: `Error creating repository: ${error instanceof Error ? error.message : String(error)}`
115
- }
116
- ],
117
- isError: true
118
- };
59
+ return formatToolError(error, 'create-group');
119
60
  }
120
61
  });
121
62
  }
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { createIssue, createIssueComment, getIssue, listIssueComments, listIssues, updateIssue, updateIssueComment, listIssueLabels, addIssueLabels, setIssueLabels, deleteIssueLabels, deleteIssueLabel } from '../api/issue.js';
3
+ import { formatTextToolResult, formatToolError } from '../helpers/formatToolResult.js';
3
4
  export default function registerIssueTools(server) {
4
5
  server.tool('list-issues', '查询仓库的 Issues', {
5
6
  repo: z.string().describe('仓库路径'),
@@ -43,25 +44,10 @@ export default function registerIssueTools(server) {
43
44
  updated_time_end,
44
45
  order_by
45
46
  });
46
- return {
47
- content: [
48
- {
49
- type: 'text',
50
- text: JSON.stringify(issues, null, 2)
51
- }
52
- ]
53
- };
47
+ return formatTextToolResult(JSON.stringify(issues, null, 2), 'list-issues');
54
48
  }
55
49
  catch (error) {
56
- return {
57
- content: [
58
- {
59
- type: 'text',
60
- text: `Error listing issues: \n${error instanceof Error ? error.message : String(error)}`
61
- }
62
- ],
63
- isError: true
64
- };
50
+ return formatToolError(error, 'list-issues');
65
51
  }
66
52
  });
67
53
  server.tool('get-issue', '获取指定 Issue 信息', {
@@ -70,25 +56,10 @@ export default function registerIssueTools(server) {
70
56
  }, async ({ repo, issueId }) => {
71
57
  try {
72
58
  const issues = await getIssue(repo, issueId);
73
- return {
74
- content: [
75
- {
76
- type: 'text',
77
- text: JSON.stringify(issues, null, 2)
78
- }
79
- ]
80
- };
59
+ return formatTextToolResult(JSON.stringify(issues, null, 2), 'get-issue');
81
60
  }
82
61
  catch (error) {
83
- return {
84
- content: [
85
- {
86
- type: 'text',
87
- text: `Error listing issues: \n${error instanceof Error ? error.message : String(error)}`
88
- }
89
- ],
90
- isError: true
91
- };
62
+ return formatToolError(error, 'get-issue');
92
63
  }
93
64
  });
94
65
  server.tool('create-issue', '创建一个 Issue. 如需添加 Issue 标签,需要另外调用 add-issue-labels', {
@@ -101,35 +72,28 @@ export default function registerIssueTools(server) {
101
72
  labels: z
102
73
  .preprocess((val) => (val === null ? undefined : val), z.array(z.string()).optional())
103
74
  .describe('一个或多个 Issue 标签'),
104
- priority: z.preprocess((val) => (val === null ? undefined : val), z.string().optional()).describe('Issue 优先级')
105
- }, async ({ repo, title, body, assignees, labels, priority }) => {
75
+ priority: z.preprocess((val) => (val === null ? undefined : val), z.string().optional()).describe('Issue 优先级'),
76
+ end_date: z
77
+ .preprocess((val) => (val === null ? undefined : val), z.string().optional())
78
+ .describe('Issue 截止时间,格式为 YYYY-MM-DD'),
79
+ start_date: z
80
+ .preprocess((val) => (val === null ? undefined : val), z.string().optional())
81
+ .describe('Issue 起始时间,格式为 YYYY-MM-DD')
82
+ }, async ({ repo, title, body, assignees, labels, priority, end_date, start_date }) => {
106
83
  try {
107
84
  const issue = await createIssue(repo, {
108
85
  title,
109
86
  body,
110
87
  assignees,
111
88
  labels,
112
- priority
89
+ priority,
90
+ end_date,
91
+ start_date
113
92
  });
114
- return {
115
- content: [
116
- {
117
- type: 'text',
118
- text: `Issue created successfully:\n${JSON.stringify(issue, null, 2)}`
119
- }
120
- ]
121
- };
93
+ return formatTextToolResult(JSON.stringify(issue, null, 2), 'create-issue');
122
94
  }
123
95
  catch (error) {
124
- return {
125
- content: [
126
- {
127
- type: 'text',
128
- text: `Error creating issue: \n${error instanceof Error ? error.message : String(error)}`
129
- }
130
- ],
131
- isError: true
132
- };
96
+ return formatToolError(error, 'create-issue');
133
97
  }
134
98
  });
135
99
  server.tool('update-issue', '更新一个 Issue。 如需更新 Issue 标签,需要另外调用 set-issue-labels', {
@@ -138,38 +102,31 @@ export default function registerIssueTools(server) {
138
102
  title: z.preprocess((val) => (val === null ? undefined : val), z.string().optional()).describe('Issue 标题'),
139
103
  body: z.preprocess((val) => (val === null ? undefined : val), z.string().optional()).describe('Issue 描述'),
140
104
  priority: z.preprocess((val) => (val === null ? undefined : val), z.string().optional()).describe('Issue 优先级'),
105
+ end_date: z
106
+ .preprocess((val) => (val === null ? undefined : val), z.string().optional())
107
+ .describe('Issue 截止时间,格式为 YYYY-MM-DD'),
108
+ start_date: z
109
+ .preprocess((val) => (val === null ? undefined : val), z.string().optional())
110
+ .describe('Issue 起始时间,格式为 YYYY-MM-DD'),
141
111
  state: z.preprocess((val) => (val === null ? undefined : val), z.string().optional()).describe('Issue 状态'),
142
112
  state_reason: z
143
113
  .preprocess((val) => (val === null ? undefined : val), z.enum(['completed', 'not_planned', 'reopened']).optional())
144
114
  .describe('Issue 状态原因')
145
- }, async ({ repo, issueId, title, body, priority, state, state_reason }) => {
115
+ }, async ({ repo, issueId, title, body, priority, end_date, start_date, state, state_reason }) => {
146
116
  try {
147
117
  const issue = await updateIssue(repo, issueId, {
148
118
  title,
149
119
  body,
150
120
  priority,
121
+ end_date,
122
+ start_date,
151
123
  state,
152
124
  state_reason
153
125
  });
154
- return {
155
- content: [
156
- {
157
- type: 'text',
158
- text: `Issue updated successfully:\n${JSON.stringify(issue, null, 2)}`
159
- }
160
- ]
161
- };
126
+ return formatTextToolResult(JSON.stringify(issue, null, 2), 'update-issue');
162
127
  }
163
128
  catch (error) {
164
- return {
165
- content: [
166
- {
167
- type: 'text',
168
- text: `Error updating issue: \n${error instanceof Error ? error.message : String(error)}`
169
- }
170
- ],
171
- isError: true
172
- };
129
+ return formatToolError(error, 'update-issue');
173
130
  }
174
131
  });
175
132
  server.tool('list-issue-comments', '查询 Issue 评论列表', {
@@ -180,25 +137,10 @@ export default function registerIssueTools(server) {
180
137
  }, async ({ repo, issueId, page, page_size }) => {
181
138
  try {
182
139
  const comments = await listIssueComments(repo, issueId, { page, page_size });
183
- return {
184
- content: [
185
- {
186
- type: 'text',
187
- text: JSON.stringify(comments, null, 2)
188
- }
189
- ]
190
- };
140
+ return formatTextToolResult(JSON.stringify(comments, null, 2), 'list-issue-comments');
191
141
  }
192
142
  catch (error) {
193
- return {
194
- content: [
195
- {
196
- type: 'text',
197
- text: `Error listing issue comments: \n${error instanceof Error ? error.message : String(error)}`
198
- }
199
- ],
200
- isError: true
201
- };
143
+ return formatToolError(error, 'list-issue-comments');
202
144
  }
203
145
  });
204
146
  server.tool('create-issue-comment', '创建一个 Issue 评论', {
@@ -208,25 +150,10 @@ export default function registerIssueTools(server) {
208
150
  }, async ({ repo, issueId, body }) => {
209
151
  try {
210
152
  const comment = await createIssueComment(repo, issueId, { body });
211
- return {
212
- content: [
213
- {
214
- type: 'text',
215
- text: `Comment created successfully:\n${JSON.stringify(comment, null, 2)}`
216
- }
217
- ]
218
- };
153
+ return formatTextToolResult(JSON.stringify(comment, null, 2), 'create-issue-comment');
219
154
  }
220
155
  catch (error) {
221
- return {
222
- content: [
223
- {
224
- type: 'text',
225
- text: `Error creating comment: \n${error instanceof Error ? error.message : String(error)}`
226
- }
227
- ],
228
- isError: true
229
- };
156
+ return formatToolError(error, 'create-issue-comment');
230
157
  }
231
158
  });
232
159
  server.tool('update-issue-comment', '更新一个 Issue 评论', {
@@ -237,25 +164,10 @@ export default function registerIssueTools(server) {
237
164
  }, async ({ repo, issueId, commentId, body }) => {
238
165
  try {
239
166
  const comment = await updateIssueComment(repo, issueId, commentId, { body });
240
- return {
241
- content: [
242
- {
243
- type: 'text',
244
- text: `Comment updated successfully:\n${JSON.stringify(comment, null, 2)}`
245
- }
246
- ]
247
- };
167
+ return formatTextToolResult(JSON.stringify(comment, null, 2), 'update-issue-comment');
248
168
  }
249
169
  catch (error) {
250
- return {
251
- content: [
252
- {
253
- type: 'text',
254
- text: `Error updating comment: \n${error instanceof Error ? error.message : String(error)}`
255
- }
256
- ],
257
- isError: true
258
- };
170
+ return formatToolError(error, 'update-issue-comment');
259
171
  }
260
172
  });
261
173
  server.tool('list-issue-labels', '查询指定Issue的标签', {
@@ -264,25 +176,10 @@ export default function registerIssueTools(server) {
264
176
  }, async ({ repo, issueId }) => {
265
177
  try {
266
178
  const labels = await listIssueLabels(repo, issueId);
267
- return {
268
- content: [
269
- {
270
- type: 'text',
271
- text: JSON.stringify(labels, null, 2)
272
- }
273
- ]
274
- };
179
+ return formatTextToolResult(JSON.stringify(labels, null, 2), 'list-issue-labels');
275
180
  }
276
181
  catch (error) {
277
- return {
278
- content: [
279
- {
280
- type: 'text',
281
- text: `Error listing issue labels: \n${error instanceof Error ? error.message : String(error)}`
282
- }
283
- ],
284
- isError: true
285
- };
182
+ return formatToolError(error, 'list-issue-labels');
286
183
  }
287
184
  });
288
185
  server.tool('add-issue-labels', '为指定Issue添加标签', {
@@ -292,25 +189,10 @@ export default function registerIssueTools(server) {
292
189
  }, async ({ repo, issueId, labels }) => {
293
190
  try {
294
191
  const result = await addIssueLabels(repo, issueId, labels);
295
- return {
296
- content: [
297
- {
298
- type: 'text',
299
- text: `Labels added successfully:\n${JSON.stringify(result, null, 2)}`
300
- }
301
- ]
302
- };
192
+ return formatTextToolResult(JSON.stringify(result, null, 2), 'add-issue-labels');
303
193
  }
304
194
  catch (error) {
305
- return {
306
- content: [
307
- {
308
- type: 'text',
309
- text: `Error adding labels: \n${error instanceof Error ? error.message : String(error)}`
310
- }
311
- ],
312
- isError: true
313
- };
195
+ return formatToolError(error, 'add-issue-labels');
314
196
  }
315
197
  });
316
198
  server.tool('set-issue-labels', '设置Issue的标签(替换所有现有标签)', {
@@ -320,25 +202,10 @@ export default function registerIssueTools(server) {
320
202
  }, async ({ repo, issueId, labels }) => {
321
203
  try {
322
204
  const result = await setIssueLabels(repo, issueId, labels);
323
- return {
324
- content: [
325
- {
326
- type: 'text',
327
- text: `Labels set successfully:\n${JSON.stringify(result, null, 2)}`
328
- }
329
- ]
330
- };
205
+ return formatTextToolResult(JSON.stringify(result, null, 2), 'set-issue-labels');
331
206
  }
332
207
  catch (error) {
333
- return {
334
- content: [
335
- {
336
- type: 'text',
337
- text: `Error setting labels: \n${error instanceof Error ? error.message : String(error)}`
338
- }
339
- ],
340
- isError: true
341
- };
208
+ return formatToolError(error, 'set-issue-labels');
342
209
  }
343
210
  });
344
211
  server.tool('delete-issue-labels', '删除Issue的所有标签', {
@@ -347,25 +214,10 @@ export default function registerIssueTools(server) {
347
214
  }, async ({ repo, issueId }) => {
348
215
  try {
349
216
  await deleteIssueLabels(repo, issueId);
350
- return {
351
- content: [
352
- {
353
- type: 'text',
354
- text: 'All labels deleted successfully'
355
- }
356
- ]
357
- };
217
+ return formatTextToolResult('All labels deleted', 'delete-issue-labels');
358
218
  }
359
219
  catch (error) {
360
- return {
361
- content: [
362
- {
363
- type: 'text',
364
- text: `Error deleting labels: \n${error instanceof Error ? error.message : String(error)}`
365
- }
366
- ],
367
- isError: true
368
- };
220
+ return formatToolError(error, 'delete-issue-labels');
369
221
  }
370
222
  });
371
223
  server.tool('delete-issue-label', '删除Issue的指定标签', {
@@ -375,25 +227,10 @@ export default function registerIssueTools(server) {
375
227
  }, async ({ repo, issueId, labelName }) => {
376
228
  try {
377
229
  await deleteIssueLabel(repo, issueId, labelName);
378
- return {
379
- content: [
380
- {
381
- type: 'text',
382
- text: `Label "${labelName}" deleted successfully`
383
- }
384
- ]
385
- };
230
+ return formatTextToolResult(`${labelName} deleted`, 'delete-issue-label');
386
231
  }
387
232
  catch (error) {
388
- return {
389
- content: [
390
- {
391
- type: 'text',
392
- text: `Error deleting label: \n${error instanceof Error ? error.message : String(error)}`
393
- }
394
- ],
395
- isError: true
396
- };
233
+ return formatToolError(error, 'delete-issue-label');
397
234
  }
398
235
  });
399
236
  }
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { listPulls, getPull, createPull, updatePull, mergePull, listPullComments, createPullComment } from '../api/pull.js';
3
+ import { formatTextToolResult, formatToolError } from '../helpers/formatToolResult.js';
3
4
  export default function registerPullTools(server) {
4
5
  server.tool('list-pulls', '查询仓库的Pull Requests', {
5
6
  repo: z.string().describe('仓库路径,格式为 {group}/{repo}'),
@@ -17,25 +18,10 @@ export default function registerPullTools(server) {
17
18
  }, async ({ repo, ...params }) => {
18
19
  try {
19
20
  const pulls = await listPulls(repo, params);
20
- return {
21
- content: [
22
- {
23
- type: 'text',
24
- text: JSON.stringify(pulls, null, 2)
25
- }
26
- ]
27
- };
21
+ return formatTextToolResult(JSON.stringify(pulls, null, 2), 'list-pulls');
28
22
  }
29
23
  catch (error) {
30
- return {
31
- content: [
32
- {
33
- type: 'text',
34
- text: `Error listing pulls: \n${error instanceof Error ? error.message : String(error)}`
35
- }
36
- ],
37
- isError: true
38
- };
24
+ return formatToolError(error, 'list-pulls');
39
25
  }
40
26
  });
41
27
  server.tool('get-pull', '获取单个Pull Request详情', {
@@ -44,25 +30,10 @@ export default function registerPullTools(server) {
44
30
  }, async ({ repo, number }) => {
45
31
  try {
46
32
  const pull = await getPull(repo, number);
47
- return {
48
- content: [
49
- {
50
- type: 'text',
51
- text: JSON.stringify(pull, null, 2)
52
- }
53
- ]
54
- };
33
+ return formatTextToolResult(JSON.stringify(pull, null, 2), 'get-pull');
55
34
  }
56
35
  catch (error) {
57
- return {
58
- content: [
59
- {
60
- type: 'text',
61
- text: `Error getting pull request: \n${error instanceof Error ? error.message : String(error)}`
62
- }
63
- ],
64
- isError: true
65
- };
36
+ return formatToolError(error, 'get-pull');
66
37
  }
67
38
  });
68
39
  server.tool('create-pull', '创建Pull Request', {
@@ -75,25 +46,10 @@ export default function registerPullTools(server) {
75
46
  }, async ({ repo, ...params }) => {
76
47
  try {
77
48
  const pull = await createPull(repo, params);
78
- return {
79
- content: [
80
- {
81
- type: 'text',
82
- text: JSON.stringify(pull, null, 2)
83
- }
84
- ]
85
- };
49
+ return formatTextToolResult(JSON.stringify(pull, null, 2), 'create-pull');
86
50
  }
87
51
  catch (error) {
88
- return {
89
- content: [
90
- {
91
- type: 'text',
92
- text: `Error creating pull request: \n${error instanceof Error ? error.message : String(error)}`
93
- }
94
- ],
95
- isError: true
96
- };
52
+ return formatToolError(error, 'create-pull');
97
53
  }
98
54
  });
99
55
  server.tool('update-pull', '更新Pull Request', {
@@ -107,25 +63,10 @@ export default function registerPullTools(server) {
107
63
  }, async ({ repo, number, ...params }) => {
108
64
  try {
109
65
  const pull = await updatePull(repo, number, params);
110
- return {
111
- content: [
112
- {
113
- type: 'text',
114
- text: JSON.stringify(pull, null, 2)
115
- }
116
- ]
117
- };
66
+ return formatTextToolResult(JSON.stringify(pull, null, 2), 'update-pull');
118
67
  }
119
68
  catch (error) {
120
- return {
121
- content: [
122
- {
123
- type: 'text',
124
- text: `Error updating pull request: \n${error instanceof Error ? error.message : String(error)}`
125
- }
126
- ],
127
- isError: true
128
- };
69
+ return formatToolError(error, 'update-pull');
129
70
  }
130
71
  });
131
72
  server.tool('merge-pull', '合并Pull Request', {
@@ -141,25 +82,10 @@ export default function registerPullTools(server) {
141
82
  }, async ({ repo, number, ...params }) => {
142
83
  try {
143
84
  const result = await mergePull(repo, number, params);
144
- return {
145
- content: [
146
- {
147
- type: 'text',
148
- text: JSON.stringify(result, null, 2)
149
- }
150
- ]
151
- };
85
+ return formatTextToolResult(JSON.stringify(result, null, 2), 'merge-pull');
152
86
  }
153
87
  catch (error) {
154
- return {
155
- content: [
156
- {
157
- type: 'text',
158
- text: `Error merging pull request: \n${error instanceof Error ? error.message : String(error)}`
159
- }
160
- ],
161
- isError: true
162
- };
88
+ return formatToolError(error, 'merge-pull');
163
89
  }
164
90
  });
165
91
  server.tool('list-pull-comments', '列出Pull Request的评论', {
@@ -170,25 +96,10 @@ export default function registerPullTools(server) {
170
96
  }, async ({ repo, number, ...params }) => {
171
97
  try {
172
98
  const comments = await listPullComments(repo, number, params);
173
- return {
174
- content: [
175
- {
176
- type: 'text',
177
- text: JSON.stringify(comments, null, 2)
178
- }
179
- ]
180
- };
99
+ return formatTextToolResult(JSON.stringify(comments, null, 2), 'list-pull-comments');
181
100
  }
182
101
  catch (error) {
183
- return {
184
- content: [
185
- {
186
- type: 'text',
187
- text: `Error listing pull request comments: \n${error instanceof Error ? error.message : String(error)}`
188
- }
189
- ],
190
- isError: true
191
- };
102
+ return formatToolError(error, 'list-pull-comments');
192
103
  }
193
104
  });
194
105
  server.tool('create-pull-comment', '创建Pull Request评论', {
@@ -198,25 +109,10 @@ export default function registerPullTools(server) {
198
109
  }, async ({ repo, number, body }) => {
199
110
  try {
200
111
  await createPullComment(repo, number, { body });
201
- return {
202
- content: [
203
- {
204
- type: 'text',
205
- text: JSON.stringify({ message: 'Comment created successfully' }, null, 2)
206
- }
207
- ]
208
- };
112
+ return formatTextToolResult('Comment created', 'create-pull-comment');
209
113
  }
210
114
  catch (error) {
211
- return {
212
- content: [
213
- {
214
- type: 'text',
215
- text: `Error creating pull request comment: \n${error instanceof Error ? error.message : String(error)}`
216
- }
217
- ],
218
- isError: true
219
- };
115
+ return formatToolError(error, 'create-pull-comment');
220
116
  }
221
117
  });
222
118
  }
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { createRepository, getRepository, listGroupRepositories, listRepositories } from '../api/repository.js';
3
3
  import { getUser } from '../api/user.js';
4
+ import { formatTextToolResult, formatToolError } from '../helpers/formatToolResult.js';
4
5
  export default function registerRepoTools(server) {
5
6
  server.tool('list-repositories', '获取当前用户拥有指定权限及其以上权限的仓库', {
6
7
  page: z.number().default(1).describe('第几页,从1开始,默认值是1'),
@@ -21,25 +22,10 @@ export default function registerRepoTools(server) {
21
22
  }, async ({ page, page_size, search, filter_type, role, order_by, desc }) => {
22
23
  try {
23
24
  const repos = await listRepositories({ page, page_size, search, filter_type, role, order_by, desc });
24
- return {
25
- content: [
26
- {
27
- type: 'text',
28
- text: JSON.stringify(repos, null, 2)
29
- }
30
- ]
31
- };
25
+ return formatTextToolResult(JSON.stringify(repos, null, 2), 'list-repositories');
32
26
  }
33
27
  catch (error) {
34
- return {
35
- content: [
36
- {
37
- type: 'text',
38
- text: `Error listing repositories: ${error instanceof Error ? error.message : String(error)}`
39
- }
40
- ],
41
- isError: true
42
- };
28
+ return formatToolError(error, 'list-repositories');
43
29
  }
44
30
  });
45
31
  server.tool('list-group-repositories', '获取分组里当前用户有权限的仓库', {
@@ -70,51 +56,21 @@ export default function registerRepoTools(server) {
70
56
  order_by,
71
57
  desc
72
58
  });
73
- return {
74
- content: [
75
- {
76
- type: 'text',
77
- text: JSON.stringify(repos, null, 2)
78
- }
79
- ]
80
- };
59
+ return formatTextToolResult(JSON.stringify(repos, null, 2), 'list-group-repositories');
81
60
  }
82
61
  catch (error) {
83
- return {
84
- content: [
85
- {
86
- type: 'text',
87
- text: `Error listing repositories: ${error instanceof Error ? error.message : String(error)}`
88
- }
89
- ],
90
- isError: true
91
- };
62
+ return formatToolError(error, 'list-group-repositories');
92
63
  }
93
64
  });
94
65
  server.tool('get-repository', '获取指定仓库信息', {
95
66
  repo: z.string().describe('仓库路径')
96
67
  }, async ({ repo }) => {
97
68
  try {
98
- const data = await getRepository(repo);
99
- return {
100
- content: [
101
- {
102
- type: 'text',
103
- text: JSON.stringify(data, null, 2)
104
- }
105
- ]
106
- };
69
+ const repoInfo = await getRepository(repo);
70
+ return formatTextToolResult(JSON.stringify(repoInfo, null, 2), 'get-repository');
107
71
  }
108
72
  catch (error) {
109
- return {
110
- content: [
111
- {
112
- type: 'text',
113
- text: `Error getting repository: ${error instanceof Error ? error.message : String(error)}`
114
- }
115
- ],
116
- isError: true
117
- };
73
+ return formatToolError(error, 'get-repository');
118
74
  }
119
75
  });
120
76
  server.tool('create-repository', '创建仓库', {
@@ -133,25 +89,10 @@ export default function registerRepoTools(server) {
133
89
  }
134
90
  try {
135
91
  const data = await createRepository(repoGroup, { name, description, license, visibility });
136
- return {
137
- content: [
138
- {
139
- type: 'text',
140
- text: JSON.stringify(data, null, 2)
141
- }
142
- ]
143
- };
92
+ return formatTextToolResult(JSON.stringify(data, null, 2), 'create-repository');
144
93
  }
145
94
  catch (error) {
146
- return {
147
- content: [
148
- {
149
- type: 'text',
150
- text: `Error creating repository: ${error instanceof Error ? error.message : String(error)}`
151
- }
152
- ],
153
- isError: true
154
- };
95
+ return formatToolError(error, 'create-repository');
155
96
  }
156
97
  });
157
98
  server.tool('get-current-repo', '获取当前仓库对应的CNB仓库信息', {
@@ -174,37 +115,14 @@ export default function registerRepoTools(server) {
174
115
  }
175
116
  }
176
117
  if (!repoPath) {
177
- return {
178
- content: [
179
- {
180
- type: 'text',
181
- text: `无法从远程仓库URL解析出仓库路径: ${remote_url}`
182
- }
183
- ],
184
- isError: true
185
- };
118
+ return formatToolError(`无法从远程仓库URL解析出仓库路径: ${remote_url}`, 'get-current-repo');
186
119
  }
187
120
  // 获取仓库信息
188
121
  const data = await getRepository(repoPath);
189
- return {
190
- content: [
191
- {
192
- type: 'text',
193
- text: JSON.stringify(data, null, 2)
194
- }
195
- ]
196
- };
122
+ return formatTextToolResult(JSON.stringify(data, null, 2), 'get-current-repo');
197
123
  }
198
124
  catch (error) {
199
- return {
200
- content: [
201
- {
202
- type: 'text',
203
- text: `Error getting current repository: ${error instanceof Error ? error.message : String(error)}`
204
- }
205
- ],
206
- isError: true
207
- };
125
+ return formatToolError(error, 'get-current-repo');
208
126
  }
209
127
  });
210
128
  }
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { deleteWorkspace, listWorkspace } from '../api/workspace.js';
3
+ import { formatTextToolResult, formatToolError } from '../helpers/formatToolResult.js';
3
4
  export default function registerWorkspaceTools(server) {
4
5
  server.tool('list-workspace', '获取我的云原生开发环境列表', {
5
6
  branch: z
@@ -34,25 +35,10 @@ export default function registerWorkspaceTools(server) {
34
35
  slug,
35
36
  status
36
37
  });
37
- return {
38
- content: [
39
- {
40
- type: 'text',
41
- text: JSON.stringify(workspaces, null, 2)
42
- }
43
- ]
44
- };
38
+ return formatTextToolResult(JSON.stringify(workspaces, null, 2), 'list-workspace');
45
39
  }
46
40
  catch (error) {
47
- return {
48
- content: [
49
- {
50
- type: 'text',
51
- text: `Error listing workspace: ${error instanceof Error ? error.message : String(error)}`
52
- }
53
- ],
54
- isError: true
55
- };
41
+ return formatToolError(error, 'list-workspace');
56
42
  }
57
43
  });
58
44
  server.tool('delete-workspace', '删除我的云原生开发环境', {
@@ -62,25 +48,10 @@ export default function registerWorkspaceTools(server) {
62
48
  const result = await deleteWorkspace({
63
49
  pipelineId
64
50
  });
65
- return {
66
- content: [
67
- {
68
- type: 'text',
69
- text: JSON.stringify(result, null, 2)
70
- }
71
- ]
72
- };
51
+ return formatTextToolResult(JSON.stringify(result, null, 2), 'delete-workspace');
73
52
  }
74
53
  catch (error) {
75
- return {
76
- content: [
77
- {
78
- type: 'text',
79
- text: `error delete workspace: ${error instanceof Error ? error.message : String(error)}`
80
- }
81
- ],
82
- isError: true
83
- };
54
+ return formatToolError(error, 'delete-workspace');
84
55
  }
85
56
  });
86
57
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cnbcool/mcp-server",
3
3
  "description": "CNB MCP Server. A comprehensive MCP server that provides seamless integration to the CNB's API(https://cnb.cool), offering a wide range of tools for repository management, pipelines operations and collaboration features",
4
- "version": "0.4.0",
4
+ "version": "0.4.2",
5
5
  "main": "./dist/stdio.js",
6
6
  "bin": {
7
7
  "cnb-mcp-stdio": "dist/stdio.js",
@@ -33,7 +33,8 @@
33
33
  "dist/",
34
34
  "package.json",
35
35
  "CHANGELOG.md",
36
- "README.md"
36
+ "README.md",
37
+ "README_zh.md"
37
38
  ],
38
39
  "engines": {
39
40
  "node": ">= 18",