@reus-able/frontend-helper-mcp 1.0.0
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 +60 -0
- package/dist/index.js +145 -0
- package/package.json +39 -0
- package/prompts/demo.md +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Frontend Helper MCP - Local Prompts
|
|
2
|
+
|
|
3
|
+
这是一个简单的 MCP (Model Context Protocol) 服务器,专门用于将本地目录中的 Prompt 文件(`.md`, `.txt`, `.json`)暴露给 Cursor 或其他 MCP 客户端使用。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- **List Prompts**: 自动扫描指定目录下的文件,并列出为可用的 Prompts。
|
|
8
|
+
- **Get Prompt**: 读取文件内容并将其作为 Prompt 内容返回。
|
|
9
|
+
|
|
10
|
+
## 快速开始
|
|
11
|
+
|
|
12
|
+
### 1. 安装与构建
|
|
13
|
+
|
|
14
|
+
确保你已安装 Node.js (v16+)。
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# 安装依赖
|
|
18
|
+
npm install
|
|
19
|
+
|
|
20
|
+
# 构建项目
|
|
21
|
+
npm run build
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 2. 在 Cursor 中配置
|
|
25
|
+
|
|
26
|
+
打开 Cursor 的设置面板 (Settings) -> Features -> MCP,点击 "Add new MCP server",并填入以下信息:
|
|
27
|
+
|
|
28
|
+
- **Type**: `stdio`
|
|
29
|
+
- **Name**: `local-prompts` (或你喜欢的任何名字)
|
|
30
|
+
- **Command**: `node`
|
|
31
|
+
- **Args**:
|
|
32
|
+
- `/path/to/frontend-helper-mcp/dist/index.js` (注意替换为实际的绝对路径)
|
|
33
|
+
- `--dir`
|
|
34
|
+
- `/path/to/your/prompts/directory` (你存放 Prompt 文件的目录绝对路径)
|
|
35
|
+
|
|
36
|
+
例如:
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"local-prompts": {
|
|
41
|
+
"command": "node",
|
|
42
|
+
"args": [
|
|
43
|
+
"/Users/username/Work/frontend-helper-mcp/dist/index.js",
|
|
44
|
+
"--dir",
|
|
45
|
+
"/Users/username/my-prompts"
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3. 使用方法
|
|
53
|
+
|
|
54
|
+
1. 在配置的 Prompt 目录下创建文件,例如 `code-review.md`。
|
|
55
|
+
2. 在 Cursor 的 Chat 界面中,点击 "Prompts" 或输入 `/`,你应该能看到 `code-review` 出现在列表中。
|
|
56
|
+
3. 选择它,文件的内容就会被加载到当前的对话上下文中。
|
|
57
|
+
|
|
58
|
+
## 命令行参数
|
|
59
|
+
|
|
60
|
+
- `--dir` / `-d`: 指定 Prompt 文件所在的目录。默认为当前目录下的 `./prompts`。
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
5
|
+
import { ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import fs from 'fs/promises';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import yargs from 'yargs';
|
|
9
|
+
import { hideBin } from 'yargs/helpers';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import express from 'express';
|
|
12
|
+
import cors from 'cors';
|
|
13
|
+
async function createServer(promptsDir) {
|
|
14
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const packageJsonPath = path.resolve(__dirname, '../package.json');
|
|
16
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
|
|
17
|
+
const mcpServer = new McpServer({
|
|
18
|
+
name: packageJson.name,
|
|
19
|
+
version: packageJson.version,
|
|
20
|
+
}, {
|
|
21
|
+
capabilities: {
|
|
22
|
+
prompts: {},
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
mcpServer.server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
26
|
+
try {
|
|
27
|
+
// Ensure dir exists
|
|
28
|
+
try {
|
|
29
|
+
await fs.access(promptsDir);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
console.error(`Directory ${promptsDir} does not exist.`);
|
|
33
|
+
return { prompts: [] };
|
|
34
|
+
}
|
|
35
|
+
const files = await fs.readdir(promptsDir);
|
|
36
|
+
const promptFiles = files.filter((f) => ['.txt', '.md', '.json'].includes(path.extname(f).toLowerCase()));
|
|
37
|
+
const prompts = promptFiles.map((file) => {
|
|
38
|
+
const name = path.parse(file).name;
|
|
39
|
+
return {
|
|
40
|
+
name: name,
|
|
41
|
+
description: `Content of ${file}`,
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
return { prompts };
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error('Error listing prompts:', error);
|
|
48
|
+
return { prompts: [] };
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
mcpServer.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
52
|
+
const promptName = request.params.name;
|
|
53
|
+
try {
|
|
54
|
+
const files = await fs.readdir(promptsDir);
|
|
55
|
+
const file = files.find((f) => path.parse(f).name === promptName &&
|
|
56
|
+
['.txt', '.md', '.json'].includes(path.extname(f).toLowerCase()));
|
|
57
|
+
if (!file) {
|
|
58
|
+
throw new Error(`Prompt not found: ${promptName}`);
|
|
59
|
+
}
|
|
60
|
+
const content = await fs.readFile(path.join(promptsDir, file), 'utf-8');
|
|
61
|
+
return {
|
|
62
|
+
messages: [
|
|
63
|
+
{
|
|
64
|
+
role: 'user',
|
|
65
|
+
content: {
|
|
66
|
+
type: 'text',
|
|
67
|
+
text: content,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
throw new Error(`Failed to load prompt ${promptName}: ${error}`);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return mcpServer;
|
|
78
|
+
}
|
|
79
|
+
async function main() {
|
|
80
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
81
|
+
const packageJsonPath = path.resolve(__dirname, '../package.json');
|
|
82
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
|
|
83
|
+
const argv = await yargs(hideBin(process.argv))
|
|
84
|
+
.option('transport', {
|
|
85
|
+
alias: 't',
|
|
86
|
+
type: 'string',
|
|
87
|
+
choices: ['stdio', 'sse'],
|
|
88
|
+
default: 'stdio',
|
|
89
|
+
description: 'Transport mode (stdio or sse)',
|
|
90
|
+
})
|
|
91
|
+
.option('port', {
|
|
92
|
+
alias: 'p',
|
|
93
|
+
type: 'number',
|
|
94
|
+
default: 3000,
|
|
95
|
+
description: 'Port to listen on (only for sse transport)',
|
|
96
|
+
})
|
|
97
|
+
.help()
|
|
98
|
+
.parse();
|
|
99
|
+
const promptsDir = path.resolve(__dirname, '../prompts');
|
|
100
|
+
if (argv.transport === 'sse') {
|
|
101
|
+
const app = express();
|
|
102
|
+
app.use(cors());
|
|
103
|
+
// Store active sessions: sessionId -> { server, transport }
|
|
104
|
+
const sessions = new Map();
|
|
105
|
+
app.get('/sse', async (req, res) => {
|
|
106
|
+
console.log(`New SSE connection from ${req.ip}`);
|
|
107
|
+
const mcpServer = await createServer(promptsDir);
|
|
108
|
+
const transport = new SSEServerTransport('/messages', res);
|
|
109
|
+
await mcpServer.connect(transport);
|
|
110
|
+
sessions.set(transport.sessionId, { server: mcpServer, transport });
|
|
111
|
+
transport.onclose = () => {
|
|
112
|
+
console.log(`Session closed: ${transport.sessionId}`);
|
|
113
|
+
sessions.delete(transport.sessionId);
|
|
114
|
+
};
|
|
115
|
+
await transport.start();
|
|
116
|
+
});
|
|
117
|
+
app.post('/messages', async (req, res) => {
|
|
118
|
+
const sessionId = req.query.sessionId;
|
|
119
|
+
if (!sessionId) {
|
|
120
|
+
res.status(400).send('Missing sessionId');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const session = sessions.get(sessionId);
|
|
124
|
+
if (!session) {
|
|
125
|
+
res.status(404).send('Session not found');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
await session.transport.handlePostMessage(req, res);
|
|
129
|
+
});
|
|
130
|
+
app.listen(argv.port, () => {
|
|
131
|
+
console.log(`MCP Server running on SSE mode at http://localhost:${argv.port}/sse`);
|
|
132
|
+
console.log(`Prompts directory: ${promptsDir}`);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
console.error(`Starting MCP server serving prompts from: ${promptsDir}`);
|
|
137
|
+
const mcpServer = await createServer(promptsDir);
|
|
138
|
+
const transport = new StdioServerTransport();
|
|
139
|
+
await mcpServer.connect(transport);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
main().catch((error) => {
|
|
143
|
+
console.error('Fatal error:', error);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "前端开发的一些cursor mcp工具集",
|
|
3
|
+
"name": "@reus-able/frontend-helper-mcp",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"prompts"
|
|
12
|
+
],
|
|
13
|
+
"bin": {
|
|
14
|
+
"mcp-prompts": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"main": "index.js",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"prepublishOnly": "npm run build",
|
|
20
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [],
|
|
23
|
+
"author": "",
|
|
24
|
+
"license": "ISC",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
27
|
+
"cors": "^2.8.5",
|
|
28
|
+
"express": "^5.2.1",
|
|
29
|
+
"yargs": "^18.0.0",
|
|
30
|
+
"zod": "^4.2.1"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/cors": "^2.8.19",
|
|
34
|
+
"@types/express": "^5.0.6",
|
|
35
|
+
"@types/node": "^25.0.3",
|
|
36
|
+
"@types/yargs": "^17.0.35",
|
|
37
|
+
"typescript": "^5.9.3"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/prompts/demo.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Hello from MCP Prompt
|