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.
Files changed (3) hide show
  1. package/README.md +25 -13
  2. package/dist/index.js +61 -102
  3. 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 skill name
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 | `SKILLS_API_HOST` |
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": "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": "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
- - `skillName` (required): Name of the skill to install
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 `skills`
111
- - `repo` (optional): Skills repository path
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 pdf skill to /Users/xxx/my-project project
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
- * Build degit source path from git URL info
101
+ * Install Skill from GitHub using degit
103
102
  */
104
- function buildDegitSource(urlInfo) {
105
- if (urlInfo.type === "github") {
106
- // GitHub: owner/repo/path#branch
107
- return `${urlInfo.owner}/${urlInfo.repo}/${urlInfo.skillPath}#${urlInfo.branch}`;
108
- }
109
- else if (urlInfo.type === "gitlab") {
110
- // GitLab (self-hosted): host:owner/repo/path#branch
111
- return `${urlInfo.host}:${urlInfo.owner}/${urlInfo.repo}/${urlInfo.skillPath}#${urlInfo.branch}`;
112
- }
113
- return "";
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
- const { stdout, stderr } = await execAsync(command);
132
- const output = stdout || stderr || "Installation completed";
133
- return `✅ Skill "${skillName}" has been successfully installed to ${destPath}\n\nSource: ${sourcePath}\n${output}`;
134
- }
135
- catch (error) {
136
- if (error instanceof Error) {
137
- throw new Error(`Failed to install Skill: ${error.message}`);
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 skill name or gitUrl. 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.", {
236
- skillName: z.string().optional().describe("Name of the skill to install (e.g., 'webapp-testing'). Either skillName or gitUrl must be provided."),
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
- repo: z
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
- // Priority: gitUrl > skillName
264
- if (gitUrl) {
265
- // Install from gitUrl (supports GitHub and GitLab)
266
- try {
267
- const result = await installSkillFromGitUrl(gitUrl, projectPath, targetDir || ".claude/skills");
268
- return {
269
- content: [
270
- {
271
- type: "text",
272
- text: result,
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
- else {
273
+ catch (error) {
315
274
  return {
316
275
  content: [
317
276
  {
318
277
  type: "text",
319
- text: "❌ Error: Either skillName or gitUrl must be provided.",
278
+ text: `❌ Error: ${error instanceof Error ? error.message : "Unknown error"}`,
320
279
  },
321
280
  ],
322
281
  isError: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ring-skills-mcp",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "MCP service for fetching and installing company Skills",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",