@autobest-ui/agent 1.0.1 → 1.0.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/README.md +3 -3
- package/mcp/azurepr-mcp-bridge/azure-devops.js +82 -112
- package/mcp/azurepr-mcp-bridge/index.js +21 -25
- package/mcp/azurepr-mcp-bridge/index.test.js +50 -59
- package/mcp/figma-mcp-bridge/src/tools/index.js +782 -361
- package/mcp/figma-mcp-bridge/src/tools/mutations.js +3566 -2160
- package/mcp/figma-mcp-bridge/src/tools/nodes.js +53 -32
- package/mcp/figma-mcp-bridge/src/tools/pages.js +38 -23
- package/mcp/rag-mcp-bridge/README.md +2 -2
- package/mcp/rag-mcp-bridge/config.toml.example +1 -1
- package/mcp/rag-mcp-bridge/index.js +80 -125
- package/mcp/rag-mcp-bridge/index.test.js +21 -25
- package/package.json +1 -1
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
export const nodesTool = {
|
|
7
7
|
name: 'figma_get_nodes',
|
|
8
|
-
description:
|
|
8
|
+
description:
|
|
9
|
+
'Get detailed information about specific Figma nodes by their IDs. Returns node properties including type, position, size, fills, strokes, auto-layout (including layoutWrap and counterAxisSpacing), clipsContent, node-level boundVariables, explicitVariableModes, and more.',
|
|
9
10
|
inputSchema: {
|
|
10
11
|
type: 'object',
|
|
11
12
|
properties: {
|
|
@@ -22,15 +23,21 @@ export const nodesTool = {
|
|
|
22
23
|
export async function handleGetNodes(bridge, args) {
|
|
23
24
|
if (!bridge.isConnected()) {
|
|
24
25
|
return {
|
|
25
|
-
content: [
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
content: [
|
|
27
|
+
{
|
|
28
|
+
type: 'text',
|
|
29
|
+
text: JSON.stringify(
|
|
30
|
+
{
|
|
31
|
+
error: {
|
|
32
|
+
code: 'NOT_CONNECTED',
|
|
33
|
+
message: 'Figma plugin is not connected. Please open Figma and run the Autobest Figma Plugin.'
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
null,
|
|
37
|
+
2
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
],
|
|
34
41
|
isError: true
|
|
35
42
|
};
|
|
36
43
|
}
|
|
@@ -39,15 +46,21 @@ export async function handleGetNodes(bridge, args) {
|
|
|
39
46
|
|
|
40
47
|
if (!nodeIds || !Array.isArray(nodeIds) || nodeIds.length === 0) {
|
|
41
48
|
return {
|
|
42
|
-
content: [
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
content: [
|
|
50
|
+
{
|
|
51
|
+
type: 'text',
|
|
52
|
+
text: JSON.stringify(
|
|
53
|
+
{
|
|
54
|
+
error: {
|
|
55
|
+
code: 'INVALID_PARAMS',
|
|
56
|
+
message: 'nodeIds must be a non-empty array of node IDs'
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
null,
|
|
60
|
+
2
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
],
|
|
51
64
|
isError: true
|
|
52
65
|
};
|
|
53
66
|
}
|
|
@@ -56,22 +69,30 @@ export async function handleGetNodes(bridge, args) {
|
|
|
56
69
|
const result = await bridge.sendCommand('get_nodes', { nodeIds, depth });
|
|
57
70
|
|
|
58
71
|
return {
|
|
59
|
-
content: [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
content: [
|
|
73
|
+
{
|
|
74
|
+
type: 'text',
|
|
75
|
+
text: JSON.stringify(result, null, 2)
|
|
76
|
+
}
|
|
77
|
+
]
|
|
63
78
|
};
|
|
64
79
|
} catch (error) {
|
|
65
80
|
return {
|
|
66
|
-
content: [
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
81
|
+
content: [
|
|
82
|
+
{
|
|
83
|
+
type: 'text',
|
|
84
|
+
text: JSON.stringify(
|
|
85
|
+
{
|
|
86
|
+
error: {
|
|
87
|
+
code: error.code || 'UNKNOWN_ERROR',
|
|
88
|
+
message: error.message
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
null,
|
|
92
|
+
2
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
],
|
|
75
96
|
isError: true
|
|
76
97
|
};
|
|
77
98
|
}
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
export const pagesTool = {
|
|
7
7
|
name: 'figma_list_pages',
|
|
8
|
-
description:
|
|
8
|
+
description:
|
|
9
|
+
'List all pages in the current Figma document. Returns page IDs, names, and indicates which page is currently active.',
|
|
9
10
|
inputSchema: {
|
|
10
11
|
type: 'object',
|
|
11
12
|
properties: {},
|
|
@@ -16,15 +17,21 @@ export const pagesTool = {
|
|
|
16
17
|
export async function handleListPages(bridge) {
|
|
17
18
|
if (!bridge.isConnected()) {
|
|
18
19
|
return {
|
|
19
|
-
content: [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
content: [
|
|
21
|
+
{
|
|
22
|
+
type: 'text',
|
|
23
|
+
text: JSON.stringify(
|
|
24
|
+
{
|
|
25
|
+
error: {
|
|
26
|
+
code: 'NOT_CONNECTED',
|
|
27
|
+
message: 'Figma plugin is not connected. Please open Figma and run the Autobest Figma Plugin.'
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
null,
|
|
31
|
+
2
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
],
|
|
28
35
|
isError: true
|
|
29
36
|
};
|
|
30
37
|
}
|
|
@@ -33,22 +40,30 @@ export async function handleListPages(bridge) {
|
|
|
33
40
|
const result = await bridge.sendCommand('list_pages', {});
|
|
34
41
|
|
|
35
42
|
return {
|
|
36
|
-
content: [
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
content: [
|
|
44
|
+
{
|
|
45
|
+
type: 'text',
|
|
46
|
+
text: JSON.stringify(result, null, 2)
|
|
47
|
+
}
|
|
48
|
+
]
|
|
40
49
|
};
|
|
41
50
|
} catch (error) {
|
|
42
51
|
return {
|
|
43
|
-
content: [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
content: [
|
|
53
|
+
{
|
|
54
|
+
type: 'text',
|
|
55
|
+
text: JSON.stringify(
|
|
56
|
+
{
|
|
57
|
+
error: {
|
|
58
|
+
code: error.code || 'UNKNOWN_ERROR',
|
|
59
|
+
message: error.message
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
null,
|
|
63
|
+
2
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
],
|
|
52
67
|
isError: true
|
|
53
68
|
};
|
|
54
69
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
先启动 PRD Knowledge HTTP API。直接在终端验证时,必须在同一条命令中提供 API 根地址:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
RAG_API_BASE_URL="http://
|
|
10
|
+
RAG_API_BASE_URL="http://192.168.1.12:3000/api/knowledge" \
|
|
11
11
|
npx --yes \
|
|
12
12
|
--package=@autobest-ui/agent@latest \
|
|
13
13
|
autobest-rag-mcp
|
|
@@ -26,7 +26,7 @@ tool_timeout_sec = 120
|
|
|
26
26
|
enabled = true
|
|
27
27
|
|
|
28
28
|
[mcp_servers.rag-mcp-bridge.env]
|
|
29
|
-
RAG_API_BASE_URL = "http://
|
|
29
|
+
RAG_API_BASE_URL = "http://192.168.1.12:3000/api/knowledge"
|
|
30
30
|
RAG_MCP_HTTP_TIMEOUT_MS = "120000"
|
|
31
31
|
```
|
|
32
32
|
|
|
@@ -7,6 +7,6 @@ enabled = true
|
|
|
7
7
|
|
|
8
8
|
[mcp_servers.rag-mcp-bridge.env]
|
|
9
9
|
# 必填:按实际部署地址修改,MCP 服务本身不提供默认地址。
|
|
10
|
-
RAG_API_BASE_URL = "http://
|
|
10
|
+
RAG_API_BASE_URL = "http://192.168.1.12:3000/api/knowledge"
|
|
11
11
|
# 可选:单次 HTTP 请求超时,单位为毫秒。
|
|
12
12
|
RAG_MCP_HTTP_TIMEOUT_MS = "120000"
|
|
@@ -10,40 +10,37 @@
|
|
|
10
10
|
* Database access, Markdown parsing, embeddings, and RAG stay in Express.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readdir, stat } from
|
|
14
|
-
import path from
|
|
15
|
-
import { McpServer } from
|
|
16
|
-
import { StdioServerTransport } from
|
|
17
|
-
import { z } from
|
|
13
|
+
import { readdir, stat } from 'node:fs/promises';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
16
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
17
|
+
import { z } from 'zod';
|
|
18
18
|
|
|
19
19
|
const DEFAULT_HTTP_TIMEOUT_MS = 120_000;
|
|
20
20
|
|
|
21
21
|
function apiBaseUrlFromEnvironment() {
|
|
22
22
|
const configuredUrl = process.env.RAG_API_BASE_URL?.trim();
|
|
23
23
|
if (!configuredUrl) {
|
|
24
|
-
throw new Error(
|
|
25
|
-
"RAG_API_BASE_URL is required; configure it in the MCP server environment",
|
|
26
|
-
);
|
|
24
|
+
throw new Error('RAG_API_BASE_URL is required; configure it in the MCP server environment');
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
let url;
|
|
30
28
|
try {
|
|
31
29
|
url = new URL(configuredUrl);
|
|
32
30
|
} catch {
|
|
33
|
-
throw new Error(
|
|
31
|
+
throw new Error('RAG_API_BASE_URL must be a valid URL');
|
|
34
32
|
}
|
|
35
|
-
if (url.protocol !==
|
|
36
|
-
throw new Error(
|
|
33
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
34
|
+
throw new Error('RAG_API_BASE_URL must use http or https');
|
|
37
35
|
}
|
|
38
|
-
return url.toString().replace(/\/$/,
|
|
36
|
+
return url.toString().replace(/\/$/, '');
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
function httpTimeoutFromEnvironment() {
|
|
42
|
-
const configuredTimeout =
|
|
43
|
-
process.env.RAG_MCP_HTTP_TIMEOUT_MS ?? String(DEFAULT_HTTP_TIMEOUT_MS);
|
|
40
|
+
const configuredTimeout = process.env.RAG_MCP_HTTP_TIMEOUT_MS ?? String(DEFAULT_HTTP_TIMEOUT_MS);
|
|
44
41
|
const timeout = Number(configuredTimeout);
|
|
45
42
|
if (!Number.isSafeInteger(timeout) || timeout <= 0) {
|
|
46
|
-
throw new Error(
|
|
43
|
+
throw new Error('RAG_MCP_HTTP_TIMEOUT_MS must be a positive integer');
|
|
47
44
|
}
|
|
48
45
|
return timeout;
|
|
49
46
|
}
|
|
@@ -52,31 +49,18 @@ const API_BASE_URL = apiBaseUrlFromEnvironment();
|
|
|
52
49
|
const HTTP_TIMEOUT_MS = httpTimeoutFromEnvironment();
|
|
53
50
|
|
|
54
51
|
const pathsSchema = z
|
|
55
|
-
.union([
|
|
56
|
-
|
|
57
|
-
z.array(z.string().min(1)).min(1),
|
|
58
|
-
])
|
|
59
|
-
.describe("单个Markdown文件、Markdown文件数组或文件夹的绝对路径");
|
|
52
|
+
.union([z.string().min(1), z.array(z.string().min(1)).min(1)])
|
|
53
|
+
.describe('单个Markdown文件、Markdown文件数组或文件夹的绝对路径');
|
|
60
54
|
const platformSchema = z
|
|
61
|
-
.enum([
|
|
62
|
-
.describe(
|
|
63
|
-
const moduleSchema = z
|
|
64
|
-
.string()
|
|
65
|
-
.min(1)
|
|
66
|
-
.optional()
|
|
67
|
-
.describe("可选业务模块标识,例如vehicle-selector");
|
|
55
|
+
.enum(['app', 'california-web', 'shared'])
|
|
56
|
+
.describe('知识所属平台;必须明确指定,防止APP与加州网站需求串库');
|
|
57
|
+
const moduleSchema = z.string().min(1).optional().describe('可选业务模块标识,例如vehicle-selector');
|
|
68
58
|
const pageSchema = z
|
|
69
59
|
.string()
|
|
70
60
|
.min(1)
|
|
71
61
|
.optional()
|
|
72
|
-
.describe(
|
|
73
|
-
|
|
74
|
-
);
|
|
75
|
-
const docKeySchema = z
|
|
76
|
-
.string()
|
|
77
|
-
.min(1)
|
|
78
|
-
.optional()
|
|
79
|
-
.describe("需要限定到单一文档时传knowledge_doc.doc_key绝对路径");
|
|
62
|
+
.describe('业务页面键,例如home、pl、pn、pd、name-search;录入多个文件时不要传,由后端按文件名分别推断');
|
|
63
|
+
const docKeySchema = z.string().min(1).optional().describe('需要限定到单一文档时传knowledge_doc.doc_key绝对路径');
|
|
80
64
|
|
|
81
65
|
/** Recursively collects Markdown files from a directory in stable order. */
|
|
82
66
|
async function scanMarkdownDirectory(directoryPath) {
|
|
@@ -88,7 +72,7 @@ async function scanMarkdownDirectory(directoryPath) {
|
|
|
88
72
|
const entryPath = path.join(directoryPath, entry.name);
|
|
89
73
|
if (entry.isDirectory()) {
|
|
90
74
|
markdownFiles.push(...(await scanMarkdownDirectory(entryPath)));
|
|
91
|
-
} else if (entry.isFile() && path.extname(entry.name).toLowerCase() ===
|
|
75
|
+
} else if (entry.isFile() && path.extname(entry.name).toLowerCase() === '.md') {
|
|
92
76
|
markdownFiles.push(path.resolve(entryPath));
|
|
93
77
|
}
|
|
94
78
|
}
|
|
@@ -101,8 +85,8 @@ async function expandMarkdownPaths(pathsInput) {
|
|
|
101
85
|
const markdownFiles = [];
|
|
102
86
|
|
|
103
87
|
for (const input of inputs) {
|
|
104
|
-
if (typeof input !==
|
|
105
|
-
throw new Error(
|
|
88
|
+
if (typeof input !== 'string' || !input.trim()) {
|
|
89
|
+
throw new Error('paths中的每一项都必须是非空字符串');
|
|
106
90
|
}
|
|
107
91
|
if (!path.isAbsolute(input.trim())) {
|
|
108
92
|
throw new Error(`必须使用绝对路径: ${input}`);
|
|
@@ -112,21 +96,16 @@ async function expandMarkdownPaths(pathsInput) {
|
|
|
112
96
|
const pathStat = await stat(absolutePath);
|
|
113
97
|
if (pathStat.isDirectory()) {
|
|
114
98
|
markdownFiles.push(...(await scanMarkdownDirectory(absolutePath)));
|
|
115
|
-
} else if (
|
|
116
|
-
pathStat.isFile() &&
|
|
117
|
-
path.extname(absolutePath).toLowerCase() === ".md"
|
|
118
|
-
) {
|
|
99
|
+
} else if (pathStat.isFile() && path.extname(absolutePath).toLowerCase() === '.md') {
|
|
119
100
|
markdownFiles.push(absolutePath);
|
|
120
101
|
} else {
|
|
121
102
|
throw new Error(`路径不是Markdown文件或文件夹: ${absolutePath}`);
|
|
122
103
|
}
|
|
123
104
|
}
|
|
124
105
|
|
|
125
|
-
const uniquePaths = [...new Set(markdownFiles)].sort((left, right) =>
|
|
126
|
-
left.localeCompare(right),
|
|
127
|
-
);
|
|
106
|
+
const uniquePaths = [...new Set(markdownFiles)].sort((left, right) => left.localeCompare(right));
|
|
128
107
|
if (uniquePaths.length === 0) {
|
|
129
|
-
throw new Error(
|
|
108
|
+
throw new Error('指定路径中没有找到任何.md文件');
|
|
130
109
|
}
|
|
131
110
|
return uniquePaths;
|
|
132
111
|
}
|
|
@@ -134,10 +113,10 @@ async function expandMarkdownPaths(pathsInput) {
|
|
|
134
113
|
/** Posts JSON to Express and returns its parsed response body. */
|
|
135
114
|
async function postJson(endpoint, body) {
|
|
136
115
|
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
|
137
|
-
method:
|
|
138
|
-
headers: {
|
|
116
|
+
method: 'POST',
|
|
117
|
+
headers: { 'content-type': 'application/json' },
|
|
139
118
|
body: JSON.stringify(body),
|
|
140
|
-
signal: AbortSignal.timeout(HTTP_TIMEOUT_MS)
|
|
119
|
+
signal: AbortSignal.timeout(HTTP_TIMEOUT_MS)
|
|
141
120
|
});
|
|
142
121
|
const responseText = await response.text();
|
|
143
122
|
let responseBody;
|
|
@@ -148,10 +127,7 @@ async function postJson(endpoint, body) {
|
|
|
148
127
|
}
|
|
149
128
|
|
|
150
129
|
if (!response.ok) {
|
|
151
|
-
const details =
|
|
152
|
-
typeof responseBody === "string"
|
|
153
|
-
? responseBody
|
|
154
|
-
: JSON.stringify(responseBody);
|
|
130
|
+
const details = typeof responseBody === 'string' ? responseBody : JSON.stringify(responseBody);
|
|
155
131
|
throw new Error(`Express后端返回HTTP ${response.status}: ${details}`);
|
|
156
132
|
}
|
|
157
133
|
return responseBody;
|
|
@@ -161,12 +137,12 @@ async function postJson(endpoint, body) {
|
|
|
161
137
|
async function getJson(endpoint, query) {
|
|
162
138
|
const url = new URL(`${API_BASE_URL}${endpoint}`);
|
|
163
139
|
for (const [name, value] of Object.entries(query)) {
|
|
164
|
-
if (value === undefined || value === null || value ===
|
|
140
|
+
if (value === undefined || value === null || value === '') continue;
|
|
165
141
|
url.searchParams.set(name, value);
|
|
166
142
|
}
|
|
167
143
|
const response = await fetch(url, {
|
|
168
|
-
method:
|
|
169
|
-
signal: AbortSignal.timeout(HTTP_TIMEOUT_MS)
|
|
144
|
+
method: 'GET',
|
|
145
|
+
signal: AbortSignal.timeout(HTTP_TIMEOUT_MS)
|
|
170
146
|
});
|
|
171
147
|
const responseText = await response.text();
|
|
172
148
|
let responseBody;
|
|
@@ -176,10 +152,7 @@ async function getJson(endpoint, query) {
|
|
|
176
152
|
responseBody = responseText;
|
|
177
153
|
}
|
|
178
154
|
if (!response.ok) {
|
|
179
|
-
const details =
|
|
180
|
-
typeof responseBody === "string"
|
|
181
|
-
? responseBody
|
|
182
|
-
: JSON.stringify(responseBody);
|
|
155
|
+
const details = typeof responseBody === 'string' ? responseBody : JSON.stringify(responseBody);
|
|
183
156
|
throw new Error(`Express后端返回HTTP ${response.status}: ${details}`);
|
|
184
157
|
}
|
|
185
158
|
return responseBody;
|
|
@@ -189,10 +162,10 @@ function toolSuccess(payload) {
|
|
|
189
162
|
return {
|
|
190
163
|
content: [
|
|
191
164
|
{
|
|
192
|
-
type:
|
|
193
|
-
text: JSON.stringify(payload, null, 2)
|
|
194
|
-
}
|
|
195
|
-
]
|
|
165
|
+
type: 'text',
|
|
166
|
+
text: JSON.stringify(payload, null, 2)
|
|
167
|
+
}
|
|
168
|
+
]
|
|
196
169
|
};
|
|
197
170
|
}
|
|
198
171
|
|
|
@@ -200,154 +173,136 @@ function toolError(error) {
|
|
|
200
173
|
const message = error instanceof Error ? error.message : String(error);
|
|
201
174
|
return {
|
|
202
175
|
isError: true,
|
|
203
|
-
content: [{ type:
|
|
176
|
+
content: [{ type: 'text', text: message }]
|
|
204
177
|
};
|
|
205
178
|
}
|
|
206
179
|
|
|
207
180
|
const server = new McpServer(
|
|
208
|
-
{ name:
|
|
181
|
+
{ name: 'rag-mcp-bridge', version: '1.0.0' },
|
|
209
182
|
{
|
|
210
183
|
instructions:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
184
|
+
'Use retrieve_knowledge before implementing requirement-sensitive code. ' +
|
|
185
|
+
'Every tool call requires an explicit platform: app, california-web, or shared. ' +
|
|
186
|
+
'add_prd_file and update_spec_file accept one file, an array of files, or a directory. ' +
|
|
187
|
+
'Only call update_spec_file after the PR is merged and leader review confirms the spec is effective.'
|
|
188
|
+
}
|
|
216
189
|
);
|
|
217
190
|
|
|
218
191
|
server.registerTool(
|
|
219
|
-
|
|
192
|
+
'add_prd_file',
|
|
220
193
|
{
|
|
221
|
-
title:
|
|
194
|
+
title: '录入或更新PRD基线',
|
|
222
195
|
description:
|
|
223
|
-
|
|
196
|
+
'录入/更新PRD基线。paths支持单个md绝对路径、md绝对路径数组或文件夹绝对路径;文件夹会递归扫描全部.md文件。后端负责解析截图并将旧文档标记为作废。',
|
|
224
197
|
inputSchema: {
|
|
225
198
|
paths: pathsSchema,
|
|
226
199
|
platform: platformSchema,
|
|
227
200
|
module: moduleSchema,
|
|
228
|
-
page: pageSchema
|
|
201
|
+
page: pageSchema
|
|
229
202
|
},
|
|
230
203
|
annotations: {
|
|
231
204
|
readOnlyHint: false,
|
|
232
205
|
destructiveHint: false,
|
|
233
206
|
idempotentHint: false,
|
|
234
|
-
openWorldHint: false
|
|
235
|
-
}
|
|
207
|
+
openWorldHint: false
|
|
208
|
+
}
|
|
236
209
|
},
|
|
237
210
|
async ({ paths, platform, module, page }) => {
|
|
238
211
|
try {
|
|
239
212
|
const expandedPaths = await expandMarkdownPaths(paths);
|
|
240
213
|
return toolSuccess(
|
|
241
|
-
await postJson(
|
|
214
|
+
await postJson('/add-prd', {
|
|
242
215
|
paths: expandedPaths,
|
|
243
216
|
platform,
|
|
244
217
|
module,
|
|
245
|
-
page
|
|
246
|
-
})
|
|
218
|
+
page
|
|
219
|
+
})
|
|
247
220
|
);
|
|
248
221
|
} catch (error) {
|
|
249
222
|
return toolError(error);
|
|
250
223
|
}
|
|
251
|
-
}
|
|
224
|
+
}
|
|
252
225
|
);
|
|
253
226
|
|
|
254
227
|
server.registerTool(
|
|
255
|
-
|
|
228
|
+
'update_spec_file',
|
|
256
229
|
{
|
|
257
|
-
title:
|
|
230
|
+
title: '录入迭代变更Spec',
|
|
258
231
|
description:
|
|
259
|
-
|
|
232
|
+
'仅在PR合并且leader review确认生效后调用。paths支持单个spec.md绝对路径、路径数组或文件夹;只需传改动spec。sprintVersion必须递增,数值越大需求越新。',
|
|
260
233
|
inputSchema: {
|
|
261
234
|
paths: pathsSchema,
|
|
262
235
|
sprintVersion: z.number().int().positive(),
|
|
263
236
|
iterationTag: z.string().min(1),
|
|
264
237
|
platform: platformSchema,
|
|
265
238
|
module: moduleSchema,
|
|
266
|
-
page: pageSchema
|
|
239
|
+
page: pageSchema
|
|
267
240
|
},
|
|
268
241
|
annotations: {
|
|
269
242
|
readOnlyHint: false,
|
|
270
243
|
destructiveHint: false,
|
|
271
244
|
idempotentHint: false,
|
|
272
|
-
openWorldHint: false
|
|
273
|
-
}
|
|
245
|
+
openWorldHint: false
|
|
246
|
+
}
|
|
274
247
|
},
|
|
275
248
|
async ({ paths, sprintVersion, iterationTag, platform, module, page }) => {
|
|
276
249
|
try {
|
|
277
250
|
const expandedPaths = await expandMarkdownPaths(paths);
|
|
278
251
|
return toolSuccess(
|
|
279
|
-
await postJson(
|
|
252
|
+
await postJson('/add-spec', {
|
|
280
253
|
paths: expandedPaths,
|
|
281
254
|
sprintVersion,
|
|
282
255
|
iterationTag,
|
|
283
256
|
platform,
|
|
284
257
|
module,
|
|
285
|
-
page
|
|
286
|
-
})
|
|
258
|
+
page
|
|
259
|
+
})
|
|
287
260
|
);
|
|
288
261
|
} catch (error) {
|
|
289
262
|
return toolError(error);
|
|
290
263
|
}
|
|
291
|
-
}
|
|
264
|
+
}
|
|
292
265
|
);
|
|
293
266
|
|
|
294
267
|
server.registerTool(
|
|
295
|
-
|
|
268
|
+
'retrieve_knowledge',
|
|
296
269
|
{
|
|
297
|
-
title:
|
|
270
|
+
title: '检索PRD与迭代Spec',
|
|
298
271
|
description:
|
|
299
|
-
|
|
272
|
+
'Codex编写需求相关代码前调用。后端执行向量+BM25-RRF混合检索并按sprint_version业务排序。enableStruct=true时才生成结构化PRD;includeImages=true时才保留图片并替换为数据库图片URL,两者默认均为false。',
|
|
300
273
|
inputSchema: {
|
|
301
274
|
query: z.string().min(1),
|
|
302
275
|
platform: platformSchema,
|
|
303
|
-
module: moduleSchema.describe(
|
|
304
|
-
|
|
305
|
-
),
|
|
306
|
-
page: pageSchema.describe(
|
|
307
|
-
"可选页面硬过滤,例如首页传home;不传时后端会从明确的查询词自动推断",
|
|
308
|
-
),
|
|
276
|
+
module: moduleSchema.describe('可选业务模块硬过滤,例如vehicle-selector'),
|
|
277
|
+
page: pageSchema.describe('可选页面硬过滤,例如首页传home;不传时后端会从明确的查询词自动推断'),
|
|
309
278
|
docKey: docKeySchema,
|
|
310
|
-
enableStruct: z
|
|
311
|
-
|
|
312
|
-
.default(false)
|
|
313
|
-
.describe("是否调用LLM生成structuredPrd,默认false"),
|
|
314
|
-
includeImages: z
|
|
315
|
-
.boolean()
|
|
316
|
-
.default(false)
|
|
317
|
-
.describe("是否返回可供UI显示的数据库图片URL,默认false"),
|
|
279
|
+
enableStruct: z.boolean().default(false).describe('是否调用LLM生成structuredPrd,默认false'),
|
|
280
|
+
includeImages: z.boolean().default(false).describe('是否返回可供UI显示的数据库图片URL,默认false')
|
|
318
281
|
},
|
|
319
282
|
annotations: {
|
|
320
283
|
readOnlyHint: true,
|
|
321
284
|
destructiveHint: false,
|
|
322
285
|
idempotentHint: true,
|
|
323
|
-
openWorldHint: false
|
|
324
|
-
}
|
|
286
|
+
openWorldHint: false
|
|
287
|
+
}
|
|
325
288
|
},
|
|
326
|
-
async ({
|
|
327
|
-
query,
|
|
328
|
-
platform,
|
|
329
|
-
module,
|
|
330
|
-
page,
|
|
331
|
-
docKey,
|
|
332
|
-
enableStruct = false,
|
|
333
|
-
includeImages = false,
|
|
334
|
-
}) => {
|
|
289
|
+
async ({ query, platform, module, page, docKey, enableStruct = false, includeImages = false }) => {
|
|
335
290
|
try {
|
|
336
291
|
return toolSuccess(
|
|
337
|
-
await getJson(
|
|
292
|
+
await getJson('/search', {
|
|
338
293
|
query: query.trim(),
|
|
339
294
|
platform,
|
|
340
295
|
module,
|
|
341
296
|
page,
|
|
342
297
|
docKey,
|
|
343
298
|
enableStruct: String(enableStruct === true),
|
|
344
|
-
includeImages: String(includeImages === true)
|
|
345
|
-
})
|
|
299
|
+
includeImages: String(includeImages === true)
|
|
300
|
+
})
|
|
346
301
|
);
|
|
347
302
|
} catch (error) {
|
|
348
303
|
return toolError(error);
|
|
349
304
|
}
|
|
350
|
-
}
|
|
305
|
+
}
|
|
351
306
|
);
|
|
352
307
|
|
|
353
308
|
async function main() {
|
|
@@ -355,7 +310,7 @@ async function main() {
|
|
|
355
310
|
await server.connect(transport);
|
|
356
311
|
}
|
|
357
312
|
|
|
358
|
-
main().catch(
|
|
359
|
-
console.error(
|
|
313
|
+
main().catch(error => {
|
|
314
|
+
console.error('Failed to start rag-mcp-bridge:', error);
|
|
360
315
|
process.exitCode = 1;
|
|
361
316
|
});
|