@cnbcool/mcp-server 0.4.0 → 0.4.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/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', {
@@ -111,25 +82,10 @@ export default function registerIssueTools(server) {
111
82
  labels,
112
83
  priority
113
84
  });
114
- return {
115
- content: [
116
- {
117
- type: 'text',
118
- text: `Issue created successfully:\n${JSON.stringify(issue, null, 2)}`
119
- }
120
- ]
121
- };
85
+ return formatTextToolResult(JSON.stringify(issue, null, 2), 'create-issue');
122
86
  }
123
87
  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
- };
88
+ return formatToolError(error, 'create-issue');
133
89
  }
134
90
  });
135
91
  server.tool('update-issue', '更新一个 Issue。 如需更新 Issue 标签,需要另外调用 set-issue-labels', {
@@ -151,25 +107,10 @@ export default function registerIssueTools(server) {
151
107
  state,
152
108
  state_reason
153
109
  });
154
- return {
155
- content: [
156
- {
157
- type: 'text',
158
- text: `Issue updated successfully:\n${JSON.stringify(issue, null, 2)}`
159
- }
160
- ]
161
- };
110
+ return formatTextToolResult(JSON.stringify(issue, null, 2), 'update-issue');
162
111
  }
163
112
  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
- };
113
+ return formatToolError(error, 'update-issue');
173
114
  }
174
115
  });
175
116
  server.tool('list-issue-comments', '查询 Issue 评论列表', {
@@ -180,25 +121,10 @@ export default function registerIssueTools(server) {
180
121
  }, async ({ repo, issueId, page, page_size }) => {
181
122
  try {
182
123
  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
- };
124
+ return formatTextToolResult(JSON.stringify(comments, null, 2), 'list-issue-comments');
191
125
  }
192
126
  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
- };
127
+ return formatToolError(error, 'list-issue-comments');
202
128
  }
203
129
  });
204
130
  server.tool('create-issue-comment', '创建一个 Issue 评论', {
@@ -208,25 +134,10 @@ export default function registerIssueTools(server) {
208
134
  }, async ({ repo, issueId, body }) => {
209
135
  try {
210
136
  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
- };
137
+ return formatTextToolResult(JSON.stringify(comment, null, 2), 'create-issue-comment');
219
138
  }
220
139
  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
- };
140
+ return formatToolError(error, 'create-issue-comment');
230
141
  }
231
142
  });
232
143
  server.tool('update-issue-comment', '更新一个 Issue 评论', {
@@ -237,25 +148,10 @@ export default function registerIssueTools(server) {
237
148
  }, async ({ repo, issueId, commentId, body }) => {
238
149
  try {
239
150
  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
- };
151
+ return formatTextToolResult(JSON.stringify(comment, null, 2), 'update-issue-comment');
248
152
  }
249
153
  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
- };
154
+ return formatToolError(error, 'update-issue-comment');
259
155
  }
260
156
  });
261
157
  server.tool('list-issue-labels', '查询指定Issue的标签', {
@@ -264,25 +160,10 @@ export default function registerIssueTools(server) {
264
160
  }, async ({ repo, issueId }) => {
265
161
  try {
266
162
  const labels = await listIssueLabels(repo, issueId);
267
- return {
268
- content: [
269
- {
270
- type: 'text',
271
- text: JSON.stringify(labels, null, 2)
272
- }
273
- ]
274
- };
163
+ return formatTextToolResult(JSON.stringify(labels, null, 2), 'list-issue-labels');
275
164
  }
276
165
  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
- };
166
+ return formatToolError(error, 'list-issue-labels');
286
167
  }
287
168
  });
288
169
  server.tool('add-issue-labels', '为指定Issue添加标签', {
@@ -292,25 +173,10 @@ export default function registerIssueTools(server) {
292
173
  }, async ({ repo, issueId, labels }) => {
293
174
  try {
294
175
  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
- };
176
+ return formatTextToolResult(JSON.stringify(result, null, 2), 'add-issue-labels');
303
177
  }
304
178
  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
- };
179
+ return formatToolError(error, 'add-issue-labels');
314
180
  }
315
181
  });
316
182
  server.tool('set-issue-labels', '设置Issue的标签(替换所有现有标签)', {
@@ -320,25 +186,10 @@ export default function registerIssueTools(server) {
320
186
  }, async ({ repo, issueId, labels }) => {
321
187
  try {
322
188
  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
- };
189
+ return formatTextToolResult(JSON.stringify(result, null, 2), 'set-issue-labels');
331
190
  }
332
191
  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
- };
192
+ return formatToolError(error, 'set-issue-labels');
342
193
  }
343
194
  });
344
195
  server.tool('delete-issue-labels', '删除Issue的所有标签', {
@@ -347,25 +198,10 @@ export default function registerIssueTools(server) {
347
198
  }, async ({ repo, issueId }) => {
348
199
  try {
349
200
  await deleteIssueLabels(repo, issueId);
350
- return {
351
- content: [
352
- {
353
- type: 'text',
354
- text: 'All labels deleted successfully'
355
- }
356
- ]
357
- };
201
+ return formatTextToolResult('All labels deleted', 'delete-issue-labels');
358
202
  }
359
203
  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
- };
204
+ return formatToolError(error, 'delete-issue-labels');
369
205
  }
370
206
  });
371
207
  server.tool('delete-issue-label', '删除Issue的指定标签', {
@@ -375,25 +211,10 @@ export default function registerIssueTools(server) {
375
211
  }, async ({ repo, issueId, labelName }) => {
376
212
  try {
377
213
  await deleteIssueLabel(repo, issueId, labelName);
378
- return {
379
- content: [
380
- {
381
- type: 'text',
382
- text: `Label "${labelName}" deleted successfully`
383
- }
384
- ]
385
- };
214
+ return formatTextToolResult(`${labelName} deleted`, 'delete-issue-label');
386
215
  }
387
216
  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
- };
217
+ return formatToolError(error, 'delete-issue-label');
397
218
  }
398
219
  });
399
220
  }
@@ -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.1",
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",