ring-skills-mcp 1.0.0
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 +133 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +206 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Ring Skills MCP Service
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) service for fetching and installing company Skills.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **list_skills** - Fetch company Skills list, supports search and pagination
|
|
8
|
+
- **install_skill** - Install skill to local project by skill name
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install
|
|
14
|
+
npm run build
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Configuration
|
|
18
|
+
|
|
19
|
+
### Environment Variables
|
|
20
|
+
|
|
21
|
+
| Variable | Description | Default |
|
|
22
|
+
|----------|-------------|---------|
|
|
23
|
+
| `SKILLS_API_HOST` | API server address | `SKILLS_API_HOST` |
|
|
24
|
+
| `SKILLS_REPO` | Skills repository path | `anthropics/skills` |
|
|
25
|
+
| `SKILLS_AUTH_TOKEN` | API authentication token (required) | None |
|
|
26
|
+
|
|
27
|
+
## Usage in Cursor
|
|
28
|
+
|
|
29
|
+
Add the following MCP configuration in Cursor settings:
|
|
30
|
+
|
|
31
|
+
### Option 1: Run with Node
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"mcpServers": {
|
|
36
|
+
"ring-skills-mcp": {
|
|
37
|
+
"command": "node",
|
|
38
|
+
"args": ["/path/to/ring-skills-mcp/dist/index.js"],
|
|
39
|
+
"env": {
|
|
40
|
+
"SKILLS_API_HOST": "SKILLS_API_HOST",
|
|
41
|
+
"SKILLS_REPO": "your-org/skills-repo",
|
|
42
|
+
"SKILLS_AUTH_TOKEN": "your-auth-token-here"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Option 2: Run with npx (development mode)
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"ring-skills-mcp": {
|
|
55
|
+
"command": "npx",
|
|
56
|
+
"args": ["tsx", "/path/to/ring-skills-mcp/src/index.ts"],
|
|
57
|
+
"env": {
|
|
58
|
+
"SKILLS_API_HOST": "SKILLS_API_HOST",
|
|
59
|
+
"SKILLS_REPO": "your-org/skills-repo",
|
|
60
|
+
"SKILLS_AUTH_TOKEN": "your-auth-token-here"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Tool Documentation
|
|
68
|
+
|
|
69
|
+
### list_skills
|
|
70
|
+
|
|
71
|
+
Fetch company Skills list.
|
|
72
|
+
|
|
73
|
+
**Parameters:**
|
|
74
|
+
- `host` (optional): API server address
|
|
75
|
+
- `search` (optional): Search keyword
|
|
76
|
+
- `pageSize` (optional): Number of results per page, default 5000
|
|
77
|
+
- `token` (required): Authorization token, can be passed as parameter or set via environment variable `SKILLS_AUTH_TOKEN`
|
|
78
|
+
|
|
79
|
+
**Example usage:**
|
|
80
|
+
```
|
|
81
|
+
Get all skills list
|
|
82
|
+
Search for skills containing "pdf"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### install_skill
|
|
86
|
+
|
|
87
|
+
Install the specified skill to local project.
|
|
88
|
+
|
|
89
|
+
**Parameters:**
|
|
90
|
+
- `skillName` (required): Name of the skill to install
|
|
91
|
+
- `projectPath` (required): Project path where the skill will be installed. Please provide the project root directory path of the currently opened file in IDE
|
|
92
|
+
- `targetDir` (optional): Installation target directory, default is `skills`
|
|
93
|
+
- `repo` (optional): Skills repository path
|
|
94
|
+
|
|
95
|
+
**Example usage:**
|
|
96
|
+
```
|
|
97
|
+
Install pdf skill to /Users/xxx/my-project project
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Development
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Run in development mode
|
|
104
|
+
npm run dev
|
|
105
|
+
|
|
106
|
+
# Build
|
|
107
|
+
npm run build
|
|
108
|
+
|
|
109
|
+
# Run built version
|
|
110
|
+
npm start
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## API Response Format
|
|
114
|
+
|
|
115
|
+
The service supports the following API response formats:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
// Format 1: Direct array
|
|
119
|
+
[{ "id": "1", "name": "skill1" }]
|
|
120
|
+
|
|
121
|
+
// Format 2: data field
|
|
122
|
+
{ "data": [{ "id": "1", "name": "skill1" }] }
|
|
123
|
+
|
|
124
|
+
// Format 3: skills field
|
|
125
|
+
{ "skills": [{ "id": "1", "name": "skill1" }] }
|
|
126
|
+
|
|
127
|
+
// Format 4: items field
|
|
128
|
+
{ "items": [{ "id": "1", "name": "skill1" }] }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { exec } from "child_process";
|
|
6
|
+
import { promisify } from "util";
|
|
7
|
+
import path from "path";
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
// 配置
|
|
10
|
+
const DEFAULT_API_HOST = process.env.SKILLS_API_HOST || "";
|
|
11
|
+
const DEFAULT_SKILLS_REPO = process.env.SKILLS_REPO || "anthropics/skills";
|
|
12
|
+
const DEFAULT_AUTH_TOKEN = process.env.SKILLS_AUTH_TOKEN || "";
|
|
13
|
+
// 创建MCP服务器
|
|
14
|
+
const server = new McpServer({
|
|
15
|
+
name: "ring-skills-mcp",
|
|
16
|
+
version: "1.0.0",
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* 获取Skills列表
|
|
20
|
+
*/
|
|
21
|
+
async function fetchSkills(host, search = "", pageSize = 5000, token = "") {
|
|
22
|
+
const url = `${host}/api/skills?search=${encodeURIComponent(search)}&pageSize=${pageSize}`;
|
|
23
|
+
try {
|
|
24
|
+
const headers = {};
|
|
25
|
+
if (token) {
|
|
26
|
+
headers["Authorization"] = token;
|
|
27
|
+
}
|
|
28
|
+
const response = await fetch(url, { headers });
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
throw new Error(`API请求失败: ${response.status} ${response.statusText}`);
|
|
31
|
+
}
|
|
32
|
+
const data = (await response.json());
|
|
33
|
+
// 兼容不同的API返回格式
|
|
34
|
+
if (Array.isArray(data)) {
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
37
|
+
if (data.data && Array.isArray(data.data)) {
|
|
38
|
+
return data.data;
|
|
39
|
+
}
|
|
40
|
+
if (data.skills && Array.isArray(data.skills)) {
|
|
41
|
+
return data.skills;
|
|
42
|
+
}
|
|
43
|
+
if (data.items && Array.isArray(data.items)) {
|
|
44
|
+
return data.items;
|
|
45
|
+
}
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error instanceof Error) {
|
|
50
|
+
throw new Error(`获取Skills列表失败: ${error.message}`);
|
|
51
|
+
}
|
|
52
|
+
throw new Error("获取Skills列表失败: 未知错误");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 安装Skill到指定项目
|
|
57
|
+
*/
|
|
58
|
+
async function installSkill(skillName, projectPath, targetDir = "skills", repo = DEFAULT_SKILLS_REPO) {
|
|
59
|
+
// 将skill名称转换为小写
|
|
60
|
+
const normalizedName = skillName.toLowerCase();
|
|
61
|
+
// 构建完整安装路径
|
|
62
|
+
const destPath = path.join(projectPath, targetDir, normalizedName);
|
|
63
|
+
// 构建degit命令
|
|
64
|
+
const sourcePath = `${repo}/skills/${normalizedName}`;
|
|
65
|
+
const command = `npx degit ${sourcePath} ${destPath}`;
|
|
66
|
+
try {
|
|
67
|
+
const { stdout, stderr } = await execAsync(command);
|
|
68
|
+
const output = stdout || stderr || "安装完成";
|
|
69
|
+
return `✅ Skill "${normalizedName}" 已成功安装到 ${destPath}\n${output}`;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof Error) {
|
|
73
|
+
throw new Error(`安装Skill失败: ${error.message}`);
|
|
74
|
+
}
|
|
75
|
+
throw new Error("安装Skill失败: 未知错误");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// 注册工具: 获取Skills列表
|
|
79
|
+
server.tool("list_skills", "获取公司Skills列表。可以通过search参数搜索特定的skill。需要提供Authorization token进行认证。", {
|
|
80
|
+
host: z
|
|
81
|
+
.string()
|
|
82
|
+
.optional()
|
|
83
|
+
.describe(`API服务器地址,默认: ${DEFAULT_API_HOST}`),
|
|
84
|
+
search: z.string().optional().describe("搜索关键词,用于过滤skills"),
|
|
85
|
+
pageSize: z.number().optional().describe("每页返回数量,默认: 5000"),
|
|
86
|
+
token: z
|
|
87
|
+
.string()
|
|
88
|
+
.optional()
|
|
89
|
+
.describe("Authorization token (必需),用于API认证。可通过此参数传入或设置环境变量SKILLS_AUTH_TOKEN"),
|
|
90
|
+
}, async ({ host, search, pageSize, token }) => {
|
|
91
|
+
try {
|
|
92
|
+
const apiHost = host || DEFAULT_API_HOST;
|
|
93
|
+
const authToken = token || DEFAULT_AUTH_TOKEN;
|
|
94
|
+
if (!authToken) {
|
|
95
|
+
return {
|
|
96
|
+
content: [
|
|
97
|
+
{
|
|
98
|
+
type: "text",
|
|
99
|
+
text: "❌ 错误: 缺少Authorization token。请通过token参数提供,或设置环境变量SKILLS_AUTH_TOKEN。",
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
isError: true,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const skills = await fetchSkills(apiHost, search || "", pageSize || 5000, authToken);
|
|
106
|
+
if (skills.length === 0) {
|
|
107
|
+
return {
|
|
108
|
+
content: [
|
|
109
|
+
{
|
|
110
|
+
type: "text",
|
|
111
|
+
text: "未找到任何Skills。请检查API地址是否正确或尝试不同的搜索关键词。",
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// 格式化输出
|
|
117
|
+
const skillsList = skills
|
|
118
|
+
.map((skill, index) => {
|
|
119
|
+
const title = skill.title || skill.id || "未命名";
|
|
120
|
+
const summary = skill.summary || "无描述";
|
|
121
|
+
const category = skill.category ? ` [${skill.category}]` : "";
|
|
122
|
+
return `${index + 1}. **${title}**${category}\n ${summary}`;
|
|
123
|
+
})
|
|
124
|
+
.join("\n\n");
|
|
125
|
+
return {
|
|
126
|
+
content: [
|
|
127
|
+
{
|
|
128
|
+
type: "text",
|
|
129
|
+
text: `## Skills列表 (共 ${skills.length} 个)\n\n${skillsList}`,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
return {
|
|
136
|
+
content: [
|
|
137
|
+
{
|
|
138
|
+
type: "text",
|
|
139
|
+
text: `❌ 错误: ${error instanceof Error ? error.message : "未知错误"}`,
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
isError: true,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
// 注册工具: 安装Skill
|
|
147
|
+
server.tool("install_skill", "根据skill名称安装skill到本地项目。使用npx degit从远程仓库下载skill模板。需要提供项目路径,可以是当前IDE打开文件所在的项目路径。", {
|
|
148
|
+
skillName: z.string().describe("要安装的skill名称"),
|
|
149
|
+
projectPath: z
|
|
150
|
+
.string()
|
|
151
|
+
.optional()
|
|
152
|
+
.describe("项目路径 (必需),skill将安装到此项目下。请提供当前IDE打开文件所在的项目根目录路径"),
|
|
153
|
+
targetDir: z
|
|
154
|
+
.string()
|
|
155
|
+
.optional()
|
|
156
|
+
.describe("安装目标目录,默认: skills"),
|
|
157
|
+
repo: z
|
|
158
|
+
.string()
|
|
159
|
+
.optional()
|
|
160
|
+
.describe(`Skills仓库路径,默认: ${DEFAULT_SKILLS_REPO}`),
|
|
161
|
+
}, async ({ skillName, projectPath, targetDir, repo }) => {
|
|
162
|
+
// 检查是否提供了项目路径
|
|
163
|
+
if (!projectPath) {
|
|
164
|
+
return {
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
type: "text",
|
|
168
|
+
text: "❌ 请提供项目路径 (projectPath)。\n\n您可以提供当前IDE打开文件所在的项目根目录路径,skill将安装到该项目的 skills 目录下。\n\n例如: /Users/xxx/my-project",
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
isError: true,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
const result = await installSkill(skillName, projectPath, targetDir || "skills", repo || DEFAULT_SKILLS_REPO);
|
|
176
|
+
return {
|
|
177
|
+
content: [
|
|
178
|
+
{
|
|
179
|
+
type: "text",
|
|
180
|
+
text: result,
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
return {
|
|
187
|
+
content: [
|
|
188
|
+
{
|
|
189
|
+
type: "text",
|
|
190
|
+
text: `❌ 错误: ${error instanceof Error ? error.message : "未知错误"}`,
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
isError: true,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
// 启动服务器
|
|
198
|
+
async function main() {
|
|
199
|
+
const transport = new StdioServerTransport();
|
|
200
|
+
await server.connect(transport);
|
|
201
|
+
console.error("Ring Skills MCP服务已启动");
|
|
202
|
+
}
|
|
203
|
+
main().catch((error) => {
|
|
204
|
+
console.error("服务启动失败:", error);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ring-skills-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP service for fetching and installing company Skills",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ring-skills-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsx src/index.ts",
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"prepare": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"skills",
|
|
22
|
+
"anthropic"
|
|
23
|
+
],
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
27
|
+
"zod": "^3.23.8"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^20.11.0",
|
|
31
|
+
"tsx": "^4.7.0",
|
|
32
|
+
"typescript": "^5.3.3"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|