skillsmp-mcp-lite 1.0.2 → 1.0.3
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/api.d.ts +1 -0
- package/dist/api.js +17 -7
- package/dist/index.js +4 -1
- package/dist/tools/skills.js +38 -17
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
package/dist/api.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
// API Configuration
|
|
5
5
|
export const SKILLSMP_API_BASE = "https://skillsmp.com/api/v1";
|
|
6
|
+
export const API_TIMEOUT_MS = 30_000;
|
|
6
7
|
// API Client
|
|
7
8
|
export async function makeApiRequest(endpoint, apiKey, params) {
|
|
8
9
|
const url = new URL(`${SKILLSMP_API_BASE}/${endpoint}`);
|
|
@@ -13,13 +14,22 @@ export async function makeApiRequest(endpoint, apiKey, params) {
|
|
|
13
14
|
}
|
|
14
15
|
});
|
|
15
16
|
}
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
const controller = new AbortController();
|
|
18
|
+
const timeoutId = setTimeout(() => controller.abort(), API_TIMEOUT_MS);
|
|
19
|
+
let response;
|
|
20
|
+
try {
|
|
21
|
+
response = await fetch(url.toString(), {
|
|
22
|
+
method: "GET",
|
|
23
|
+
headers: {
|
|
24
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
25
|
+
"Content-Type": "application/json"
|
|
26
|
+
},
|
|
27
|
+
signal: controller.signal
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
clearTimeout(timeoutId);
|
|
32
|
+
}
|
|
23
33
|
if (!response.ok) {
|
|
24
34
|
const errorData = await response.json().catch(() => ({}));
|
|
25
35
|
throw new Error(errorData.error?.message ||
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { createRequire } from "module";
|
|
4
5
|
import { registerSkillsTools } from "./tools/skills.js";
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const { version } = require("../package.json");
|
|
5
8
|
/**
|
|
6
9
|
* SkillsMP MCP Server
|
|
7
10
|
*
|
|
@@ -20,7 +23,7 @@ if (!API_KEY) {
|
|
|
20
23
|
// Initialize MCP Server
|
|
21
24
|
const server = new McpServer({
|
|
22
25
|
name: "skillsmp-mcp-server",
|
|
23
|
-
version:
|
|
26
|
+
version: version
|
|
24
27
|
});
|
|
25
28
|
// Register skills search tools
|
|
26
29
|
registerSkillsTools(server, API_KEY);
|
package/dist/tools/skills.js
CHANGED
|
@@ -1,8 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { makeApiRequest, handleApiError, validateSearchResponse, validateAISearchResponse } from "../api.js";
|
|
4
4
|
import { KeywordSearchSchema, AISearchSchema, InstallAndReadSchema } from "../schemas.js";
|
|
5
|
-
const
|
|
5
|
+
const execFileAsync = promisify(execFile);
|
|
6
|
+
// Timeout constants (in milliseconds)
|
|
7
|
+
const TIMEOUTS = {
|
|
8
|
+
READ: 30_000,
|
|
9
|
+
INSTALL: 120_000
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Safely extract error message from unknown error type
|
|
13
|
+
*/
|
|
14
|
+
function getErrorDetails(error) {
|
|
15
|
+
if (error instanceof Error) {
|
|
16
|
+
const execError = error;
|
|
17
|
+
return {
|
|
18
|
+
message: execError.message,
|
|
19
|
+
stderr: execError.stderr
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
if (typeof error === 'object' && error !== null) {
|
|
23
|
+
const e = error;
|
|
24
|
+
return {
|
|
25
|
+
message: String(e.message || 'Unknown error'),
|
|
26
|
+
stderr: typeof e.stderr === 'string' ? e.stderr : undefined
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return { message: String(error) };
|
|
30
|
+
}
|
|
6
31
|
/**
|
|
7
32
|
* Register SkillsMP tools on the MCP server
|
|
8
33
|
*/
|
|
@@ -177,13 +202,11 @@ Examples:
|
|
|
177
202
|
}
|
|
178
203
|
}, async (params) => {
|
|
179
204
|
try {
|
|
180
|
-
const
|
|
205
|
+
const readArgs = ["-y", "openskills", "read", params.skillName];
|
|
181
206
|
// Step 1: Check if skill already exists locally
|
|
182
|
-
let skillExists = false;
|
|
183
207
|
try {
|
|
184
|
-
const { stdout } = await
|
|
208
|
+
const { stdout } = await execFileAsync("npx", readArgs, { timeout: TIMEOUTS.READ });
|
|
185
209
|
if (stdout && !stdout.includes("not found") && !stdout.includes("Error")) {
|
|
186
|
-
skillExists = true;
|
|
187
210
|
const output = formatInstallAndReadResponse(params.repo, params.skillName, stdout, true);
|
|
188
211
|
return {
|
|
189
212
|
content: [{ type: "text", text: output }],
|
|
@@ -198,41 +221,39 @@ Examples:
|
|
|
198
221
|
}
|
|
199
222
|
catch {
|
|
200
223
|
// Skill not found locally, proceed with installation
|
|
201
|
-
skillExists = false;
|
|
202
224
|
}
|
|
203
225
|
// Step 2: Install if not exists
|
|
204
|
-
const
|
|
226
|
+
const installArgs = ["-y", "openskills", "install", params.repo];
|
|
205
227
|
if (params.global)
|
|
206
|
-
|
|
228
|
+
installArgs.push("--global");
|
|
207
229
|
if (params.universal)
|
|
208
|
-
|
|
209
|
-
const installCmd = `npx -y openskills install ${params.repo} ${installFlags.join(" ")}`;
|
|
230
|
+
installArgs.push("--universal");
|
|
210
231
|
let installOutput;
|
|
211
232
|
try {
|
|
212
|
-
const { stdout, stderr } = await
|
|
233
|
+
const { stdout, stderr } = await execFileAsync("npx", installArgs, { timeout: TIMEOUTS.INSTALL });
|
|
213
234
|
installOutput = stdout || stderr;
|
|
214
235
|
}
|
|
215
236
|
catch (installError) {
|
|
216
|
-
const
|
|
237
|
+
const errorDetails = getErrorDetails(installError);
|
|
217
238
|
return {
|
|
218
239
|
content: [{
|
|
219
240
|
type: "text",
|
|
220
|
-
text: `❌ **Installation Failed**\n\nRepository: ${params.repo}\n\nError:\n${
|
|
241
|
+
text: `❌ **Installation Failed**\n\nRepository: ${params.repo}\n\nError:\n${errorDetails.stderr || errorDetails.message}`
|
|
221
242
|
}]
|
|
222
243
|
};
|
|
223
244
|
}
|
|
224
245
|
// Step 3: Read the skill after installation
|
|
225
246
|
let readOutput;
|
|
226
247
|
try {
|
|
227
|
-
const { stdout, stderr } = await
|
|
248
|
+
const { stdout, stderr } = await execFileAsync("npx", readArgs, { timeout: TIMEOUTS.READ });
|
|
228
249
|
readOutput = stdout || stderr;
|
|
229
250
|
}
|
|
230
251
|
catch (readError) {
|
|
231
|
-
const
|
|
252
|
+
const errorDetails = getErrorDetails(readError);
|
|
232
253
|
return {
|
|
233
254
|
content: [{
|
|
234
255
|
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${
|
|
256
|
+
text: `✅ **Installation Succeeded** but **Read Failed**\n\nRepository: ${params.repo}\nSkill: ${params.skillName}\n\n**Install Output:**\n${installOutput}\n\n**Read Error:**\n${errorDetails.stderr || errorDetails.message}\n\n💡 **Tip**: Check if the skill name is correct. Use \`npx openskills list\` to see installed skills.`
|
|
236
257
|
}]
|
|
237
258
|
};
|
|
238
259
|
}
|