skillsmp-mcp-lite 1.0.1 → 1.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/README.md +29 -0
- package/dist/schemas.d.ts +17 -0
- package/dist/schemas.js +18 -0
- package/dist/tools/skills.js +132 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -102,6 +102,17 @@ AI semantic search for skills using natural language.
|
|
|
102
102
|
|-----------|------|----------|-------------|
|
|
103
103
|
| `query` | string | Yes | Natural language description |
|
|
104
104
|
|
|
105
|
+
### `skillsmp_install_and_read_skill`
|
|
106
|
+
|
|
107
|
+
Install a skill from GitHub and immediately read its content.
|
|
108
|
+
|
|
109
|
+
| Parameter | Type | Required | Description |
|
|
110
|
+
|-----------|------|----------|-------------|
|
|
111
|
+
| `repo` | string | Yes | GitHub repository in 'owner/repo' format |
|
|
112
|
+
| `skillName` | string | Yes | Name of the skill to read after installation |
|
|
113
|
+
| `global` | boolean | No | Install globally to ~/.claude/skills/ (default: false) |
|
|
114
|
+
| `universal` | boolean | No | Install to .agent/skills/ for universal usage (default: false) |
|
|
115
|
+
|
|
105
116
|
## Usage Examples
|
|
106
117
|
|
|
107
118
|
Ask your AI assistant:
|
|
@@ -109,6 +120,24 @@ Ask your AI assistant:
|
|
|
109
120
|
- "Search for PDF manipulation skills"
|
|
110
121
|
- "Find skills for building a web scraper"
|
|
111
122
|
- "What skills can help me with SEO optimization?"
|
|
123
|
+
- "Install and load the python-code-review skill from existential-birds/beagle"
|
|
124
|
+
|
|
125
|
+
## AGENTS.md Integration
|
|
126
|
+
|
|
127
|
+
To enable automatic skill discovery, copy the content from [`AGENTS.example.md`](./AGENTS.example.md) and paste it at the top of your `AGENTS.md` file.
|
|
128
|
+
|
|
129
|
+
### How It Works
|
|
130
|
+
|
|
131
|
+
1. AI receives a complex task
|
|
132
|
+
2. AI searches SkillsMP for relevant skills using `skillsmp_search_skills`
|
|
133
|
+
3. If keyword search is insufficient, AI tries `skillsmp_ai_search_skills`
|
|
134
|
+
4. If a relevant skill is found, AI installs and reads it with `skillsmp_install_and_read_skill`
|
|
135
|
+
5. AI follows the skill's instructions to complete the task
|
|
136
|
+
|
|
137
|
+
### Search Tips
|
|
138
|
+
|
|
139
|
+
- **Keyword search**: Keep queries short (1-3 words). Example: `"code review"`, `"typescript"`, `"pdf"`
|
|
140
|
+
- **Semantic search**: Use natural language. Example: `"how to build a landing page with React"`
|
|
112
141
|
|
|
113
142
|
## License
|
|
114
143
|
|
package/dist/schemas.d.ts
CHANGED
|
@@ -31,3 +31,20 @@ export declare const AISearchSchema: z.ZodObject<{
|
|
|
31
31
|
query: string;
|
|
32
32
|
}>;
|
|
33
33
|
export type AISearchInput = z.infer<typeof AISearchSchema>;
|
|
34
|
+
export declare const InstallAndReadSchema: z.ZodObject<{
|
|
35
|
+
repo: z.ZodString;
|
|
36
|
+
skillName: z.ZodString;
|
|
37
|
+
global: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
38
|
+
universal: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
39
|
+
}, "strict", z.ZodTypeAny, {
|
|
40
|
+
repo: string;
|
|
41
|
+
skillName: string;
|
|
42
|
+
global: boolean;
|
|
43
|
+
universal: boolean;
|
|
44
|
+
}, {
|
|
45
|
+
repo: string;
|
|
46
|
+
skillName: string;
|
|
47
|
+
global?: boolean | undefined;
|
|
48
|
+
universal?: boolean | undefined;
|
|
49
|
+
}>;
|
|
50
|
+
export type InstallAndReadInput = z.infer<typeof InstallAndReadSchema>;
|
package/dist/schemas.js
CHANGED
|
@@ -36,3 +36,21 @@ export const AISearchSchema = z.object({
|
|
|
36
36
|
.max(500, "Query must not exceed 500 characters")
|
|
37
37
|
.describe("Natural language description of what you want to accomplish")
|
|
38
38
|
}).strict();
|
|
39
|
+
// Install and Read Skill Schema
|
|
40
|
+
export const InstallAndReadSchema = z.object({
|
|
41
|
+
repo: z.string()
|
|
42
|
+
.min(1, "Repository is required")
|
|
43
|
+
.regex(/^[^/]+\/[^/]+$/, "Repository must be in 'owner/repo' format")
|
|
44
|
+
.describe("GitHub repository in 'owner/repo' format (e.g., 'existential-birds/beagle')"),
|
|
45
|
+
skillName: z.string()
|
|
46
|
+
.min(1, "Skill name is required")
|
|
47
|
+
.describe("Name of the skill to read after installation"),
|
|
48
|
+
global: z.boolean()
|
|
49
|
+
.optional()
|
|
50
|
+
.default(false)
|
|
51
|
+
.describe("Install globally to ~/.claude/skills/ (default: false, installs to project)"),
|
|
52
|
+
universal: z.boolean()
|
|
53
|
+
.optional()
|
|
54
|
+
.default(false)
|
|
55
|
+
.describe("Install to .agent/skills/ for universal AGENTS.md usage (default: false)")
|
|
56
|
+
}).strict();
|
package/dist/tools/skills.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { exec } from "child_process";
|
|
2
|
+
import { promisify } from "util";
|
|
1
3
|
import { makeApiRequest, handleApiError, validateSearchResponse, validateAISearchResponse } from "../api.js";
|
|
2
|
-
import { KeywordSearchSchema, AISearchSchema } from "../schemas.js";
|
|
4
|
+
import { KeywordSearchSchema, AISearchSchema, InstallAndReadSchema } from "../schemas.js";
|
|
5
|
+
const execAsync = promisify(exec);
|
|
3
6
|
/**
|
|
4
7
|
* Register SkillsMP tools on the MCP server
|
|
5
8
|
*/
|
|
@@ -144,6 +147,115 @@ Examples:
|
|
|
144
147
|
};
|
|
145
148
|
}
|
|
146
149
|
});
|
|
150
|
+
// Tool 3: Install and Read Skill
|
|
151
|
+
server.registerTool("skillsmp_install_and_read_skill", {
|
|
152
|
+
title: "Install and Read Skill",
|
|
153
|
+
description: `Install a skill from GitHub and immediately read its content.
|
|
154
|
+
|
|
155
|
+
This tool first checks if the skill is already installed locally. If found, it skips installation and directly reads the content (faster). If not found, it installs from GitHub first.
|
|
156
|
+
|
|
157
|
+
**IMPORTANT**: Use this to quickly load skill instructions without manual steps.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
- repo (string, required): GitHub repository in 'owner/repo' format
|
|
161
|
+
- skillName (string, required): Name of the skill to read after installation
|
|
162
|
+
- global (boolean, optional): Install globally to ~/.claude/skills/ (default: false)
|
|
163
|
+
- universal (boolean, optional): Install to .agent/skills/ for universal usage (default: false)
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
The full content of the skill's instructions (SKILL.md).
|
|
167
|
+
|
|
168
|
+
Examples:
|
|
169
|
+
- repo: "existential-birds/beagle", skillName: "python-code-review"
|
|
170
|
+
- repo: "LA3D/skillhelper", skillName: "code-reviewer"`,
|
|
171
|
+
inputSchema: InstallAndReadSchema,
|
|
172
|
+
annotations: {
|
|
173
|
+
readOnlyHint: false,
|
|
174
|
+
destructiveHint: false,
|
|
175
|
+
idempotentHint: true,
|
|
176
|
+
openWorldHint: true
|
|
177
|
+
}
|
|
178
|
+
}, async (params) => {
|
|
179
|
+
try {
|
|
180
|
+
const readCmd = `npx -y openskills read ${params.skillName}`;
|
|
181
|
+
// Step 1: Check if skill already exists locally
|
|
182
|
+
let skillExists = false;
|
|
183
|
+
try {
|
|
184
|
+
const { stdout } = await execAsync(readCmd, { timeout: 30000 });
|
|
185
|
+
if (stdout && !stdout.includes("not found") && !stdout.includes("Error")) {
|
|
186
|
+
skillExists = true;
|
|
187
|
+
const output = formatInstallAndReadResponse(params.repo, params.skillName, stdout, true);
|
|
188
|
+
return {
|
|
189
|
+
content: [{ type: "text", text: output }],
|
|
190
|
+
structuredContent: {
|
|
191
|
+
repo: params.repo,
|
|
192
|
+
skillName: params.skillName,
|
|
193
|
+
skillContent: stdout,
|
|
194
|
+
wasAlreadyInstalled: true
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// Skill not found locally, proceed with installation
|
|
201
|
+
skillExists = false;
|
|
202
|
+
}
|
|
203
|
+
// Step 2: Install if not exists
|
|
204
|
+
const installFlags = ["-y"];
|
|
205
|
+
if (params.global)
|
|
206
|
+
installFlags.push("--global");
|
|
207
|
+
if (params.universal)
|
|
208
|
+
installFlags.push("--universal");
|
|
209
|
+
const installCmd = `npx -y openskills install ${params.repo} ${installFlags.join(" ")}`;
|
|
210
|
+
let installOutput;
|
|
211
|
+
try {
|
|
212
|
+
const { stdout, stderr } = await execAsync(installCmd, { timeout: 120000 });
|
|
213
|
+
installOutput = stdout || stderr;
|
|
214
|
+
}
|
|
215
|
+
catch (installError) {
|
|
216
|
+
const error = installError;
|
|
217
|
+
return {
|
|
218
|
+
content: [{
|
|
219
|
+
type: "text",
|
|
220
|
+
text: `❌ **Installation Failed**\n\nRepository: ${params.repo}\n\nError:\n${error.stderr || error.message || "Unknown error"}`
|
|
221
|
+
}]
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
// Step 3: Read the skill after installation
|
|
225
|
+
let readOutput;
|
|
226
|
+
try {
|
|
227
|
+
const { stdout, stderr } = await execAsync(readCmd, { timeout: 30000 });
|
|
228
|
+
readOutput = stdout || stderr;
|
|
229
|
+
}
|
|
230
|
+
catch (readError) {
|
|
231
|
+
const error = readError;
|
|
232
|
+
return {
|
|
233
|
+
content: [{
|
|
234
|
+
type: "text",
|
|
235
|
+
text: `✅ **Installation Succeeded** but **Read Failed**\n\nRepository: ${params.repo}\nSkill: ${params.skillName}\n\n**Install Output:**\n${installOutput}\n\n**Read Error:**\n${error.stderr || error.message || "Skill not found"}\n\n💡 **Tip**: Check if the skill name is correct. Use \`npx openskills list\` to see installed skills.`
|
|
236
|
+
}]
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const output = formatInstallAndReadResponse(params.repo, params.skillName, readOutput, false);
|
|
240
|
+
return {
|
|
241
|
+
content: [{ type: "text", text: output }],
|
|
242
|
+
structuredContent: {
|
|
243
|
+
repo: params.repo,
|
|
244
|
+
skillName: params.skillName,
|
|
245
|
+
skillContent: readOutput,
|
|
246
|
+
wasAlreadyInstalled: false
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
return {
|
|
252
|
+
content: [{
|
|
253
|
+
type: "text",
|
|
254
|
+
text: `❌ **Error**: ${error instanceof Error ? error.message : "An unexpected error occurred"}`
|
|
255
|
+
}]
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
});
|
|
147
259
|
}
|
|
148
260
|
/**
|
|
149
261
|
* Render a single skill as markdown lines
|
|
@@ -205,6 +317,24 @@ function formatAISearchResponse(skills, query) {
|
|
|
205
317
|
""
|
|
206
318
|
];
|
|
207
319
|
skills.forEach((skill, i) => lines.push(...renderSkill(skill, i)));
|
|
208
|
-
lines.push("💡 **Tip**: Use `
|
|
320
|
+
lines.push("💡 **Tip**: Use `skillsmp_install_and_read_skill` to install and load a skill's instructions.");
|
|
321
|
+
return lines.join("\n");
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Format install and read response as markdown
|
|
325
|
+
*/
|
|
326
|
+
function formatInstallAndReadResponse(repo, skillName, skillContent, wasAlreadyInstalled) {
|
|
327
|
+
const statusIcon = wasAlreadyInstalled ? "📖" : "📦";
|
|
328
|
+
const statusText = wasAlreadyInstalled ? "Skill Loaded (already installed)" : "Skill Installed & Loaded";
|
|
329
|
+
const lines = [
|
|
330
|
+
`# ${statusIcon} ${statusText}: ${skillName}`,
|
|
331
|
+
"",
|
|
332
|
+
`**Repository**: ${repo}`,
|
|
333
|
+
wasAlreadyInstalled ? "**Status**: ⚡ Loaded from local cache (skipped installation)" : "**Status**: ✅ Freshly installed",
|
|
334
|
+
"",
|
|
335
|
+
"---",
|
|
336
|
+
"",
|
|
337
|
+
skillContent
|
|
338
|
+
];
|
|
209
339
|
return lines.join("\n");
|
|
210
340
|
}
|