ring-skills-mcp 1.0.5 → 1.0.7
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 +25 -13
- package/dist/index.js +61 -102
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ An MCP (Model Context Protocol) service for fetching and installing company Skil
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **list_skills** - Fetch company Skills list, supports search and pagination
|
|
8
|
-
- **install_skill** - Install skill to local project by
|
|
8
|
+
- **install_skill** - Install skill to local project by gitUrl (supports GitHub and GitLab)
|
|
9
9
|
|
|
10
10
|
## Installation
|
|
11
11
|
|
|
@@ -20,8 +20,7 @@ npm run build
|
|
|
20
20
|
|
|
21
21
|
| Variable | Description | Default |
|
|
22
22
|
|----------|-------------|---------|
|
|
23
|
-
| `SKILLS_API_HOST` | API server address |
|
|
24
|
-
| `SKILLS_REPO` | Skills repository path | `anthropics/skills` |
|
|
23
|
+
| `SKILLS_API_HOST` | API server address | None |
|
|
25
24
|
| `SKILLS_AUTH_TOKEN` | API authentication token (required) | None |
|
|
26
25
|
|
|
27
26
|
## Usage in Cursor
|
|
@@ -38,7 +37,6 @@ Add the following MCP configuration in Cursor settings:
|
|
|
38
37
|
"args": ["ring-skills-mcp@latest"],
|
|
39
38
|
"env": {
|
|
40
39
|
"SKILLS_API_HOST": "http://your-api-host:8000",
|
|
41
|
-
"SKILLS_REPO": "your-org/skills-repo",
|
|
42
40
|
"SKILLS_AUTH_TOKEN": "your-auth-token-here"
|
|
43
41
|
}
|
|
44
42
|
}
|
|
@@ -55,8 +53,7 @@ Add the following MCP configuration in Cursor settings:
|
|
|
55
53
|
"command": "node",
|
|
56
54
|
"args": ["/path/to/ring-skills-mcp/dist/index.js"],
|
|
57
55
|
"env": {
|
|
58
|
-
"SKILLS_API_HOST": "
|
|
59
|
-
"SKILLS_REPO": "your-org/skills-repo",
|
|
56
|
+
"SKILLS_API_HOST": "http://your-api-host:8000",
|
|
60
57
|
"SKILLS_AUTH_TOKEN": "your-auth-token-here"
|
|
61
58
|
}
|
|
62
59
|
}
|
|
@@ -73,8 +70,7 @@ Add the following MCP configuration in Cursor settings:
|
|
|
73
70
|
"command": "npx",
|
|
74
71
|
"args": ["tsx", "/path/to/ring-skills-mcp/src/index.ts"],
|
|
75
72
|
"env": {
|
|
76
|
-
"SKILLS_API_HOST": "
|
|
77
|
-
"SKILLS_REPO": "your-org/skills-repo",
|
|
73
|
+
"SKILLS_API_HOST": "http://your-api-host:8000",
|
|
78
74
|
"SKILLS_AUTH_TOKEN": "your-auth-token-here"
|
|
79
75
|
}
|
|
80
76
|
}
|
|
@@ -102,17 +98,33 @@ Search for skills containing "pdf"
|
|
|
102
98
|
|
|
103
99
|
### install_skill
|
|
104
100
|
|
|
105
|
-
Install the specified skill to local project.
|
|
101
|
+
Install the specified skill to local project by gitUrl.
|
|
106
102
|
|
|
107
103
|
**Parameters:**
|
|
108
|
-
- `
|
|
104
|
+
- `gitUrl` (required): Git URL of the skill. Supports both GitHub and GitLab URLs.
|
|
109
105
|
- `projectPath` (required): Project path where the skill will be installed. Please provide the project root directory path of the currently opened file in IDE
|
|
110
|
-
- `targetDir` (optional): Installation target directory, default is
|
|
111
|
-
|
|
106
|
+
- `targetDir` (optional): Installation target directory, default is `.claude/skills`
|
|
107
|
+
|
|
108
|
+
**Supported URL Formats:**
|
|
109
|
+
|
|
110
|
+
| Platform | URL Format | Installation Method |
|
|
111
|
+
|----------|------------|---------------------|
|
|
112
|
+
| GitHub | `https://github.com/{owner}/{repo}/tree/{branch}/{path}` | `npx degit` (fast, no git history) |
|
|
113
|
+
| GitLab | `https://{host}/{owner}/{repo}/-/tree/{branch}/{path}` | `git sparse-checkout` (supports private repos) |
|
|
114
|
+
|
|
115
|
+
**Example URLs:**
|
|
116
|
+
```
|
|
117
|
+
# GitHub
|
|
118
|
+
https://github.com/anthropics/skills/tree/main/skills/docx
|
|
119
|
+
|
|
120
|
+
# GitLab (self-hosted)
|
|
121
|
+
https://git.ringcentral.com/ai-testing/aiter-skills/-/tree/main/.agent/skills/nova-scripts-generator
|
|
122
|
+
```
|
|
112
123
|
|
|
113
124
|
**Example usage:**
|
|
114
125
|
```
|
|
115
|
-
Install
|
|
126
|
+
Install skill from GitHub URL to /Users/xxx/my-project
|
|
127
|
+
Install skill from GitLab URL to /Users/xxx/my-project
|
|
116
128
|
```
|
|
117
129
|
|
|
118
130
|
## Development
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,6 @@ 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
|
-
const DEFAULT_SKILLS_REPO = process.env.SKILLS_REPO || "anthropics/skills";
|
|
12
11
|
const DEFAULT_AUTH_TOKEN = process.env.SKILLS_AUTH_TOKEN || "";
|
|
13
12
|
// Create MCP server
|
|
14
13
|
const server = new McpServer({
|
|
@@ -99,18 +98,43 @@ function parseGitUrl(gitUrl) {
|
|
|
99
98
|
return null;
|
|
100
99
|
}
|
|
101
100
|
/**
|
|
102
|
-
*
|
|
101
|
+
* Install Skill from GitHub using degit
|
|
103
102
|
*/
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
103
|
+
async function installFromGitHub(urlInfo, destPath) {
|
|
104
|
+
// GitHub: owner/repo/path#branch
|
|
105
|
+
const sourcePath = `${urlInfo.owner}/${urlInfo.repo}/${urlInfo.skillPath}#${urlInfo.branch}`;
|
|
106
|
+
const command = `npx degit ${sourcePath} ${destPath}`;
|
|
107
|
+
const { stdout, stderr } = await execAsync(command);
|
|
108
|
+
const output = stdout || stderr || "Installation completed";
|
|
109
|
+
return `✅ Skill "${urlInfo.skillName}" has been successfully installed to ${destPath}\n\nSource: ${sourcePath}\n${output}`;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Install Skill from GitLab using git sparse-checkout
|
|
113
|
+
* For private GitLab repositories that degit doesn't support
|
|
114
|
+
*/
|
|
115
|
+
async function installFromGitLab(urlInfo, destPath) {
|
|
116
|
+
const skillsDir = path.dirname(destPath);
|
|
117
|
+
const tempDir = `temp-${urlInfo.skillName}-${Date.now()}`;
|
|
118
|
+
const repoUrl = `https://${urlInfo.host}/${urlInfo.owner}/${urlInfo.repo}.git`;
|
|
119
|
+
// Build command sequence:
|
|
120
|
+
// 1. Create target directory
|
|
121
|
+
// 2. Clone with sparse checkout
|
|
122
|
+
// 3. Set sparse-checkout path
|
|
123
|
+
// 4. Move skill folder to destination
|
|
124
|
+
// 5. Cleanup temp directory
|
|
125
|
+
const commands = [
|
|
126
|
+
`mkdir -p ${skillsDir}`,
|
|
127
|
+
`cd ${skillsDir}`,
|
|
128
|
+
`git clone --depth 1 --filter=blob:none --sparse ${repoUrl} ${tempDir}`,
|
|
129
|
+
`cd ${tempDir}`,
|
|
130
|
+
`git sparse-checkout set ${urlInfo.skillPath}`,
|
|
131
|
+
`cd ..`,
|
|
132
|
+
`mv ${tempDir}/${urlInfo.skillPath} ${urlInfo.skillName}`,
|
|
133
|
+
`rm -rf ${tempDir}`,
|
|
134
|
+
].join(" && ");
|
|
135
|
+
const { stdout, stderr } = await execAsync(commands);
|
|
136
|
+
const output = stdout || stderr || "Installation completed";
|
|
137
|
+
return `✅ Skill "${urlInfo.skillName}" has been successfully installed to ${destPath}\n\nSource: ${repoUrl} (${urlInfo.skillPath})\n${output}`;
|
|
114
138
|
}
|
|
115
139
|
/**
|
|
116
140
|
* Install Skill to specified project using gitUrl
|
|
@@ -122,38 +146,18 @@ async function installSkillFromGitUrl(gitUrl, projectPath, targetDir = ".claude/
|
|
|
122
146
|
}
|
|
123
147
|
const skillName = urlInfo.skillName;
|
|
124
148
|
const destPath = path.join(projectPath, targetDir, skillName);
|
|
125
|
-
const sourcePath = buildDegitSource(urlInfo);
|
|
126
|
-
if (!sourcePath) {
|
|
127
|
-
throw new Error(`Could not build degit source from URL: ${gitUrl}`);
|
|
128
|
-
}
|
|
129
|
-
const command = `npx degit ${sourcePath} ${destPath}`;
|
|
130
149
|
try {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
150
|
+
if (urlInfo.type === "github") {
|
|
151
|
+
// Use degit for GitHub (faster, no git history)
|
|
152
|
+
return await installFromGitHub(urlInfo, destPath);
|
|
153
|
+
}
|
|
154
|
+
else if (urlInfo.type === "gitlab") {
|
|
155
|
+
// Use git sparse-checkout for GitLab (supports private repos)
|
|
156
|
+
return await installFromGitLab(urlInfo, destPath);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
throw new Error(`Unsupported git URL type: ${gitUrl}`);
|
|
138
160
|
}
|
|
139
|
-
throw new Error("Failed to install Skill: Unknown error");
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Install Skill to specified project using skill name and repo
|
|
144
|
-
*/
|
|
145
|
-
async function installSkillFromRepo(skillName, projectPath, targetDir = ".claude/skills", repo = DEFAULT_SKILLS_REPO) {
|
|
146
|
-
// Convert skill name to lowercase
|
|
147
|
-
const normalizedName = skillName.toLowerCase();
|
|
148
|
-
// Build full installation path
|
|
149
|
-
const destPath = path.join(projectPath, targetDir, normalizedName);
|
|
150
|
-
// Build degit command
|
|
151
|
-
const sourcePath = `${repo}/skills/${normalizedName}`;
|
|
152
|
-
const command = `npx degit ${sourcePath} ${destPath}`;
|
|
153
|
-
try {
|
|
154
|
-
const { stdout, stderr } = await execAsync(command);
|
|
155
|
-
const output = stdout || stderr || "Installation completed";
|
|
156
|
-
return `✅ Skill "${normalizedName}" has been successfully installed to ${destPath}\n${output}`;
|
|
157
161
|
}
|
|
158
162
|
catch (error) {
|
|
159
163
|
if (error instanceof Error) {
|
|
@@ -232,9 +236,8 @@ server.tool("list_skills", "Fetch company Skills list. You can search for specif
|
|
|
232
236
|
}
|
|
233
237
|
});
|
|
234
238
|
// Register tool: Install Skill
|
|
235
|
-
server.tool("install_skill", "Install skill to local project by
|
|
236
|
-
|
|
237
|
-
gitUrl: z.string().optional().describe("Git URL of the skill (e.g., 'https://github.com/anthropics/skills/tree/main/skills/webapp-testing'). The skill name will be extracted from this URL. Either skillName or gitUrl must be provided."),
|
|
239
|
+
server.tool("install_skill", "Install skill to local project by gitUrl. Uses npx degit for GitHub repositories and git sparse-checkout for GitLab repositories. Project path is required, which can be the project path of the currently opened file in IDE.", {
|
|
240
|
+
gitUrl: z.string().describe("Git URL of the skill (e.g., 'https://github.com/anthropics/skills/tree/main/skills/webapp-testing'). The skill name will be extracted from this URL."),
|
|
238
241
|
projectPath: z
|
|
239
242
|
.string()
|
|
240
243
|
.optional()
|
|
@@ -243,11 +246,7 @@ server.tool("install_skill", "Install skill to local project by skill name or gi
|
|
|
243
246
|
.string()
|
|
244
247
|
.optional()
|
|
245
248
|
.describe("Installation target directory, default: .claude/skills"),
|
|
246
|
-
|
|
247
|
-
.string()
|
|
248
|
-
.optional()
|
|
249
|
-
.describe(`Skills repository path, default: ${DEFAULT_SKILLS_REPO}`),
|
|
250
|
-
}, async ({ skillName, gitUrl, projectPath, targetDir, repo }) => {
|
|
249
|
+
}, async ({ gitUrl, projectPath, targetDir }) => {
|
|
251
250
|
// Check if project path is provided
|
|
252
251
|
if (!projectPath) {
|
|
253
252
|
return {
|
|
@@ -260,63 +259,23 @@ server.tool("install_skill", "Install skill to local project by skill name or gi
|
|
|
260
259
|
isError: true,
|
|
261
260
|
};
|
|
262
261
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
},
|
|
274
|
-
],
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
catch (error) {
|
|
278
|
-
return {
|
|
279
|
-
content: [
|
|
280
|
-
{
|
|
281
|
-
type: "text",
|
|
282
|
-
text: `❌ Error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
283
|
-
},
|
|
284
|
-
],
|
|
285
|
-
isError: true,
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
else if (skillName) {
|
|
290
|
-
// Install from repo using skill name
|
|
291
|
-
try {
|
|
292
|
-
const result = await installSkillFromRepo(skillName, projectPath, targetDir || ".claude/skills", repo || DEFAULT_SKILLS_REPO);
|
|
293
|
-
return {
|
|
294
|
-
content: [
|
|
295
|
-
{
|
|
296
|
-
type: "text",
|
|
297
|
-
text: result,
|
|
298
|
-
},
|
|
299
|
-
],
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
catch (error) {
|
|
303
|
-
return {
|
|
304
|
-
content: [
|
|
305
|
-
{
|
|
306
|
-
type: "text",
|
|
307
|
-
text: `❌ Error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
308
|
-
},
|
|
309
|
-
],
|
|
310
|
-
isError: true,
|
|
311
|
-
};
|
|
312
|
-
}
|
|
262
|
+
try {
|
|
263
|
+
const result = await installSkillFromGitUrl(gitUrl, projectPath, targetDir || ".claude/skills");
|
|
264
|
+
return {
|
|
265
|
+
content: [
|
|
266
|
+
{
|
|
267
|
+
type: "text",
|
|
268
|
+
text: result,
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
};
|
|
313
272
|
}
|
|
314
|
-
|
|
273
|
+
catch (error) {
|
|
315
274
|
return {
|
|
316
275
|
content: [
|
|
317
276
|
{
|
|
318
277
|
type: "text",
|
|
319
|
-
text:
|
|
278
|
+
text: `❌ Error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
320
279
|
},
|
|
321
280
|
],
|
|
322
281
|
isError: true,
|