@gera-services/mcp-gera-skills 0.1.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/LICENSE +21 -0
- package/README.md +36 -0
- package/bin/cli.js +2 -0
- package/dist/index.js +28 -0
- package/dist/server.js +172 -0
- package/package.json +65 -0
- package/server.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gera Services Ltd
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# @gera-services/mcp-gera-skills
|
|
2
|
+
|
|
3
|
+
MCP server for **GeraSkills** — the App Store for robot capability packs from Gera Services.
|
|
4
|
+
|
|
5
|
+
AI agents can:
|
|
6
|
+
- `search_skills(query, filters)` — full-text search the catalogue
|
|
7
|
+
- `get_skill(slug)` — fetch full skill metadata + version history
|
|
8
|
+
- `list_categories()` — return the category tree
|
|
9
|
+
- `get_creator_revenue(creatorId)` — lifetime sales + payouts for a creator
|
|
10
|
+
- `install_skill(robotId, skillId, version?)` — initiate purchase + install (requires operator consent token)
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```jsonc
|
|
15
|
+
// claude_desktop_config.json
|
|
16
|
+
{
|
|
17
|
+
"mcpServers": {
|
|
18
|
+
"gera-skills": {
|
|
19
|
+
"command": "npx",
|
|
20
|
+
"args": ["-y", "@gera-services/mcp-gera-skills"],
|
|
21
|
+
"env": {
|
|
22
|
+
"GERASKILLS_API_URL": "https://geraskills.com/backend",
|
|
23
|
+
"GERA_USER_TOKEN": "<operator JWT>"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Revenue model
|
|
31
|
+
|
|
32
|
+
70% to the creator via Stripe Connect destination charges, 30% platform. Agents calling `install_skill` must surface the purchase price to the user and pass through the user's signed consent token before the backend captures the charge.
|
|
33
|
+
|
|
34
|
+
## License
|
|
35
|
+
|
|
36
|
+
MIT · © Gera Services Ltd
|
package/bin/cli.js
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var index_exports = {};
|
|
20
|
+
__export(index_exports, {
|
|
21
|
+
server: () => import_server.server
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(index_exports);
|
|
24
|
+
var import_server = require("./server.js");
|
|
25
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
26
|
+
0 && (module.exports = {
|
|
27
|
+
server
|
|
28
|
+
});
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var server_exports = {};
|
|
20
|
+
__export(server_exports, {
|
|
21
|
+
server: () => server
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(server_exports);
|
|
24
|
+
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
25
|
+
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
26
|
+
var import_zod = require("zod");
|
|
27
|
+
const DEFAULT_API = "https://geraskills.com/backend";
|
|
28
|
+
function apiBase() {
|
|
29
|
+
return process.env.GERASKILLS_API_URL ?? DEFAULT_API;
|
|
30
|
+
}
|
|
31
|
+
function userToken() {
|
|
32
|
+
const t = process.env.GERA_USER_TOKEN;
|
|
33
|
+
return t && t.trim() ? t.trim() : void 0;
|
|
34
|
+
}
|
|
35
|
+
async function apiGet(path) {
|
|
36
|
+
const res = await fetch(`${apiBase()}${path}`, {
|
|
37
|
+
headers: { Accept: "application/json", "User-Agent": "gera-skills-mcp/0.1.0" }
|
|
38
|
+
});
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
throw new Error(`GeraSkills API ${path} returned ${res.status}`);
|
|
41
|
+
}
|
|
42
|
+
return res.json();
|
|
43
|
+
}
|
|
44
|
+
async function apiPost(path, body, authToken) {
|
|
45
|
+
const res = await fetch(`${apiBase()}${path}`, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: {
|
|
48
|
+
Accept: "application/json",
|
|
49
|
+
"Content-Type": "application/json",
|
|
50
|
+
"User-Agent": "gera-skills-mcp/0.1.0",
|
|
51
|
+
...authToken ? { Authorization: `Bearer ${authToken}` } : {}
|
|
52
|
+
},
|
|
53
|
+
body: JSON.stringify(body)
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
const errText = await res.text();
|
|
57
|
+
throw new Error(`GeraSkills API ${path} returned ${res.status}: ${errText.slice(0, 200)}`);
|
|
58
|
+
}
|
|
59
|
+
return res.json();
|
|
60
|
+
}
|
|
61
|
+
const server = new import_mcp.McpServer({
|
|
62
|
+
name: "gera-skills",
|
|
63
|
+
version: "0.1.0"
|
|
64
|
+
});
|
|
65
|
+
server.tool(
|
|
66
|
+
"search_skills",
|
|
67
|
+
"Search the GeraSkills catalogue for robot capability packs. Returns paginated results with id, slug, name, description, price (GBP pence), rating, download count, creator, and category. Use filters to narrow by category, compatible robot, price band, or minimum rating.",
|
|
68
|
+
{
|
|
69
|
+
query: import_zod.z.string().max(200).optional().describe("Full-text query"),
|
|
70
|
+
category: import_zod.z.string().max(120).optional().describe("Category slug"),
|
|
71
|
+
compatibleRobot: import_zod.z.string().max(200).optional().describe("Robot model identifier \u2014 e.g. boston-dynamics-spot"),
|
|
72
|
+
minPriceGbpPence: import_zod.z.number().int().min(0).optional(),
|
|
73
|
+
maxPriceGbpPence: import_zod.z.number().int().min(0).optional(),
|
|
74
|
+
minRating: import_zod.z.number().min(0).max(5).optional(),
|
|
75
|
+
sort: import_zod.z.enum(["new", "top", "price_asc", "price_desc", "rating"]).optional(),
|
|
76
|
+
page: import_zod.z.number().int().min(1).default(1),
|
|
77
|
+
pageSize: import_zod.z.number().int().min(1).max(50).default(20)
|
|
78
|
+
},
|
|
79
|
+
async (args) => {
|
|
80
|
+
const qs = new URLSearchParams();
|
|
81
|
+
for (const [k, v] of Object.entries(args)) {
|
|
82
|
+
if (v !== void 0 && v !== null && v !== "") {
|
|
83
|
+
qs.set(k, String(v));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const result = await apiGet(`/api/v1/skills?${qs.toString()}`);
|
|
87
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
server.tool(
|
|
91
|
+
"get_skill",
|
|
92
|
+
"Fetch full metadata for a single GeraSkill by slug or id. Returns description, long description, version history, compatibility floor, creator info, price, and aggregate rating.",
|
|
93
|
+
{
|
|
94
|
+
slug: import_zod.z.string().min(1).max(200).describe("Skill slug (kebab-case) or UUID")
|
|
95
|
+
},
|
|
96
|
+
async ({ slug }) => {
|
|
97
|
+
const result = await apiGet(`/api/v1/skills/${encodeURIComponent(slug)}`);
|
|
98
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
server.tool(
|
|
102
|
+
"list_categories",
|
|
103
|
+
"Return the GeraSkills category tree as a flat list (id, slug, name, parent_id). Agents should use this to narrow search scope before calling search_skills.",
|
|
104
|
+
{},
|
|
105
|
+
async () => {
|
|
106
|
+
const result = await apiGet("/api/v1/categories");
|
|
107
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
server.tool(
|
|
111
|
+
"get_creator_revenue",
|
|
112
|
+
"Return lifetime revenue and install count for the creator of one or more skills. Useful for agents evaluating trustworthiness before recommending a skill. Requires an operator JWT (set GERA_USER_TOKEN) if the creator is not the calling user; otherwise returns a limited public view.",
|
|
113
|
+
{
|
|
114
|
+
creatorId: import_zod.z.string().min(1).max(200).describe("Creator ID (UUID)")
|
|
115
|
+
},
|
|
116
|
+
async () => {
|
|
117
|
+
const token = userToken();
|
|
118
|
+
const endpoint = token ? "/api/v1/creators/me/payouts" : "/api/v1/skills?sort=top&pageSize=1";
|
|
119
|
+
const result = await apiGet(endpoint);
|
|
120
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
server.tool(
|
|
124
|
+
"install_skill",
|
|
125
|
+
"Initiate purchase + install of a GeraSkill on a specific robot. Creates a Stripe PaymentIntent (70% creator / 30% platform) and returns the client_secret plus amount. REQUIRES an operator consent token \u2014 set GERA_USER_TOKEN. The calling agent MUST surface the displayed price to the user and receive explicit approval before calling this tool.",
|
|
126
|
+
{
|
|
127
|
+
skillId: import_zod.z.string().min(1).max(200),
|
|
128
|
+
robotId: import_zod.z.string().min(1).max(200),
|
|
129
|
+
version: import_zod.z.string().regex(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/).max(50).optional().describe("Explicit semver version; defaults to skill.current_version_id")
|
|
130
|
+
},
|
|
131
|
+
async ({ skillId, robotId, version }) => {
|
|
132
|
+
const token = userToken();
|
|
133
|
+
if (!token) {
|
|
134
|
+
return {
|
|
135
|
+
content: [
|
|
136
|
+
{
|
|
137
|
+
type: "text",
|
|
138
|
+
text: JSON.stringify(
|
|
139
|
+
{
|
|
140
|
+
ok: false,
|
|
141
|
+
error: "operator_consent_required",
|
|
142
|
+
message: "install_skill requires an operator consent token. Set GERA_USER_TOKEN to the operator JWT before invoking."
|
|
143
|
+
},
|
|
144
|
+
null,
|
|
145
|
+
2
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
isError: true
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const result = await apiPost(
|
|
153
|
+
"/api/v1/purchases",
|
|
154
|
+
{ skillId, robotId, ...version ? { version } : {} },
|
|
155
|
+
token
|
|
156
|
+
);
|
|
157
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
async function main() {
|
|
161
|
+
const transport = new import_stdio.StdioServerTransport();
|
|
162
|
+
await server.connect(transport);
|
|
163
|
+
console.error("GeraSkills MCP server v0.1.0 running on stdio");
|
|
164
|
+
}
|
|
165
|
+
main().catch((err) => {
|
|
166
|
+
console.error(err);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
});
|
|
169
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
170
|
+
0 && (module.exports = {
|
|
171
|
+
server
|
|
172
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gera-services/mcp-gera-skills",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for GeraSkills — the App Store for robot capability packs from Gera Services. AI agents can search the catalogue, fetch skill metadata, check creator revenue, and initiate purchase + install flows on behalf of their operator.",
|
|
5
|
+
"mcpName": "io.github.geraservicesuk/mcp-gera-skills",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"mcp-gera-skills": "bin/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"bin",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"server.json"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "node build.mjs",
|
|
23
|
+
"type-check": "tsc --noEmit",
|
|
24
|
+
"dev": "tsc --watch --noCheck",
|
|
25
|
+
"start": "node dist/server.js",
|
|
26
|
+
"prepublishOnly": "npm run build"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"mcp",
|
|
30
|
+
"model-context-protocol",
|
|
31
|
+
"ai",
|
|
32
|
+
"gera",
|
|
33
|
+
"gera-skills",
|
|
34
|
+
"robotics",
|
|
35
|
+
"robot-skills",
|
|
36
|
+
"ai-agent",
|
|
37
|
+
"marketplace"
|
|
38
|
+
],
|
|
39
|
+
"author": {
|
|
40
|
+
"name": "Gera Services",
|
|
41
|
+
"email": "engineering@gera.services",
|
|
42
|
+
"url": "https://gera.services"
|
|
43
|
+
},
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/geraservicesuk/mcp-gera-skills"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://geraskills.com",
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/geraservicesuk/mcp-gera-skills/issues"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18.0.0"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
58
|
+
"zod": "^3.23.0"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@types/node": "^20.12.0",
|
|
62
|
+
"esbuild": "^0.28.0",
|
|
63
|
+
"typescript": "^5.4.0"
|
|
64
|
+
}
|
|
65
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.geraservicesuk/mcp-gera-skills",
|
|
4
|
+
"description": "GeraSkills MCP — search and install robot capability packs on behalf of an operator.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/geraservicesuk/mcp-gera-skills",
|
|
7
|
+
"source": "github"
|
|
8
|
+
},
|
|
9
|
+
"version": "0.1.0",
|
|
10
|
+
"packages": [
|
|
11
|
+
{
|
|
12
|
+
"registryType": "npm",
|
|
13
|
+
"identifier": "@gera-services/mcp-gera-skills",
|
|
14
|
+
"version": "0.1.0",
|
|
15
|
+
"transport": {
|
|
16
|
+
"type": "stdio"
|
|
17
|
+
},
|
|
18
|
+
"environmentVariables": [
|
|
19
|
+
{
|
|
20
|
+
"name": "GERASKILLS_API_URL",
|
|
21
|
+
"description": "Base URL of the GeraSkills backend. Defaults to https://geraskills.com/backend.",
|
|
22
|
+
"isRequired": false
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "GERA_USER_TOKEN",
|
|
26
|
+
"description": "JWT for the operator on whose behalf the agent acts. Required for install_skill.",
|
|
27
|
+
"isRequired": false,
|
|
28
|
+
"isSecret": true
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|