@douyinfe/semi-mcp 1.0.1 → 1.0.3
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 +59 -53
- package/dist/index.js +219 -13
- package/dist/utils/fetch-directory-list.d.ts +11 -1
- package/dist/utils/fetch-file-content.d.ts +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,38 +1,40 @@
|
|
|
1
|
+
[中文](README-zh_CN.md) | [English](README.md)
|
|
2
|
+
|
|
1
3
|
# Semi MCP Server
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
An MCP (Model Context Protocol) server implementation based on the MCP SDK, providing Semi Design component documentation and component list query functionality.
|
|
4
6
|
|
|
5
|
-
##
|
|
7
|
+
## Introduction
|
|
6
8
|
|
|
7
|
-
Semi MCP Server
|
|
9
|
+
Semi MCP Server is an MCP (Model Context Protocol) server that communicates with MCP-compatible clients through stdio transport. It provides functionality to fetch Semi Design component documentation, component lists, and more.
|
|
8
10
|
|
|
9
|
-
##
|
|
11
|
+
## Installation
|
|
10
12
|
|
|
11
|
-
###
|
|
13
|
+
### Global Installation
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
16
|
npm install -g @douyinfe/semi-mcp
|
|
15
17
|
```
|
|
16
18
|
|
|
17
|
-
###
|
|
19
|
+
### Local Installation
|
|
18
20
|
|
|
19
21
|
```bash
|
|
20
22
|
npm install @douyinfe/semi-mcp
|
|
21
23
|
```
|
|
22
24
|
|
|
23
|
-
##
|
|
25
|
+
## Usage
|
|
24
26
|
|
|
25
|
-
###
|
|
27
|
+
### As a Command Line Tool
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
After global installation, you can use it directly:
|
|
28
30
|
|
|
29
31
|
```bash
|
|
30
32
|
semi-mcp
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
###
|
|
35
|
+
### Configuration in MCP Clients
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
Configure in MCP-compatible clients (such as Claude Desktop):
|
|
36
38
|
|
|
37
39
|
```json
|
|
38
40
|
{
|
|
@@ -45,7 +47,7 @@ semi-mcp
|
|
|
45
47
|
}
|
|
46
48
|
```
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
Or if installed globally:
|
|
49
51
|
|
|
50
52
|
```json
|
|
51
53
|
{
|
|
@@ -57,28 +59,29 @@ semi-mcp
|
|
|
57
59
|
}
|
|
58
60
|
```
|
|
59
61
|
|
|
60
|
-
##
|
|
62
|
+
## Features
|
|
61
63
|
|
|
62
|
-
###
|
|
64
|
+
### Tools
|
|
63
65
|
|
|
64
66
|
#### `get_semi_document`
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
Get Semi Design component documentation or component list.
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
- `componentName` (
|
|
70
|
-
- `version` (
|
|
70
|
+
**Parameters:**
|
|
71
|
+
- `componentName` (optional): Component name, e.g., `Button`, `Input`, etc. If not provided, returns the component list
|
|
72
|
+
- `version` (optional): Version number, e.g., `2.89.2-alpha.3`. If not provided, defaults to `latest`
|
|
73
|
+
- `get_path` (optional): If `true`, saves documents to the system temporary directory and returns the path instead of returning document content in the response. Defaults to `false`
|
|
71
74
|
|
|
72
|
-
|
|
75
|
+
**Examples:**
|
|
73
76
|
|
|
74
|
-
|
|
77
|
+
Get component list:
|
|
75
78
|
```json
|
|
76
79
|
{
|
|
77
80
|
"name": "get_semi_document"
|
|
78
81
|
}
|
|
79
82
|
```
|
|
80
83
|
|
|
81
|
-
|
|
84
|
+
Get specific component documentation:
|
|
82
85
|
```json
|
|
83
86
|
{
|
|
84
87
|
"name": "get_semi_document",
|
|
@@ -89,9 +92,9 @@ semi-mcp
|
|
|
89
92
|
}
|
|
90
93
|
```
|
|
91
94
|
|
|
92
|
-
|
|
95
|
+
**Response Format:**
|
|
93
96
|
|
|
94
|
-
|
|
97
|
+
When getting component list:
|
|
95
98
|
```json
|
|
96
99
|
{
|
|
97
100
|
"version": "2.89.2-alpha.3",
|
|
@@ -100,7 +103,7 @@ semi-mcp
|
|
|
100
103
|
}
|
|
101
104
|
```
|
|
102
105
|
|
|
103
|
-
|
|
106
|
+
When getting component documentation:
|
|
104
107
|
```json
|
|
105
108
|
{
|
|
106
109
|
"componentName": "button",
|
|
@@ -113,91 +116,94 @@ semi-mcp
|
|
|
113
116
|
}
|
|
114
117
|
```
|
|
115
118
|
|
|
116
|
-
|
|
119
|
+
**Note:** For large documents (over 888 lines), the tool will automatically save them to a temporary directory and return the file paths instead of content.
|
|
120
|
+
|
|
121
|
+
### Resources
|
|
117
122
|
|
|
118
123
|
#### `semi://components`
|
|
119
124
|
|
|
120
|
-
Semi Design
|
|
125
|
+
Semi Design component list resource.
|
|
121
126
|
|
|
122
|
-
##
|
|
127
|
+
## Development
|
|
123
128
|
|
|
124
|
-
###
|
|
129
|
+
### Requirements
|
|
125
130
|
|
|
126
131
|
- Node.js >= 18.0.0
|
|
127
|
-
- npm
|
|
132
|
+
- npm or yarn
|
|
128
133
|
|
|
129
|
-
###
|
|
134
|
+
### Install Dependencies
|
|
130
135
|
|
|
131
136
|
```bash
|
|
132
137
|
npm install
|
|
133
138
|
```
|
|
134
139
|
|
|
135
|
-
###
|
|
140
|
+
### Build
|
|
136
141
|
|
|
137
|
-
|
|
142
|
+
Build production version:
|
|
138
143
|
|
|
139
144
|
```bash
|
|
140
145
|
npm run build
|
|
141
146
|
```
|
|
142
147
|
|
|
143
|
-
|
|
148
|
+
Development mode (watch for file changes and auto-rebuild):
|
|
144
149
|
|
|
145
150
|
```bash
|
|
146
151
|
npm run dev
|
|
147
152
|
```
|
|
148
153
|
|
|
149
|
-
###
|
|
154
|
+
### Test
|
|
150
155
|
|
|
151
|
-
|
|
156
|
+
Run tests:
|
|
152
157
|
|
|
153
158
|
```bash
|
|
154
159
|
npm test
|
|
155
160
|
```
|
|
156
161
|
|
|
157
|
-
###
|
|
162
|
+
### Run
|
|
158
163
|
|
|
159
|
-
|
|
164
|
+
Run the server after building:
|
|
160
165
|
|
|
161
166
|
```bash
|
|
162
167
|
npm start
|
|
163
168
|
```
|
|
164
169
|
|
|
165
|
-
|
|
170
|
+
Or run the built file directly:
|
|
166
171
|
|
|
167
172
|
```bash
|
|
168
173
|
node dist/index.js
|
|
169
174
|
```
|
|
170
175
|
|
|
171
|
-
##
|
|
176
|
+
## Tech Stack
|
|
172
177
|
|
|
173
|
-
- **TypeScript**:
|
|
174
|
-
- **Rslib**:
|
|
175
|
-
- **@modelcontextprotocol/sdk**: MCP
|
|
178
|
+
- **TypeScript**: Type-safe JavaScript
|
|
179
|
+
- **Rslib**: Fast build tool
|
|
180
|
+
- **@modelcontextprotocol/sdk**: Official MCP SDK
|
|
176
181
|
|
|
177
|
-
##
|
|
182
|
+
## Project Structure
|
|
178
183
|
|
|
179
184
|
```
|
|
180
185
|
semi-mcp/
|
|
181
186
|
├── src/
|
|
182
|
-
│ ├── index.ts #
|
|
183
|
-
│ ├── tools/ #
|
|
187
|
+
│ ├── index.ts # Main entry file
|
|
188
|
+
│ ├── tools/ # Tool definitions
|
|
184
189
|
│ │ ├── index.ts
|
|
185
190
|
│ │ └── get-semi-document.ts
|
|
186
|
-
│ └── utils/ #
|
|
191
|
+
│ └── utils/ # Utility functions
|
|
187
192
|
│ ├── fetch-directory-list.ts
|
|
188
|
-
│
|
|
189
|
-
|
|
193
|
+
│ ├── fetch-file-content.ts
|
|
194
|
+
│ └── get-component-list.ts
|
|
195
|
+
├── tests/ # Test files
|
|
190
196
|
│ └── get-semi-document.test.ts
|
|
191
|
-
├── dist/ #
|
|
197
|
+
├── dist/ # Build output
|
|
192
198
|
├── package.json
|
|
193
199
|
└── README.md
|
|
194
200
|
```
|
|
195
201
|
|
|
196
|
-
##
|
|
202
|
+
## License
|
|
197
203
|
|
|
198
204
|
MIT
|
|
199
205
|
|
|
200
|
-
##
|
|
206
|
+
## Related Links
|
|
201
207
|
|
|
202
|
-
- [Semi Design
|
|
203
|
-
- [Model Context Protocol
|
|
208
|
+
- [Semi Design Official Website](https://semi.design)
|
|
209
|
+
- [Model Context Protocol Documentation](https://modelcontextprotocol.io)
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,79 @@
|
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import { tmpdir } from "os";
|
|
5
8
|
const UNPKG_BASE_URL = 'https://unpkg.com';
|
|
6
9
|
const NPMMIRROR_BASE_URL = 'https://registry.npmmirror.com';
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
function flattenDirectoryStructure(item, result = []) {
|
|
11
|
+
result.push({
|
|
12
|
+
path: item.path,
|
|
13
|
+
type: item.type,
|
|
14
|
+
size: item.size
|
|
15
|
+
});
|
|
16
|
+
if (item.files && Array.isArray(item.files)) for (const file of item.files)flattenDirectoryStructure(file, result);
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
async function fetchNpmMirrorDirectoryRecursive(baseUrl, packageName, version, path, maxDepth = 10) {
|
|
20
|
+
if (maxDepth <= 0) return [];
|
|
21
|
+
const url = `${baseUrl}/${packageName}/${version}/files/${path}/?meta`;
|
|
22
|
+
const response = await fetch(url, {
|
|
23
|
+
headers: {
|
|
24
|
+
Accept: 'application/json'
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
if (!response.ok) throw new Error(`获取目录列表失败: ${response.status} ${response.statusText}`);
|
|
28
|
+
const contentType = response.headers.get('content-type') || '';
|
|
29
|
+
if (!contentType.includes('application/json')) throw new Error(`API 返回了非 JSON 格式: ${contentType}`);
|
|
30
|
+
const data = await response.json();
|
|
31
|
+
const normalizeType = (item)=>{
|
|
32
|
+
const path = item.path;
|
|
33
|
+
if (path.endsWith('/')) return {
|
|
34
|
+
path,
|
|
35
|
+
type: 'directory'
|
|
36
|
+
};
|
|
37
|
+
if (item.type && item.type.includes('/')) return {
|
|
38
|
+
path,
|
|
39
|
+
type: 'file'
|
|
40
|
+
};
|
|
41
|
+
if ('directory' === item.type) return {
|
|
42
|
+
path,
|
|
43
|
+
type: 'directory'
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
path,
|
|
47
|
+
type: 'file'
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
const result = [];
|
|
51
|
+
if (data && 'object' == typeof data && 'files' in data && Array.isArray(data.files)) {
|
|
52
|
+
const promises = [];
|
|
53
|
+
for (const item of data.files){
|
|
54
|
+
const normalized = normalizeType(item);
|
|
55
|
+
result.push(normalized);
|
|
56
|
+
if ('directory' !== normalized.type || item.files && 0 !== item.files.length) {
|
|
57
|
+
if (item.files && Array.isArray(item.files) && item.files.length > 0) {
|
|
58
|
+
const flattened = [];
|
|
59
|
+
flattenDirectoryStructure(item, flattened);
|
|
60
|
+
const subFiles = flattened.filter((f)=>f.path !== normalized.path).map(normalizeType);
|
|
61
|
+
result.push(...subFiles);
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
const subPath = normalized.path.startsWith('/') ? normalized.path.slice(1) : normalized.path;
|
|
65
|
+
promises.push(fetchNpmMirrorDirectoryRecursive(baseUrl, packageName, version, subPath, maxDepth - 1).then((subFiles)=>subFiles.filter((f)=>f.path !== normalized.path)).catch(()=>[]));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (promises.length > 0) {
|
|
69
|
+
const subResults = await Promise.all(promises);
|
|
70
|
+
for (const subFiles of subResults)result.push(...subFiles);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
async function fetchDirectoryListFromSource(baseUrl, packageName, version, path, isNpmMirror = false) {
|
|
76
|
+
if (isNpmMirror) return fetchNpmMirrorDirectoryRecursive(baseUrl, packageName, version, path);
|
|
77
|
+
const url = `${baseUrl}/${packageName}@${version}/${path}/?meta`;
|
|
9
78
|
const response = await fetch(url, {
|
|
10
79
|
headers: {
|
|
11
80
|
Accept: 'application/json'
|
|
@@ -35,15 +104,66 @@ async function fetchFromSource(baseUrl, packageName, version, path, isNpmMirror
|
|
|
35
104
|
};
|
|
36
105
|
};
|
|
37
106
|
if (Array.isArray(data)) return data.map(normalizeType);
|
|
38
|
-
if (data && 'object' == typeof data && 'files' in data
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
107
|
+
if (data && 'object' == typeof data && 'files' in data) {
|
|
108
|
+
const filesData = data;
|
|
109
|
+
if (Array.isArray(filesData.files)) return filesData.files.map(normalizeType);
|
|
110
|
+
}
|
|
111
|
+
if (data && 'object' == typeof data && 'path' in data) {
|
|
112
|
+
const singleItem = data;
|
|
113
|
+
if (singleItem.files && Array.isArray(singleItem.files)) {
|
|
114
|
+
const flattened = [];
|
|
115
|
+
flattenDirectoryStructure(singleItem, flattened);
|
|
116
|
+
return flattened.map(normalizeType);
|
|
117
|
+
}
|
|
118
|
+
return [
|
|
119
|
+
normalizeType(singleItem)
|
|
120
|
+
];
|
|
121
|
+
}
|
|
42
122
|
throw new Error('无法解析目录列表数据格式');
|
|
43
123
|
}
|
|
44
124
|
async function fetchDirectoryList(packageName, version, path) {
|
|
45
|
-
const unpkgPromise =
|
|
46
|
-
const npmmirrorPromise =
|
|
125
|
+
const unpkgPromise = fetchDirectoryListFromSource(UNPKG_BASE_URL, packageName, version, path, false);
|
|
126
|
+
const npmmirrorPromise = fetchDirectoryListFromSource(NPMMIRROR_BASE_URL, packageName, version, path, true);
|
|
127
|
+
const results = await Promise.allSettled([
|
|
128
|
+
unpkgPromise,
|
|
129
|
+
npmmirrorPromise
|
|
130
|
+
]);
|
|
131
|
+
const successfulResults = [];
|
|
132
|
+
const errors = [];
|
|
133
|
+
if ('fulfilled' === results[0].status) successfulResults.push({
|
|
134
|
+
source: 'unpkg',
|
|
135
|
+
files: results[0].value
|
|
136
|
+
});
|
|
137
|
+
else errors.push(results[0].reason instanceof Error ? results[0].reason : new Error(String(results[0].reason)));
|
|
138
|
+
if ('fulfilled' === results[1].status) successfulResults.push({
|
|
139
|
+
source: 'npmmirror',
|
|
140
|
+
files: results[1].value
|
|
141
|
+
});
|
|
142
|
+
else errors.push(results[1].reason instanceof Error ? results[1].reason : new Error(String(results[1].reason)));
|
|
143
|
+
if (0 === successfulResults.length) throw new Error(`所有数据源都失败了: ${errors.map((e)=>e.message).join('; ')}`);
|
|
144
|
+
successfulResults.sort((a, b)=>{
|
|
145
|
+
if (b.files.length !== a.files.length) return b.files.length - a.files.length;
|
|
146
|
+
return 'unpkg' === a.source ? -1 : 1;
|
|
147
|
+
});
|
|
148
|
+
return successfulResults[0].files;
|
|
149
|
+
}
|
|
150
|
+
const fetch_file_content_UNPKG_BASE_URL = 'https://unpkg.com';
|
|
151
|
+
const fetch_file_content_NPMMIRROR_BASE_URL = 'https://registry.npmmirror.com';
|
|
152
|
+
async function fetchFileContentFromSource(baseUrl, packageName, version, filePath, isNpmMirror = false) {
|
|
153
|
+
const url = isNpmMirror ? `${baseUrl}/${packageName}/${version}/files/${filePath}` : `${baseUrl}/${packageName}@${version}/${filePath}`;
|
|
154
|
+
const response = await fetch(url, {
|
|
155
|
+
headers: {
|
|
156
|
+
Accept: 'text/plain, application/json, */*'
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
if (!response.ok) throw new Error(`获取文件失败: ${response.status} ${response.statusText}`);
|
|
160
|
+
const content = await response.text();
|
|
161
|
+
if (content.trim().startsWith('<!DOCTYPE html>') || content.includes('npmmirror 镜像站')) throw new Error('返回了 HTML 错误页面');
|
|
162
|
+
return content;
|
|
163
|
+
}
|
|
164
|
+
async function fetchFileContent(packageName, version, filePath) {
|
|
165
|
+
const unpkgPromise = fetchFileContentFromSource(fetch_file_content_UNPKG_BASE_URL, packageName, version, filePath, false);
|
|
166
|
+
const npmmirrorPromise = fetchFileContentFromSource(fetch_file_content_NPMMIRROR_BASE_URL, packageName, version, filePath, true);
|
|
47
167
|
const unpkgWithFallback = unpkgPromise.catch(()=>new Promise(()=>{}));
|
|
48
168
|
const npmmirrorWithFallback = npmmirrorPromise.catch(()=>new Promise(()=>{}));
|
|
49
169
|
const raceResult = await Promise.race([
|
|
@@ -91,6 +211,11 @@ const getSemiDocumentTool = {
|
|
|
91
211
|
version: {
|
|
92
212
|
type: 'string',
|
|
93
213
|
description: '版本号,例如 2.89.1。如果不提供,默认使用 latest'
|
|
214
|
+
},
|
|
215
|
+
get_path: {
|
|
216
|
+
type: 'boolean',
|
|
217
|
+
description: '如果为 true,将文档写入操作系统临时目录并返回路径,而不是在响应中返回文档内容。默认为 false',
|
|
218
|
+
default: false
|
|
94
219
|
}
|
|
95
220
|
},
|
|
96
221
|
required: []
|
|
@@ -117,18 +242,36 @@ async function getComponentDocuments(componentName, version) {
|
|
|
117
242
|
}
|
|
118
243
|
if (-1 === categoryIndex || categoryIndex >= pathParts.length) return null;
|
|
119
244
|
const category = pathParts[categoryIndex];
|
|
120
|
-
const
|
|
245
|
+
const documentPromises = componentFiles.map(async (file)=>{
|
|
246
|
+
const filePath = file.path.startsWith('/') ? file.path.slice(1) : file.path;
|
|
121
247
|
const parts = file.path.split('/');
|
|
122
|
-
|
|
123
|
-
|
|
248
|
+
const fileName = parts[parts.length - 1];
|
|
249
|
+
try {
|
|
250
|
+
const content = await fetchFileContent(packageName, version, filePath);
|
|
251
|
+
return {
|
|
252
|
+
name: fileName,
|
|
253
|
+
path: file.path,
|
|
254
|
+
content: content
|
|
255
|
+
};
|
|
256
|
+
} catch (error) {
|
|
257
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
258
|
+
return {
|
|
259
|
+
name: fileName,
|
|
260
|
+
path: file.path,
|
|
261
|
+
content: `获取文档内容失败: ${errorMessage}`
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
const documents = await Promise.all(documentPromises);
|
|
124
266
|
return {
|
|
125
267
|
category,
|
|
126
|
-
documents:
|
|
268
|
+
documents: documents.sort((a, b)=>a.name.localeCompare(b.name))
|
|
127
269
|
};
|
|
128
270
|
}
|
|
129
271
|
async function handleGetSemiDocument(args) {
|
|
130
272
|
const componentName = args?.componentName;
|
|
131
273
|
const version = args?.version || 'latest';
|
|
274
|
+
const getPath = args?.get_path || false;
|
|
132
275
|
try {
|
|
133
276
|
if (componentName) {
|
|
134
277
|
const result = await getComponentDocuments(componentName, version);
|
|
@@ -149,6 +292,60 @@ async function handleGetSemiDocument(args) {
|
|
|
149
292
|
}
|
|
150
293
|
]
|
|
151
294
|
};
|
|
295
|
+
const documentsWithLines = result.documents.map((doc)=>({
|
|
296
|
+
...doc,
|
|
297
|
+
lines: doc.content.split('\n').length
|
|
298
|
+
}));
|
|
299
|
+
const hasLargeDocument = documentsWithLines.some((doc)=>doc.lines > 888);
|
|
300
|
+
const userExplicitlySetGetPath = 'get_path' in args;
|
|
301
|
+
const shouldUsePath = getPath || hasLargeDocument && !userExplicitlySetGetPath;
|
|
302
|
+
if (shouldUsePath) {
|
|
303
|
+
const baseTempDir = tmpdir();
|
|
304
|
+
const tempDirName = `semi-docs-${componentName.toLowerCase()}-${version}-${Date.now()}`;
|
|
305
|
+
const tempDir = join(baseTempDir, tempDirName);
|
|
306
|
+
await mkdir(tempDir, {
|
|
307
|
+
recursive: true
|
|
308
|
+
});
|
|
309
|
+
const filePaths = [];
|
|
310
|
+
for (const doc of result.documents){
|
|
311
|
+
const filePath = join(tempDir, doc.name);
|
|
312
|
+
await writeFile(filePath, doc.content, 'utf-8');
|
|
313
|
+
filePaths.push(filePath);
|
|
314
|
+
}
|
|
315
|
+
const largeDocs = documentsWithLines.filter((doc)=>doc.lines > 888);
|
|
316
|
+
let message = `文档已保存到临时目录: ${tempDir}\n请使用文件读取工具查看文档内容。`;
|
|
317
|
+
if (hasLargeDocument && !userExplicitlySetGetPath) {
|
|
318
|
+
const largeDocNames = largeDocs.map((doc)=>`${doc.name} (${doc.lines.toLocaleString()} 行)`).join(', ');
|
|
319
|
+
message = `文档已保存到临时目录: ${tempDir}\n注意:以下文档文件较大,已自动保存到临时目录:${largeDocNames}\n请使用文件读取工具查看文档内容。`;
|
|
320
|
+
} else if (hasLargeDocument) {
|
|
321
|
+
const largeDocNames = largeDocs.map((doc)=>`${doc.name} (${doc.lines.toLocaleString()} 行)`).join(', ');
|
|
322
|
+
message = `文档已保存到临时目录: ${tempDir}\n注意:以下文档文件较大:${largeDocNames}\n请使用文件读取工具查看文档内容。`;
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
content: [
|
|
326
|
+
{
|
|
327
|
+
type: 'text',
|
|
328
|
+
text: JSON.stringify({
|
|
329
|
+
componentName: componentName.toLowerCase(),
|
|
330
|
+
version,
|
|
331
|
+
category: result.category,
|
|
332
|
+
tempDirectory: tempDir,
|
|
333
|
+
files: documentsWithLines.map((doc)=>({
|
|
334
|
+
name: doc.name,
|
|
335
|
+
path: join(tempDir, doc.name),
|
|
336
|
+
contentLength: doc.content.length,
|
|
337
|
+
lines: doc.lines
|
|
338
|
+
})),
|
|
339
|
+
count: result.documents.length,
|
|
340
|
+
message,
|
|
341
|
+
autoGetPath: hasLargeDocument && !userExplicitlySetGetPath,
|
|
342
|
+
allComponents,
|
|
343
|
+
allComponentsCount: allComponents.length
|
|
344
|
+
}, null, 2)
|
|
345
|
+
}
|
|
346
|
+
]
|
|
347
|
+
};
|
|
348
|
+
}
|
|
152
349
|
return {
|
|
153
350
|
content: [
|
|
154
351
|
{
|
|
@@ -157,7 +354,16 @@ async function handleGetSemiDocument(args) {
|
|
|
157
354
|
componentName: componentName.toLowerCase(),
|
|
158
355
|
version,
|
|
159
356
|
category: result.category,
|
|
160
|
-
documents: result.documents
|
|
357
|
+
documents: result.documents.map((doc)=>({
|
|
358
|
+
name: doc.name,
|
|
359
|
+
path: doc.path,
|
|
360
|
+
contentLength: doc.content.length
|
|
361
|
+
})),
|
|
362
|
+
contents: result.documents.map((doc)=>({
|
|
363
|
+
name: doc.name,
|
|
364
|
+
path: doc.path,
|
|
365
|
+
content: doc.content
|
|
366
|
+
})),
|
|
161
367
|
count: result.documents.length,
|
|
162
368
|
allComponents,
|
|
163
369
|
allComponentsCount: allComponents.length
|
|
@@ -2,9 +2,19 @@
|
|
|
2
2
|
* 从 unpkg 或 npmmirror 获取目录列表
|
|
3
3
|
* 同时向两个数据源发送请求,使用第一个成功返回的结果
|
|
4
4
|
*/
|
|
5
|
+
export declare const UNPKG_BASE_URL = "https://unpkg.com";
|
|
6
|
+
export declare const NPMMIRROR_BASE_URL = "https://registry.npmmirror.com";
|
|
7
|
+
/**
|
|
8
|
+
* 从单个源获取目录列表
|
|
9
|
+
* 导出用于测试
|
|
10
|
+
*/
|
|
11
|
+
export declare function fetchDirectoryListFromSource(baseUrl: string, packageName: string, version: string, path: string, isNpmMirror?: boolean): Promise<Array<{
|
|
12
|
+
path: string;
|
|
13
|
+
type: string;
|
|
14
|
+
}>>;
|
|
5
15
|
/**
|
|
6
16
|
* 从 unpkg 或 npmmirror 获取目录列表
|
|
7
|
-
*
|
|
17
|
+
* 同时向两个数据源发送请求,优先使用返回更多文件的结果
|
|
8
18
|
*/
|
|
9
19
|
export declare function fetchDirectoryList(packageName: string, version: string, path: string): Promise<Array<{
|
|
10
20
|
path: string;
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
* 从 unpkg 或 npmmirror 获取具体文件内容
|
|
3
3
|
* 同时向两个数据源发送请求,使用第一个成功返回的结果
|
|
4
4
|
*/
|
|
5
|
+
export declare const UNPKG_BASE_URL = "https://unpkg.com";
|
|
6
|
+
export declare const NPMMIRROR_BASE_URL = "https://registry.npmmirror.com";
|
|
7
|
+
/**
|
|
8
|
+
* 从单个源获取文件内容
|
|
9
|
+
* 导出用于测试
|
|
10
|
+
*/
|
|
11
|
+
export declare function fetchFileContentFromSource(baseUrl: string, packageName: string, version: string, filePath: string, isNpmMirror?: boolean): Promise<string>;
|
|
5
12
|
/**
|
|
6
13
|
* 从 unpkg 或 npmmirror 获取具体文件内容
|
|
7
14
|
* 同时向两个数据源发送请求,使用第一个成功返回的结果
|
package/package.json
CHANGED