@baitong-dev/skills-mcp 0.0.1 → 0.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/dist/index.d.ts +1 -0
- package/{SkillsMcpServer.js → dist/index.js} +83 -15
- package/package.json +10 -7
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -11,6 +11,7 @@ const http_1 = require("http");
|
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
12
|
const zod_1 = __importDefault(require("zod"));
|
|
13
13
|
const commander_1 = require("commander");
|
|
14
|
+
const mcp_helpers_1 = require("@baitong-dev/mcp-helpers");
|
|
14
15
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
15
16
|
const yamlFront = require('yaml-front-matter');
|
|
16
17
|
// 命令行参数处理
|
|
@@ -27,7 +28,6 @@ if (!allowedTransports.includes(cliOptions.transport)) {
|
|
|
27
28
|
const MCP_NAME = 'Skills MCP';
|
|
28
29
|
const MCP_VERSION = '0.0.1';
|
|
29
30
|
const PORT = parseInt(cliOptions.port, 10);
|
|
30
|
-
const CONFIG_DIR = process.env.MCP_HOME_DIR;
|
|
31
31
|
const TRANSPORT_TYPE = (cliOptions.transport || 'stdio');
|
|
32
32
|
const logInfo = (...args) => {
|
|
33
33
|
if (TRANSPORT_TYPE === 'stdio') {
|
|
@@ -38,7 +38,7 @@ const logInfo = (...args) => {
|
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
40
|
function getSkillsRootDir() {
|
|
41
|
-
const dir = path_1.default.join(
|
|
41
|
+
const dir = path_1.default.join(mcp_helpers_1.MCP_HOME_DIR, 'skills');
|
|
42
42
|
if (!fs_1.default.existsSync(dir)) {
|
|
43
43
|
fs_1.default.mkdirSync(dir, {
|
|
44
44
|
recursive: true
|
|
@@ -46,15 +46,27 @@ function getSkillsRootDir() {
|
|
|
46
46
|
}
|
|
47
47
|
return dir;
|
|
48
48
|
}
|
|
49
|
-
function
|
|
49
|
+
function loadSkillsMetadata() {
|
|
50
50
|
const skillsRootDirs = fs_1.default.readdirSync(getSkillsRootDir());
|
|
51
|
+
let skillConfigs;
|
|
52
|
+
try {
|
|
53
|
+
skillConfigs =
|
|
54
|
+
JSON.parse(fs_1.default.readFileSync(path_1.default.join(mcp_helpers_1.MCP_HOME_DIR, 'config', 'skills_config.json'), 'utf-8'))
|
|
55
|
+
.skills || [];
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
skillConfigs = [];
|
|
59
|
+
}
|
|
51
60
|
return skillsRootDirs
|
|
52
|
-
.map(
|
|
61
|
+
.map(skillDir => {
|
|
53
62
|
try {
|
|
54
|
-
const { name, description } = readSkillJson(getSkillFilePath(
|
|
63
|
+
const { name, description } = readSkillJson(getSkillFilePath(skillDir));
|
|
64
|
+
const isActive = skillConfigs.find(skill => skill.name === name)?.isActive ?? true;
|
|
55
65
|
return {
|
|
56
|
-
name
|
|
57
|
-
description
|
|
66
|
+
name,
|
|
67
|
+
description,
|
|
68
|
+
cwd: getSkillCwd(skillDir),
|
|
69
|
+
isActive
|
|
58
70
|
};
|
|
59
71
|
}
|
|
60
72
|
catch (_) {
|
|
@@ -71,21 +83,62 @@ function getSkillFilePath(skillName) {
|
|
|
71
83
|
}
|
|
72
84
|
function readSkillJson(skillFilePath) {
|
|
73
85
|
const skillMd = fs_1.default.readFileSync(skillFilePath, 'utf-8');
|
|
74
|
-
|
|
86
|
+
const { name, description, __content } = yamlFront.loadFront(skillMd);
|
|
87
|
+
return {
|
|
88
|
+
name: name.toString().trim(),
|
|
89
|
+
description: description.toString().trim(),
|
|
90
|
+
__content: __content.trim()
|
|
91
|
+
};
|
|
75
92
|
}
|
|
76
93
|
function registerLoadSkill(server) {
|
|
94
|
+
const metadata = loadSkillsMetadata().filter(skill => skill.isActive);
|
|
95
|
+
const examples = metadata
|
|
96
|
+
.map(skill => `'${skill.name}'`)
|
|
97
|
+
.slice(0, 3)
|
|
98
|
+
.join(', ');
|
|
99
|
+
const hint = examples.length > 0 ? ` (e.g., ${examples}, ...)` : '';
|
|
77
100
|
server.registerTool('load_skill', {
|
|
78
|
-
description:
|
|
101
|
+
description: metadata.length === 0
|
|
102
|
+
? 'Load a specialized skill that provides domain-specific instructions and workflows. No skills are currently available.'
|
|
103
|
+
: `Load a specialized skill that provides domain-specific instructions and workflows.
|
|
104
|
+
|
|
105
|
+
When you recognize that a task matches one of the available skills listed below, use this tool to load the full skill instructions.
|
|
106
|
+
|
|
107
|
+
The skill will inject detailed instructions, workflows, and access to bundled resources (scripts, references, templates) into the conversation context.
|
|
108
|
+
|
|
109
|
+
The following skills provide specialized sets of instructions for particular tasks.
|
|
110
|
+
|
|
111
|
+
Invoke this tool to load a skill when a task matches one of the available skills listed below:
|
|
112
|
+
|
|
79
113
|
Available skills:
|
|
80
|
-
${loadSkillsMetadatas()
|
|
81
|
-
.map(skill => `- ${skill.name}: ${skill.description}`)
|
|
82
|
-
.join('\n')}
|
|
83
114
|
|
|
84
|
-
|
|
115
|
+
${metadata.map(skill => `- ${skill.name}: ${skill.description}`).join('\n')}`,
|
|
85
116
|
inputSchema: zod_1.default.object({
|
|
86
|
-
skillName: zod_1.default.string().describe(
|
|
117
|
+
skillName: zod_1.default.string().describe(`The name of the skill from available skills${hint}`)
|
|
87
118
|
})
|
|
88
119
|
}, async ({ skillName }) => {
|
|
120
|
+
if (metadata.length === 0) {
|
|
121
|
+
return {
|
|
122
|
+
isError: true,
|
|
123
|
+
content: [
|
|
124
|
+
{
|
|
125
|
+
type: 'text',
|
|
126
|
+
text: 'No skills are currently available.'
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
if (!metadata.some(skill => skill.name === skillName)) {
|
|
132
|
+
return {
|
|
133
|
+
isError: true,
|
|
134
|
+
content: [
|
|
135
|
+
{
|
|
136
|
+
type: 'text',
|
|
137
|
+
text: `Skill "${skillName}" not found.`
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
};
|
|
141
|
+
}
|
|
89
142
|
// 返回skill元素据,只包含name和description
|
|
90
143
|
const skillCwd = getSkillCwd(skillName);
|
|
91
144
|
const skillFilePath = getSkillFilePath(skillName);
|
|
@@ -96,7 +149,21 @@ Returns the skill's prompt and context.`,
|
|
|
96
149
|
type: 'text',
|
|
97
150
|
text: `Base directory for this skill: ${skillCwd}
|
|
98
151
|
|
|
99
|
-
${json.__content
|
|
152
|
+
${json.__content}`
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function registerRefreshSkills(server) {
|
|
159
|
+
server.registerTool('reload_skills', {
|
|
160
|
+
description: `Reload skills metadata. Especially after creating a new skill.` // 尤其在创建技能后要调用下重新加载技能
|
|
161
|
+
}, async () => {
|
|
162
|
+
return {
|
|
163
|
+
content: [
|
|
164
|
+
{
|
|
165
|
+
type: 'text',
|
|
166
|
+
text: `Skills metadata reloaded.`
|
|
100
167
|
}
|
|
101
168
|
]
|
|
102
169
|
};
|
|
@@ -114,6 +181,7 @@ function createServerInstance() {
|
|
|
114
181
|
});
|
|
115
182
|
// 注册所有工具
|
|
116
183
|
registerLoadSkill(server);
|
|
184
|
+
registerRefreshSkills(server);
|
|
117
185
|
return server;
|
|
118
186
|
}
|
|
119
187
|
async function main() {
|
package/package.json
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@baitong-dev/skills-mcp",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"main": "
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
5
|
"bin": {
|
|
6
|
-
"@baitong-dev/skills-mcp": "./
|
|
6
|
+
"@baitong-dev/skills-mcp": "./dist/index.js"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
|
-
"
|
|
9
|
+
"dist",
|
|
10
10
|
"README.md"
|
|
11
11
|
],
|
|
12
|
-
"scripts": {},
|
|
13
12
|
"keywords": [
|
|
14
13
|
"mcp",
|
|
15
14
|
"skills"
|
|
@@ -19,10 +18,14 @@
|
|
|
19
18
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
20
19
|
"commander": "^14.0.3",
|
|
21
20
|
"yaml-front-matter": "^4.1.1",
|
|
22
|
-
"zod": "^4.3.4"
|
|
21
|
+
"zod": "^4.3.4",
|
|
22
|
+
"@baitong-dev/mcp-helpers": "0.0.1"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public",
|
|
26
26
|
"registry": "https://registry.npmjs.org"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"tsc": "tsc ./src/index.ts --declaration --module commonjs --target es2021 --esModuleInterop --outDir ./dist"
|
|
27
30
|
}
|
|
28
|
-
}
|
|
31
|
+
}
|