@baitong-dev/skills-mcp 0.0.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.
Files changed (2) hide show
  1. package/SkillsMcpServer.js +201 -0
  2. package/package.json +28 -0
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
7
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
8
+ const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const http_1 = require("http");
11
+ const path_1 = __importDefault(require("path"));
12
+ const zod_1 = __importDefault(require("zod"));
13
+ const commander_1 = require("commander");
14
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
15
+ const yamlFront = require('yaml-front-matter');
16
+ // 命令行参数处理
17
+ const program = new commander_1.Command()
18
+ .option('--transport <stdio|http|sse>', 'transport type', 'stdio')
19
+ .option('--port <number>', 'port for HTTP/SSE transport', '3000')
20
+ .parse(process.argv);
21
+ const cliOptions = program.opts();
22
+ const allowedTransports = ['stdio', 'http'];
23
+ if (!allowedTransports.includes(cliOptions.transport)) {
24
+ console.error(`Invalid --transport value: '${cliOptions.transport}'. Must be one of: stdio, http, sse.`);
25
+ process.exit(1);
26
+ }
27
+ const MCP_NAME = 'Skills MCP';
28
+ const MCP_VERSION = '0.0.1';
29
+ const PORT = parseInt(cliOptions.port, 10);
30
+ const CONFIG_DIR = process.env.MCP_HOME_DIR;
31
+ const TRANSPORT_TYPE = (cliOptions.transport || 'stdio');
32
+ const logInfo = (...args) => {
33
+ if (TRANSPORT_TYPE === 'stdio') {
34
+ console.error(...args);
35
+ }
36
+ else {
37
+ console.log(...args);
38
+ }
39
+ };
40
+ function getSkillsRootDir() {
41
+ const dir = path_1.default.join(CONFIG_DIR, 'skills');
42
+ if (!fs_1.default.existsSync(dir)) {
43
+ fs_1.default.mkdirSync(dir, {
44
+ recursive: true
45
+ });
46
+ }
47
+ return dir;
48
+ }
49
+ function loadSkillsMetadatas() {
50
+ const skillsRootDirs = fs_1.default.readdirSync(getSkillsRootDir());
51
+ return skillsRootDirs
52
+ .map(skillName => {
53
+ try {
54
+ const { name, description } = readSkillJson(getSkillFilePath(skillName));
55
+ return {
56
+ name: name.trim(),
57
+ description: description.trim()
58
+ };
59
+ }
60
+ catch (_) {
61
+ return null;
62
+ }
63
+ })
64
+ .filter(item => item !== null);
65
+ }
66
+ function getSkillCwd(skillName) {
67
+ return path_1.default.join(getSkillsRootDir(), skillName);
68
+ }
69
+ function getSkillFilePath(skillName) {
70
+ return path_1.default.join(getSkillCwd(skillName), 'SKILL.md');
71
+ }
72
+ function readSkillJson(skillFilePath) {
73
+ const skillMd = fs_1.default.readFileSync(skillFilePath, 'utf-8');
74
+ return yamlFront.loadFront(skillMd);
75
+ }
76
+ function registerLoadSkill(server) {
77
+ server.registerTool('load_skill', {
78
+ description: `
79
+ Available skills:
80
+ ${loadSkillsMetadatas()
81
+ .map(skill => `- ${skill.name}: ${skill.description}`)
82
+ .join('\n')}
83
+
84
+ Returns the skill's prompt and context.`,
85
+ inputSchema: zod_1.default.object({
86
+ skillName: zod_1.default.string().describe('Name of skill to load')
87
+ })
88
+ }, async ({ skillName }) => {
89
+ // 返回skill元素据,只包含name和description
90
+ const skillCwd = getSkillCwd(skillName);
91
+ const skillFilePath = getSkillFilePath(skillName);
92
+ const json = readSkillJson(skillFilePath);
93
+ return {
94
+ content: [
95
+ {
96
+ type: 'text',
97
+ text: `Base directory for this skill: ${skillCwd}
98
+
99
+ ${json.__content.trim()}`
100
+ }
101
+ ]
102
+ };
103
+ });
104
+ }
105
+ // 创建MCP服务器实例
106
+ function createServerInstance() {
107
+ const server = new mcp_js_1.McpServer({
108
+ name: MCP_NAME,
109
+ version: MCP_VERSION
110
+ }, {
111
+ capabilities: {
112
+ logging: {}
113
+ }
114
+ });
115
+ // 注册所有工具
116
+ registerLoadSkill(server);
117
+ return server;
118
+ }
119
+ async function main() {
120
+ if (TRANSPORT_TYPE === 'http') {
121
+ const httpServer = (0, http_1.createServer)(async (req, res) => {
122
+ const url = new URL(req.url || '', `http://${req.headers.host}`).pathname;
123
+ // 设置 CORS 头
124
+ res.setHeader('Access-Control-Allow-Origin', '*');
125
+ res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS,DELETE');
126
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, MCP-Session-Id, mcp-session-id');
127
+ // 处理预检请求
128
+ if (req.method === 'OPTIONS') {
129
+ res.writeHead(200);
130
+ res.end();
131
+ return;
132
+ }
133
+ try {
134
+ // 为每个请求创建新的服务器实例
135
+ const requestServer = createServerInstance();
136
+ if (url === '/mcp') {
137
+ const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
138
+ sessionIdGenerator: undefined
139
+ });
140
+ await requestServer.connect(transport);
141
+ await transport.handleRequest(req, res);
142
+ }
143
+ else if (url === '/health') {
144
+ res.writeHead(200, { 'Content-Type': 'application/json' });
145
+ res.end(JSON.stringify({ status: 'ok', transport: TRANSPORT_TYPE }));
146
+ }
147
+ else if (url === '/info') {
148
+ res.writeHead(200, { 'Content-Type': 'application/json' });
149
+ res.end(JSON.stringify({
150
+ name: `${MCP_NAME} Server`,
151
+ version: MCP_VERSION,
152
+ transport: TRANSPORT_TYPE,
153
+ endpoints: {
154
+ mcp: '/mcp',
155
+ health: '/health',
156
+ info: '/info'
157
+ }
158
+ }));
159
+ }
160
+ else {
161
+ res.writeHead(404);
162
+ res.end('Not found');
163
+ }
164
+ }
165
+ catch (error) {
166
+ console.error('Error handling request: ', error);
167
+ if (!res.headersSent) {
168
+ res.writeHead(500);
169
+ res.end('Internal Server Error');
170
+ }
171
+ }
172
+ });
173
+ httpServer.listen(PORT, () => {
174
+ logInfo(`${MCP_NAME} ${TRANSPORT_TYPE.toUpperCase()} server started.`);
175
+ logInfo(`MCP endpoint: http://localhost:${PORT}/mcp`);
176
+ logInfo(`Health: http://localhost:${PORT}/health`);
177
+ logInfo(`Info: http://localhost:${PORT}/info`);
178
+ });
179
+ }
180
+ else {
181
+ // stdio 模式
182
+ const server = createServerInstance();
183
+ const transport = new stdio_js_1.StdioServerTransport();
184
+ await server.connect(transport);
185
+ logInfo(`${MCP_NAME} ${TRANSPORT_TYPE.toUpperCase()} server started.`);
186
+ }
187
+ }
188
+ // 优雅关闭
189
+ process.on('SIGINT', async () => {
190
+ logInfo(`\nShutting down ${MCP_NAME} server...`);
191
+ process.exit(0);
192
+ });
193
+ process.on('SIGTERM', async () => {
194
+ logInfo(`\nReceived termination signal, shutting down ${MCP_NAME} server...`);
195
+ process.exit(0);
196
+ });
197
+ // 启动服务器
198
+ main().catch(error => {
199
+ logInfo('Starting server failed:', error);
200
+ process.exit(1);
201
+ });
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@baitong-dev/skills-mcp",
3
+ "version": "0.0.1",
4
+ "main": "SkillsMcpServer.js",
5
+ "bin": {
6
+ "@baitong-dev/skills-mcp": "./SkillsMcpServer.js"
7
+ },
8
+ "files": [
9
+ "SkillsMcpServer.js",
10
+ "README.md"
11
+ ],
12
+ "scripts": {},
13
+ "keywords": [
14
+ "mcp",
15
+ "skills"
16
+ ],
17
+ "description": "skills-mcp",
18
+ "dependencies": {
19
+ "@modelcontextprotocol/sdk": "^1.25.1",
20
+ "commander": "^14.0.3",
21
+ "yaml-front-matter": "^4.1.1",
22
+ "zod": "^4.3.4"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public",
26
+ "registry": "https://registry.npmjs.org"
27
+ }
28
+ }