Skip to content

Commit 90a54df

Browse files
committed
feat: register MCP Registry tools
Wires seven new MCP tools backed by the SwarmDock registry: - mcp_registry_search — semantic + faceted search - mcp_registry_get — server detail with tools + installs - mcp_registry_recommend — task-description → top servers - mcp_registry_record_usage — signed Ed25519 usage attestation - mcp_registry_submit — register a new server (submitter-only future edits) - mcp_registry_rate — 1-5 rating, gated on prior verified usage - (archive via SDK, not tool) Requires @swarmdock/sdk >= 0.6.0 (adds client.mcp + signAttestation).
1 parent 5d7402d commit 90a54df

2 files changed

Lines changed: 133 additions & 0 deletions

File tree

src/server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { registerRatingTools } from "./tools/ratings.js";
1010
import { registerSocialTools } from "./tools/social.js";
1111
import { registerQualityTools } from "./tools/quality.js";
1212
import { registerPaymentTools } from "./tools/payments.js";
13+
import { registerMcpRegistryTools } from "./tools/mcp-registry.js";
1314

1415
export interface ServerOptions {
1516
config?: Partial<Config>;
@@ -42,6 +43,7 @@ export function createServer(options: ServerOptions = {}): {
4243
registerSocialTools(server, client);
4344
registerQualityTools(server, client);
4445
registerPaymentTools(server, client);
46+
registerMcpRegistryTools(server, client);
4547

4648
return { server, client, config };
4749
}

src/tools/mcp-registry.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/**
2+
* MCP tools that expose SwarmDock's MCP Registry to agents. Search runs over
3+
* a semantic index (pgvector 768-dim) seeded from Smithery, the official
4+
* modelcontextprotocol/servers repo, and direct submissions.
5+
*/
6+
import { z } from "zod";
7+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8+
import type { SwarmDockClient } from "@swarmdock/sdk";
9+
10+
import { run } from "./_helpers.js";
11+
12+
export function registerMcpRegistryTools(server: McpServer, client: SwarmDockClient): void {
13+
server.registerTool(
14+
"mcp_registry_search",
15+
{
16+
title: "Search MCP server registry",
17+
description:
18+
"Semantic + faceted search over the public SwarmDock MCP registry. Returns servers ranked by a blended embedding-similarity and quality-signal score. Use this when you need to find an MCP server for a task.",
19+
inputSchema: {
20+
q: z.string().max(500).optional().describe("Free-text query — e.g. 'parse PDFs', 'postgres introspection'."),
21+
transport: z.enum(["stdio", "sse", "streamable_http", "websocket"]).optional(),
22+
authMode: z.enum(["none", "api_key", "oauth", "bearer"]).optional(),
23+
language: z.string().max(40).optional(),
24+
category: z.string().max(40).optional(),
25+
paidTier: z.boolean().optional(),
26+
minQuality: z.number().min(0).max(1).optional().describe("Minimum quality score (0-1)."),
27+
limit: z.number().int().min(1).max(100).default(20),
28+
offset: z.number().int().min(0).default(0),
29+
},
30+
},
31+
async (args) => run(() => client.mcp.search(args)),
32+
);
33+
34+
server.registerTool(
35+
"mcp_registry_get",
36+
{
37+
title: "Get MCP server detail",
38+
description:
39+
"Fetch full detail for a single MCP server: metadata, tools, installation methods, quality score, and rating aggregate.",
40+
inputSchema: { slug: z.string().min(1).describe("Server slug (lower-kebab-case).") },
41+
},
42+
async ({ slug }) => run(() => client.mcp.get(slug)),
43+
);
44+
45+
server.registerTool(
46+
"mcp_registry_recommend",
47+
{
48+
title: "Recommend MCP server for task",
49+
description:
50+
"Given a free-text description of what the agent needs to do, return the top-matching servers ranked by semantic similarity blended with quality score. Honors price and transport filters.",
51+
inputSchema: {
52+
description: z.string().min(5).describe("What you need the MCP server to help with."),
53+
transport: z.enum(["stdio", "sse", "streamable_http", "websocket"]).optional(),
54+
maxPriceMicroUsdc: z.string().optional().describe("Maximum per-call price in micro-USDC (omit to include free-tier only with no cap)."),
55+
limit: z.number().int().min(1).max(50).default(10),
56+
},
57+
},
58+
async (args) => run(() => client.mcp.recommend(args)),
59+
);
60+
61+
server.registerTool(
62+
"mcp_registry_record_usage",
63+
{
64+
title: "Record signed MCP usage attestation",
65+
description:
66+
"Sign and submit a usage attestation for an MCP server you just invoked. The attestation is cryptographically attributable to your agent DID — it feeds the public quality score that other agents see.",
67+
inputSchema: {
68+
slug: z.string().min(1),
69+
outcome: z.enum(["success", "error", "timeout", "cancelled"]),
70+
latencyMs: z.number().int().min(0).max(600_000).optional(),
71+
errorCode: z.string().max(120).optional(),
72+
toolName: z.string().max(128).optional(),
73+
taskId: z.string().uuid().optional().describe("Optional task this usage is tied to — cross-links into SwarmDock task history."),
74+
},
75+
},
76+
async ({ slug, outcome, latencyMs, errorCode, toolName, taskId }) =>
77+
run(() => client.mcp.recordUsage(slug, outcome, { latencyMs, errorCode, toolName, taskId })),
78+
);
79+
80+
server.registerTool(
81+
"mcp_registry_submit",
82+
{
83+
title: "Submit a new MCP server to the registry",
84+
description:
85+
"Register an MCP server you built so other agents can discover it. Submitter is the only account that can later update or archive the listing.",
86+
inputSchema: {
87+
slug: z.string().min(2).max(80).describe("lower-kebab-case slug — unique across the registry."),
88+
name: z.string().min(1).max(200),
89+
description: z.string().min(10).max(4000),
90+
homepage: z.string().url().optional(),
91+
repoUrl: z.string().url().optional(),
92+
license: z.string().max(40).optional(),
93+
transport: z.enum(["stdio", "sse", "streamable_http", "websocket"]),
94+
authMode: z.enum(["none", "api_key", "oauth", "bearer"]).default("none"),
95+
language: z.string().max(40).optional(),
96+
categories: z.array(z.string().max(40)).default([]),
97+
tags: z.array(z.string().max(40)).default([]),
98+
installations: z.array(z.object({
99+
method: z.enum(["npm", "npx", "pipx", "uvx", "docker", "binary", "remote"]),
100+
spec: z.record(z.unknown()),
101+
})).min(1),
102+
tools: z.array(z.object({
103+
name: z.string().min(1).max(128),
104+
description: z.string().max(2000).optional(),
105+
inputSchema: z.unknown().optional(),
106+
})).default([]),
107+
paidTier: z.boolean().default(false),
108+
priceMicroUsdc: z.string().optional().describe("Required when paidTier=true."),
109+
payoutAddress: z.string().optional().describe("0x-prefixed Base wallet — required when paidTier=true."),
110+
},
111+
},
112+
async (args) => run(() => client.mcp.submit(args)),
113+
);
114+
115+
server.registerTool(
116+
"mcp_registry_rate",
117+
{
118+
title: "Rate an MCP server",
119+
description:
120+
"Submit a 1-5 rating for an MCP server. Requires at least one previously recorded usage attestation so ratings are tied to real experience.",
121+
inputSchema: {
122+
slug: z.string().min(1),
123+
score: z.number().int().min(1).max(5),
124+
comment: z.string().max(2000).optional(),
125+
usageEventId: z.string().uuid().optional(),
126+
},
127+
},
128+
async ({ slug, score, comment, usageEventId }) =>
129+
run(() => client.mcp.rate(slug, { score, comment, usageEventId })),
130+
);
131+
}

0 commit comments

Comments
 (0)