ai-supply-mcp 1.0.0 → 1.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/README.md +5 -5
- package/package.json +26 -6
- package/server.mjs +28 -26
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# ai-supply-mcp
|
|
2
2
|
|
|
3
|
-
MCP server for **[ai-supply.store](https://ai-supply.store)** — the security-
|
|
3
|
+
MCP server for **[ai-supply.store](https://ai-supply.store)** — the free, security-vetted registry of AI capabilities. Every capability is downloaded, scanned, and graded **0–100 (A–D)** for security before it's listed; risky ones are hidden by default. Connect it to any MCP client (Claude Code, Claude Desktop, VS Code / GitHub Copilot, Cursor) and your agent can **search, install, download, publish, version, and review** capabilities natively — full parity with the website, and every search result carries its security score.
|
|
4
4
|
|
|
5
5
|
## 1. Get an API key
|
|
6
6
|
|
|
7
|
-
Create one at **https://ai-supply.store/dashboard/api-keys** and pick the scopes the agent needs (`read`, `install`, `
|
|
7
|
+
Create one at **https://ai-supply.store/dashboard/api-keys** and pick the scopes the agent needs (`read`, `install`, `publish`, `review`, `manage`, `account`). The `read` scope alone is enough to browse the vetted catalog. The server mints a short-lived, revocable agent **session** from your key on startup, so the long-lived key isn't sent on every request.
|
|
8
8
|
|
|
9
9
|
## 2. Add it to your client
|
|
10
10
|
|
|
@@ -32,11 +32,11 @@ claude mcp add ai-supply --env AIM_API_KEY=aim_your_key_here -- npx -y ai-supply
|
|
|
32
32
|
|
|
33
33
|
> VS Code / Copilot also discover this server from the MCP Registry — search `@mcp ai-supply` in the Extensions view.
|
|
34
34
|
|
|
35
|
-
## Tools (
|
|
35
|
+
## Tools (17)
|
|
36
36
|
|
|
37
|
-
`whoami`, `list_categories`, `list_kinds`, `search_listings`, `get_listing`, `install_listing`, `
|
|
37
|
+
`whoami`, `list_categories`, `list_kinds`, `search_listings`, `get_listing`, `install_listing`, `download_listing`, `review_listing`, `upload_artifact`, `publish_listing`, `update_listing`, `delete_listing`, `add_version`, `my_listings`, `list_community`, `post_community`, `accept_agreements`.
|
|
38
38
|
|
|
39
|
-
Each tool calls the corresponding `/api/v1` endpoint and is gated by the credential's scopes
|
|
39
|
+
Each tool calls the corresponding `/api/v1` endpoint and is gated by the credential's scopes. Every capability on ai-supply is security-scanned and graded (0–100 score, A–D grade) before it's listed — `search_listings` supports `sort: "security"` to rank the most-secure first, `get_listing` returns the full scan findings, and quarantined (critical-finding) listings are hidden by default. The whole catalog is **free**. See <https://ai-supply.store/security> for the vetting methodology.
|
|
40
40
|
|
|
41
41
|
## Local development
|
|
42
42
|
|
package/package.json
CHANGED
|
@@ -1,16 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-supply-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"bin": {
|
|
5
|
+
"bin": {
|
|
6
|
+
"ai-supply-mcp": "./server.mjs"
|
|
7
|
+
},
|
|
6
8
|
"description": "MCP server for ai-supply.store — search, install, download, publish and review security-scanned AI capabilities from any MCP client (Claude, VS Code/Copilot, Cursor).",
|
|
7
9
|
"mcpName": "io.github.ai-supply-store/ai-supply",
|
|
8
10
|
"homepage": "https://ai-supply.store",
|
|
9
|
-
"repository": {
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/ai-supply-store/ai-supply-plugin.git",
|
|
14
|
+
"directory": "mcp"
|
|
15
|
+
},
|
|
10
16
|
"license": "MIT",
|
|
11
|
-
"keywords": [
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"modelcontextprotocol",
|
|
20
|
+
"ai-supply",
|
|
21
|
+
"marketplace",
|
|
22
|
+
"ai-agents",
|
|
23
|
+
"claude",
|
|
24
|
+
"copilot"
|
|
25
|
+
],
|
|
26
|
+
"files": [
|
|
27
|
+
"server.mjs",
|
|
28
|
+
"lib.mjs",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18"
|
|
33
|
+
},
|
|
14
34
|
"dependencies": {
|
|
15
35
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
16
36
|
"zod": "^3.24.1"
|
package/server.mjs
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* ai-supply MCP server.
|
|
4
4
|
*
|
|
5
|
-
* Exposes the
|
|
6
|
-
* (Claude, etc.) can discover, install,
|
|
7
|
-
*
|
|
5
|
+
* Exposes the ai-supply Agent API as MCP tools so any MCP-speaking agent
|
|
6
|
+
* (Claude, etc.) can discover, install, download, publish, update and review
|
|
7
|
+
* capabilities from a free, security-vetted registry — everything a human can do.
|
|
8
|
+
* Every listing is security-scanned and graded (0–100 score, A–D grade); risky
|
|
9
|
+
* (quarantined) ones are hidden by default and each result carries its score.
|
|
8
10
|
*
|
|
9
11
|
* Auth: set AIM_API_KEY (create one at /dashboard/api-keys). The server mints a
|
|
10
12
|
* short-lived agent SESSION from your key on startup and uses it for calls, so
|
|
@@ -18,10 +20,10 @@ import { apiUrl, authHeaders, qs } from "./lib.mjs";
|
|
|
18
20
|
|
|
19
21
|
const BASE = process.env.AIM_BASE_URL || "https://ai-supply.store";
|
|
20
22
|
const API_KEY = process.env.AIM_API_KEY;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
// Don't exit when the key is missing — start anyway so the client lists the tools,
|
|
24
|
+
// and return a clear setup hint from each call (silent exit = confusing "0 tools").
|
|
25
|
+
const NO_KEY_MSG =
|
|
26
|
+
"No API key configured. Set the AIM_API_KEY environment variable for the ai-supply MCP server and reload — create one (the 'read' scope is enough to browse) at https://ai-supply.store/dashboard/api-keys";
|
|
25
27
|
|
|
26
28
|
let TOKEN = API_KEY; // falls back to the raw key if session minting fails
|
|
27
29
|
|
|
@@ -42,6 +44,7 @@ async function mintSession() {
|
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
async function call(method, path, body) {
|
|
47
|
+
if (!API_KEY) return { ok: false, status: 401, data: { error: NO_KEY_MSG } };
|
|
45
48
|
const res = await fetch(apiUrl(BASE, path), {
|
|
46
49
|
method,
|
|
47
50
|
headers: authHeaders(TOKEN, body !== undefined),
|
|
@@ -54,6 +57,13 @@ async function call(method, path, body) {
|
|
|
54
57
|
} catch {
|
|
55
58
|
data = { raw: text };
|
|
56
59
|
}
|
|
60
|
+
if (res.status === 401) {
|
|
61
|
+
data = {
|
|
62
|
+
error:
|
|
63
|
+
"ai-supply rejected the API key (401). Check AIM_API_KEY or create a fresh one at https://ai-supply.store/dashboard/api-keys",
|
|
64
|
+
detail: data,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
57
67
|
return { ok: res.ok, status: res.status, data };
|
|
58
68
|
}
|
|
59
69
|
|
|
@@ -64,7 +74,7 @@ function result(r) {
|
|
|
64
74
|
};
|
|
65
75
|
}
|
|
66
76
|
|
|
67
|
-
const server = new McpServer({ name: "ai-supply", version: "1.
|
|
77
|
+
const server = new McpServer({ name: "ai-supply", version: "1.1.0" });
|
|
68
78
|
|
|
69
79
|
const reg = (name, description, shape, handler) =>
|
|
70
80
|
server.registerTool(name, { description, inputSchema: shape }, handler);
|
|
@@ -76,34 +86,30 @@ reg("whoami", "Identity, scopes and agreements of the authenticated agent.", {},
|
|
|
76
86
|
reg("list_categories", "List categories and subcategories.", {}, async () =>
|
|
77
87
|
result(await call("GET", "/api/v1/categories")),
|
|
78
88
|
);
|
|
79
|
-
reg("list_kinds", "List capability types (kinds)
|
|
89
|
+
reg("list_kinds", "List capability types (kinds).", {}, async () =>
|
|
80
90
|
result(await call("GET", "/api/v1/kinds")),
|
|
81
91
|
);
|
|
82
92
|
reg(
|
|
83
93
|
"search_listings",
|
|
84
|
-
"Search the catalog. Filters: q, category, subcategory, kind,
|
|
94
|
+
"Search the security-vetted catalog. Filters: q, category, subcategory, kind, sort(popular|rating|new|security), limit. Every result carries a security score (0–100) + grade (A–D); risky (quarantined) listings are hidden by default. All listings are free.",
|
|
85
95
|
{
|
|
86
96
|
q: z.string().optional(),
|
|
87
97
|
category: z.string().optional(),
|
|
88
98
|
subcategory: z.string().optional(),
|
|
89
99
|
kind: z.string().optional(),
|
|
90
|
-
|
|
91
|
-
sort: z.enum(["popular", "rating", "new"]).optional(),
|
|
100
|
+
sort: z.enum(["popular", "rating", "new", "security"]).optional(),
|
|
92
101
|
limit: z.number().int().min(1).max(100).optional(),
|
|
93
102
|
},
|
|
94
103
|
async (a) => result(await call("GET", "/api/v1/listings" + qs(a))),
|
|
95
104
|
);
|
|
96
|
-
reg("get_listing", "Get a listing's detail by slug.", { slug: z.string() }, async ({ slug }) =>
|
|
105
|
+
reg("get_listing", "Get a listing's full detail by slug — including its security score, grade, level and scan findings.", { slug: z.string() }, async ({ slug }) =>
|
|
97
106
|
result(await call("GET", `/api/v1/listings/${slug}`)),
|
|
98
107
|
);
|
|
99
108
|
|
|
100
109
|
// ---- Consume ----
|
|
101
|
-
reg("install_listing", "Install a
|
|
110
|
+
reg("install_listing", "Install a listing (free; records ownership). Downloads are gated on a passing security scan.", { slug: z.string() }, async ({ slug }) =>
|
|
102
111
|
result(await call("POST", `/api/v1/listings/${slug}/install`)),
|
|
103
112
|
);
|
|
104
|
-
reg("purchase_listing", "Purchase a paid listing (needs 'purchase' scope; respects spend cap).", { slug: z.string() }, async ({ slug }) =>
|
|
105
|
-
result(await call("POST", `/api/v1/listings/${slug}/purchase`)),
|
|
106
|
-
);
|
|
107
113
|
reg("download_listing", "Fetch the latest version artifact ref for an owned listing.", { slug: z.string() }, async ({ slug }) =>
|
|
108
114
|
result(await call("GET", `/api/v1/listings/${slug}/download`)),
|
|
109
115
|
);
|
|
@@ -131,8 +137,6 @@ reg(
|
|
|
131
137
|
subcategorySlug: z.string().optional(),
|
|
132
138
|
shortDesc: z.string(),
|
|
133
139
|
longDescriptionMd: z.string().optional(),
|
|
134
|
-
pricingModel: z.enum(["FREE", "ONE_TIME", "PER_CALL", "SUBSCRIPTION"]).optional(),
|
|
135
|
-
priceAmount: z.number().int().optional(),
|
|
136
140
|
version: z.string().optional(),
|
|
137
141
|
versionContent: z.string().optional(),
|
|
138
142
|
filePath: z.string().optional(),
|
|
@@ -149,8 +153,6 @@ reg(
|
|
|
149
153
|
name: z.string().optional(),
|
|
150
154
|
shortDesc: z.string().optional(),
|
|
151
155
|
longDescriptionMd: z.string().optional(),
|
|
152
|
-
pricingModel: z.enum(["FREE", "ONE_TIME", "PER_CALL", "SUBSCRIPTION"]).optional(),
|
|
153
|
-
priceAmount: z.number().int().optional(),
|
|
154
156
|
status: z.enum(["DRAFT", "PUBLISHED", "SUSPENDED"]).optional(),
|
|
155
157
|
},
|
|
156
158
|
async ({ slug, ...patch }) => result(await call("PATCH", `/api/v1/listings/${slug}`, patch)),
|
|
@@ -186,10 +188,10 @@ reg(
|
|
|
186
188
|
{ kind: z.enum(["terms", "publisher", "both"]).optional() },
|
|
187
189
|
async (a) => result(await call("POST", "/api/v1/me/accept", a)),
|
|
188
190
|
);
|
|
189
|
-
reg("my_revenue", "Aggregate (mock) revenue for your listings.", {}, async () =>
|
|
190
|
-
result(await call("GET", "/api/v1/me/revenue")),
|
|
191
|
-
);
|
|
192
191
|
|
|
193
|
-
await mintSession();
|
|
192
|
+
if (API_KEY) await mintSession();
|
|
194
193
|
await server.connect(new StdioServerTransport());
|
|
195
|
-
console.error(
|
|
194
|
+
console.error(
|
|
195
|
+
`ai-supply MCP server connected (base: ${BASE})` +
|
|
196
|
+
(API_KEY ? "" : " — WARNING: AIM_API_KEY not set; tools will return a setup hint until you configure it."),
|
|
197
|
+
);
|