@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.
@@ -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(CONFIG_DIR, 'skills');
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 loadSkillsMetadatas() {
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(skillName => {
61
+ .map(skillDir => {
53
62
  try {
54
- const { name, description } = readSkillJson(getSkillFilePath(skillName));
63
+ const { name, description } = readSkillJson(getSkillFilePath(skillDir));
64
+ const isActive = skillConfigs.find(skill => skill.name === name)?.isActive ?? true;
55
65
  return {
56
- name: name.trim(),
57
- description: description.trim()
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
- return yamlFront.loadFront(skillMd);
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
- Returns the skill's prompt and context.`,
115
+ ${metadata.map(skill => `- ${skill.name}: ${skill.description}`).join('\n')}`,
85
116
  inputSchema: zod_1.default.object({
86
- skillName: zod_1.default.string().describe('Name of skill to load')
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.trim()}`
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.1",
4
- "main": "SkillsMcpServer.js",
3
+ "version": "0.0.2",
4
+ "main": "./dist/index.js",
5
5
  "bin": {
6
- "@baitong-dev/skills-mcp": "./SkillsMcpServer.js"
6
+ "@baitong-dev/skills-mcp": "./dist/index.js"
7
7
  },
8
8
  "files": [
9
- "SkillsMcpServer.js",
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
+ }