ring-skills-mcp 1.0.0 → 1.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.
- package/dist/index.js +40 -40
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,17 +6,17 @@ import { exec } from "child_process";
|
|
|
6
6
|
import { promisify } from "util";
|
|
7
7
|
import path from "path";
|
|
8
8
|
const execAsync = promisify(exec);
|
|
9
|
-
//
|
|
9
|
+
// Configuration
|
|
10
10
|
const DEFAULT_API_HOST = process.env.SKILLS_API_HOST || "";
|
|
11
11
|
const DEFAULT_SKILLS_REPO = process.env.SKILLS_REPO || "anthropics/skills";
|
|
12
12
|
const DEFAULT_AUTH_TOKEN = process.env.SKILLS_AUTH_TOKEN || "";
|
|
13
|
-
//
|
|
13
|
+
// Create MCP server
|
|
14
14
|
const server = new McpServer({
|
|
15
15
|
name: "ring-skills-mcp",
|
|
16
16
|
version: "1.0.0",
|
|
17
17
|
});
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* Fetch Skills list
|
|
20
20
|
*/
|
|
21
21
|
async function fetchSkills(host, search = "", pageSize = 5000, token = "") {
|
|
22
22
|
const url = `${host}/api/skills?search=${encodeURIComponent(search)}&pageSize=${pageSize}`;
|
|
@@ -27,10 +27,10 @@ async function fetchSkills(host, search = "", pageSize = 5000, token = "") {
|
|
|
27
27
|
}
|
|
28
28
|
const response = await fetch(url, { headers });
|
|
29
29
|
if (!response.ok) {
|
|
30
|
-
throw new Error(`API
|
|
30
|
+
throw new Error(`API request failed: ${response.status} ${response.statusText}`);
|
|
31
31
|
}
|
|
32
32
|
const data = (await response.json());
|
|
33
|
-
//
|
|
33
|
+
// Compatible with different API response formats
|
|
34
34
|
if (Array.isArray(data)) {
|
|
35
35
|
return data;
|
|
36
36
|
}
|
|
@@ -47,46 +47,46 @@ async function fetchSkills(host, search = "", pageSize = 5000, token = "") {
|
|
|
47
47
|
}
|
|
48
48
|
catch (error) {
|
|
49
49
|
if (error instanceof Error) {
|
|
50
|
-
throw new Error(
|
|
50
|
+
throw new Error(`Failed to fetch Skills list: ${error.message}`);
|
|
51
51
|
}
|
|
52
|
-
throw new Error("
|
|
52
|
+
throw new Error("Failed to fetch Skills list: Unknown error");
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
56
|
+
* Install Skill to specified project
|
|
57
57
|
*/
|
|
58
58
|
async function installSkill(skillName, projectPath, targetDir = "skills", repo = DEFAULT_SKILLS_REPO) {
|
|
59
|
-
//
|
|
59
|
+
// Convert skill name to lowercase
|
|
60
60
|
const normalizedName = skillName.toLowerCase();
|
|
61
|
-
//
|
|
61
|
+
// Build full installation path
|
|
62
62
|
const destPath = path.join(projectPath, targetDir, normalizedName);
|
|
63
|
-
//
|
|
63
|
+
// Build degit command
|
|
64
64
|
const sourcePath = `${repo}/skills/${normalizedName}`;
|
|
65
65
|
const command = `npx degit ${sourcePath} ${destPath}`;
|
|
66
66
|
try {
|
|
67
67
|
const { stdout, stderr } = await execAsync(command);
|
|
68
|
-
const output = stdout || stderr || "
|
|
69
|
-
return `✅ Skill "${normalizedName}"
|
|
68
|
+
const output = stdout || stderr || "Installation completed";
|
|
69
|
+
return `✅ Skill "${normalizedName}" has been successfully installed to ${destPath}\n${output}`;
|
|
70
70
|
}
|
|
71
71
|
catch (error) {
|
|
72
72
|
if (error instanceof Error) {
|
|
73
|
-
throw new Error(
|
|
73
|
+
throw new Error(`Failed to install Skill: ${error.message}`);
|
|
74
74
|
}
|
|
75
|
-
throw new Error("
|
|
75
|
+
throw new Error("Failed to install Skill: Unknown error");
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
//
|
|
79
|
-
server.tool("list_skills", "
|
|
78
|
+
// Register tool: Fetch Skills list
|
|
79
|
+
server.tool("list_skills", "Fetch company Skills list. You can search for specific skills using the search parameter. Authorization token is required for authentication.", {
|
|
80
80
|
host: z
|
|
81
81
|
.string()
|
|
82
82
|
.optional()
|
|
83
|
-
.describe(`API
|
|
84
|
-
search: z.string().optional().describe("
|
|
85
|
-
pageSize: z.number().optional().describe("
|
|
83
|
+
.describe(`API server address, default: ${DEFAULT_API_HOST}`),
|
|
84
|
+
search: z.string().optional().describe("Search keyword for filtering skills"),
|
|
85
|
+
pageSize: z.number().optional().describe("Number of results per page, default: 5000"),
|
|
86
86
|
token: z
|
|
87
87
|
.string()
|
|
88
88
|
.optional()
|
|
89
|
-
.describe("Authorization token (
|
|
89
|
+
.describe("Authorization token (required) for API authentication. Can be passed as parameter or set via environment variable SKILLS_AUTH_TOKEN"),
|
|
90
90
|
}, async ({ host, search, pageSize, token }) => {
|
|
91
91
|
try {
|
|
92
92
|
const apiHost = host || DEFAULT_API_HOST;
|
|
@@ -96,7 +96,7 @@ server.tool("list_skills", "获取公司Skills列表。可以通过search参数
|
|
|
96
96
|
content: [
|
|
97
97
|
{
|
|
98
98
|
type: "text",
|
|
99
|
-
text: "❌
|
|
99
|
+
text: "❌ Error: Missing Authorization token. Please provide via token parameter or set environment variable SKILLS_AUTH_TOKEN.",
|
|
100
100
|
},
|
|
101
101
|
],
|
|
102
102
|
isError: true,
|
|
@@ -108,16 +108,16 @@ server.tool("list_skills", "获取公司Skills列表。可以通过search参数
|
|
|
108
108
|
content: [
|
|
109
109
|
{
|
|
110
110
|
type: "text",
|
|
111
|
-
text: "
|
|
111
|
+
text: "No Skills found. Please check if the API address is correct or try different search keywords.",
|
|
112
112
|
},
|
|
113
113
|
],
|
|
114
114
|
};
|
|
115
115
|
}
|
|
116
|
-
//
|
|
116
|
+
// Format output
|
|
117
117
|
const skillsList = skills
|
|
118
118
|
.map((skill, index) => {
|
|
119
|
-
const title = skill.title || skill.id || "
|
|
120
|
-
const summary = skill.summary || "
|
|
119
|
+
const title = skill.title || skill.id || "Unnamed";
|
|
120
|
+
const summary = skill.summary || "No description";
|
|
121
121
|
const category = skill.category ? ` [${skill.category}]` : "";
|
|
122
122
|
return `${index + 1}. **${title}**${category}\n ${summary}`;
|
|
123
123
|
})
|
|
@@ -126,7 +126,7 @@ server.tool("list_skills", "获取公司Skills列表。可以通过search参数
|
|
|
126
126
|
content: [
|
|
127
127
|
{
|
|
128
128
|
type: "text",
|
|
129
|
-
text: `## Skills
|
|
129
|
+
text: `## Skills List (${skills.length} total)\n\n${skillsList}`,
|
|
130
130
|
},
|
|
131
131
|
],
|
|
132
132
|
};
|
|
@@ -136,36 +136,36 @@ server.tool("list_skills", "获取公司Skills列表。可以通过search参数
|
|
|
136
136
|
content: [
|
|
137
137
|
{
|
|
138
138
|
type: "text",
|
|
139
|
-
text: `❌
|
|
139
|
+
text: `❌ Error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
140
140
|
},
|
|
141
141
|
],
|
|
142
142
|
isError: true,
|
|
143
143
|
};
|
|
144
144
|
}
|
|
145
145
|
});
|
|
146
|
-
//
|
|
147
|
-
server.tool("install_skill", "
|
|
148
|
-
skillName: z.string().describe("
|
|
146
|
+
// Register tool: Install Skill
|
|
147
|
+
server.tool("install_skill", "Install skill to local project by skill name. Uses npx degit to download skill template from remote repository. Project path is required, which can be the project path of the currently opened file in IDE.", {
|
|
148
|
+
skillName: z.string().describe("Name of the skill to install"),
|
|
149
149
|
projectPath: z
|
|
150
150
|
.string()
|
|
151
151
|
.optional()
|
|
152
|
-
.describe("
|
|
152
|
+
.describe("Project path (required), skill will be installed to this project. Please provide the project root directory path of the currently opened file in IDE"),
|
|
153
153
|
targetDir: z
|
|
154
154
|
.string()
|
|
155
155
|
.optional()
|
|
156
|
-
.describe("
|
|
156
|
+
.describe("Installation target directory, default: skills"),
|
|
157
157
|
repo: z
|
|
158
158
|
.string()
|
|
159
159
|
.optional()
|
|
160
|
-
.describe(`Skills
|
|
160
|
+
.describe(`Skills repository path, default: ${DEFAULT_SKILLS_REPO}`),
|
|
161
161
|
}, async ({ skillName, projectPath, targetDir, repo }) => {
|
|
162
|
-
//
|
|
162
|
+
// Check if project path is provided
|
|
163
163
|
if (!projectPath) {
|
|
164
164
|
return {
|
|
165
165
|
content: [
|
|
166
166
|
{
|
|
167
167
|
type: "text",
|
|
168
|
-
text: "❌
|
|
168
|
+
text: "❌ Please provide project path (projectPath).\n\nYou can provide the project root directory path of the currently opened file in IDE, skill will be installed to the skills directory of that project.\n\nExample: /Users/xxx/my-project",
|
|
169
169
|
},
|
|
170
170
|
],
|
|
171
171
|
isError: true,
|
|
@@ -187,20 +187,20 @@ server.tool("install_skill", "根据skill名称安装skill到本地项目。使
|
|
|
187
187
|
content: [
|
|
188
188
|
{
|
|
189
189
|
type: "text",
|
|
190
|
-
text: `❌
|
|
190
|
+
text: `❌ Error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
191
191
|
},
|
|
192
192
|
],
|
|
193
193
|
isError: true,
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
});
|
|
197
|
-
//
|
|
197
|
+
// Start server
|
|
198
198
|
async function main() {
|
|
199
199
|
const transport = new StdioServerTransport();
|
|
200
200
|
await server.connect(transport);
|
|
201
|
-
console.error("Ring Skills MCP
|
|
201
|
+
console.error("Ring Skills MCP service started");
|
|
202
202
|
}
|
|
203
203
|
main().catch((error) => {
|
|
204
|
-
console.error("
|
|
204
|
+
console.error("Service startup failed:", error);
|
|
205
205
|
process.exit(1);
|
|
206
206
|
});
|