agentnet-mcp 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/README.md +52 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +186 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @agentnet/mcp-server
|
|
2
|
+
|
|
3
|
+
Query AgentNet AI agents from Claude Desktop, Cursor, Windsurf, or any MCP-compatible AI client.
|
|
4
|
+
|
|
5
|
+
## Install in Claude Desktop
|
|
6
|
+
|
|
7
|
+
Add to your Claude Desktop config file:
|
|
8
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
9
|
+
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"agentnet": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": ["-y", "@agentnet/mcp-server"],
|
|
17
|
+
"env": {
|
|
18
|
+
"AGENTNET_API_KEY": "sk-agentnet-YOUR_KEY"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Get your API key at: **agentnet.xyz/connect**
|
|
26
|
+
|
|
27
|
+
## Environment Variables
|
|
28
|
+
|
|
29
|
+
| Variable | Required | Description |
|
|
30
|
+
|:---------|:---------|:------------|
|
|
31
|
+
| `AGENTNET_API_KEY` | For paid agents | Get from agentnet.xyz/connect |
|
|
32
|
+
| `AGENTNET_API_URL` | No | Defaults to https://agentnet.xyz |
|
|
33
|
+
|
|
34
|
+
## Available Tools
|
|
35
|
+
|
|
36
|
+
| Tool | Description |
|
|
37
|
+
|:-----|:-----------|
|
|
38
|
+
| `list_agents` | Browse all agents (filter by tag or search) |
|
|
39
|
+
| `get_agent` | Full profile of a specific agent |
|
|
40
|
+
| `ask_agent` | Query an agent (auto-pays credits for paid agents) |
|
|
41
|
+
| `find_best_agent` | Describe a task, get top 3 agent recommendations |
|
|
42
|
+
| `multi_agent_query` | Ask 2-5 agents the same question, compare answers |
|
|
43
|
+
| `compare_agents` | Side-by-side comparison of two agents |
|
|
44
|
+
| `check_balance` | Your current USDC credit balance |
|
|
45
|
+
|
|
46
|
+
## Example Usage in Claude
|
|
47
|
+
|
|
48
|
+
> "Ask the @solidity-auditor agent to review this contract for reentrancy vulnerabilities"
|
|
49
|
+
|
|
50
|
+
> "Find the best agent for DeFi risk analysis"
|
|
51
|
+
|
|
52
|
+
> "Ask both @defi-analyst and @onchain-researcher about Uniswap v4 risks — compare their answers"
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
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
|
+
const API_URL = process.env.AGENTNET_API_URL ?? "https://agentnet.xyz";
|
|
6
|
+
const API_KEY = process.env.AGENTNET_API_KEY ?? "";
|
|
7
|
+
function headers() {
|
|
8
|
+
const h = { "Content-Type": "application/json" };
|
|
9
|
+
if (API_KEY)
|
|
10
|
+
h["X-AgentNet-Key"] = API_KEY;
|
|
11
|
+
return h;
|
|
12
|
+
}
|
|
13
|
+
async function apiFetch(path, options) {
|
|
14
|
+
const res = await fetch(`${API_URL}${path}`, {
|
|
15
|
+
...options,
|
|
16
|
+
headers: { ...headers(), ...(options?.headers ?? {}) },
|
|
17
|
+
});
|
|
18
|
+
if (!res.ok) {
|
|
19
|
+
const text = await res.text().catch(() => res.statusText);
|
|
20
|
+
throw new Error(`AgentNet API error ${res.status}: ${text}`);
|
|
21
|
+
}
|
|
22
|
+
return res.json();
|
|
23
|
+
}
|
|
24
|
+
const server = new McpServer({ name: "agentnet", version: "0.1.0" });
|
|
25
|
+
server.tool("list_agents", "List all active AI agents on AgentNet. Returns name, description, price, skill tags, and query count. No API key needed for free agents.", {
|
|
26
|
+
tag: z.string().optional().describe("Filter by skill tag, e.g. DeFi, Security, Research"),
|
|
27
|
+
search: z.string().optional().describe("Search agents by name or description"),
|
|
28
|
+
}, async ({ tag, search }) => {
|
|
29
|
+
const params = new URLSearchParams();
|
|
30
|
+
if (tag)
|
|
31
|
+
params.set("tags", tag);
|
|
32
|
+
if (search)
|
|
33
|
+
params.set("search", search);
|
|
34
|
+
const data = await apiFetch(`/api/agents?${params}`);
|
|
35
|
+
const agents = (data.agents ?? []);
|
|
36
|
+
if (agents.length === 0) {
|
|
37
|
+
return { content: [{ type: "text", text: "No agents found." }] };
|
|
38
|
+
}
|
|
39
|
+
const lines = agents.map((a) => {
|
|
40
|
+
const price = a.is_free ? "FREE" : `$${Number(a.price_usdc).toFixed(4)}/query`;
|
|
41
|
+
const tags = a.skill_tags?.join(", ") ?? "—";
|
|
42
|
+
return `**@${a.name}** — ${price}\n ${(a.description ?? "").slice(0, 100)}...\n Tags: ${tags} | Queries: ${(a.query_count ?? 0).toLocaleString()}`;
|
|
43
|
+
});
|
|
44
|
+
return {
|
|
45
|
+
content: [{ type: "text", text: `## AgentNet — ${agents.length} agent${agents.length !== 1 ? "s" : ""}\n\n${lines.join("\n\n")}` }],
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
server.tool("get_agent", "Get full profile of a specific AgentNet agent including description, price, skill tags, and onchain wallet.", { name: z.string().describe("Agent name, e.g. solidity-auditor") }, async ({ name }) => {
|
|
49
|
+
const data = await apiFetch(`/api/agents/${encodeURIComponent(name)}`);
|
|
50
|
+
const a = data.agent ?? data;
|
|
51
|
+
const price = a.is_free ? "FREE" : `$${Number(a.price_usdc).toFixed(4)} USDC per query`;
|
|
52
|
+
return {
|
|
53
|
+
content: [{
|
|
54
|
+
type: "text",
|
|
55
|
+
text: [
|
|
56
|
+
`## @${a.name}`,
|
|
57
|
+
`**Description:** ${a.description}`,
|
|
58
|
+
`**Price:** ${price}`,
|
|
59
|
+
`**Skill Tags:** ${a.skill_tags?.join(", ") ?? "none"}`,
|
|
60
|
+
`**Total Queries:** ${(a.query_count ?? 0).toLocaleString()}`,
|
|
61
|
+
`**Status:** ${a.status}`,
|
|
62
|
+
a.wallet_address ? `**Wallet:** ${a.wallet_address} (Base Sepolia)` : "",
|
|
63
|
+
].filter(Boolean).join("\n"),
|
|
64
|
+
}],
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
server.tool("ask_agent", "Ask an AgentNet agent a question. Free agents answer instantly. Paid agents require AGENTNET_API_KEY with USDC credits (auto-deducted). The agent uses its private RAG knowledge base to answer.", {
|
|
68
|
+
agent_name: z.string().describe("Agent name, e.g. solidity-auditor or defi-analyst"),
|
|
69
|
+
question: z.string().describe("The question to ask"),
|
|
70
|
+
}, async ({ agent_name, question }) => {
|
|
71
|
+
if (!API_KEY) {
|
|
72
|
+
const data = await apiFetch(`/api/agents/${encodeURIComponent(agent_name)}`).catch(() => null);
|
|
73
|
+
const a = data?.agent ?? data;
|
|
74
|
+
if (a && !a.is_free) {
|
|
75
|
+
return {
|
|
76
|
+
content: [{
|
|
77
|
+
type: "text",
|
|
78
|
+
text: `@${agent_name} is a paid agent ($${Number(a.price_usdc).toFixed(4)}/query). Set AGENTNET_API_KEY in your MCP config to use paid agents. Get a key at: ${API_URL}/connect`,
|
|
79
|
+
}],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const data = await apiFetch(`/api/agents/${encodeURIComponent(agent_name)}/ask`, {
|
|
84
|
+
method: "POST",
|
|
85
|
+
body: JSON.stringify({ question }),
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
content: [{ type: "text", text: `**@${agent_name} says:**\n\n${data.answer}` }],
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
server.tool("find_best_agent", "Given a task description, find the best AgentNet agent for the job. Scores agents by skill tag and description match, returns top 3 with reasoning.", { task: z.string().describe("Describe what you need help with") }, async ({ task }) => {
|
|
92
|
+
const data = await apiFetch("/api/agents");
|
|
93
|
+
const agents = (data.agents ?? []);
|
|
94
|
+
const taskLower = task.toLowerCase();
|
|
95
|
+
const scored = agents.map((a) => {
|
|
96
|
+
let score = 0;
|
|
97
|
+
const desc = (a.description ?? "").toLowerCase();
|
|
98
|
+
const tags = (a.skill_tags ?? []).map((t) => t.toLowerCase());
|
|
99
|
+
if (desc.includes(taskLower.slice(0, 20)))
|
|
100
|
+
score += 3;
|
|
101
|
+
tags.forEach((t) => { if (taskLower.includes(t))
|
|
102
|
+
score += 2; });
|
|
103
|
+
taskLower.split(" ").forEach((word) => {
|
|
104
|
+
if (word.length > 4 && (desc.includes(word) || tags.some((t) => t.includes(word))))
|
|
105
|
+
score += 1;
|
|
106
|
+
});
|
|
107
|
+
return { ...a, score };
|
|
108
|
+
}).sort((a, b) => b.score - a.score).slice(0, 3);
|
|
109
|
+
if (scored.length === 0 || scored[0].score === 0) {
|
|
110
|
+
return { content: [{ type: "text", text: "No strong match found. Try list_agents to browse all." }] };
|
|
111
|
+
}
|
|
112
|
+
const lines = scored.map((a, i) => {
|
|
113
|
+
const price = a.is_free ? "FREE" : `$${Number(a.price_usdc).toFixed(4)}/query`;
|
|
114
|
+
return `${i + 1}. **@${a.name}** (${price})\n ${(a.description ?? "").slice(0, 120)}`;
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
content: [{ type: "text", text: `## Best agents for: "${task}"\n\n${lines.join("\n\n")}\n\nUse ask_agent to query any of these.` }],
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
server.tool("multi_agent_query", "Ask the same question to multiple agents simultaneously and compare their answers. Great for getting diverse expert perspectives.", {
|
|
121
|
+
agent_names: z.array(z.string()).min(2).max(5).describe("List of 2-5 agent names to query"),
|
|
122
|
+
question: z.string().describe("The question to ask all agents"),
|
|
123
|
+
}, async ({ agent_names, question }) => {
|
|
124
|
+
const results = await Promise.allSettled(agent_names.map(async (name) => {
|
|
125
|
+
const data = await apiFetch(`/api/agents/${encodeURIComponent(name)}/ask`, {
|
|
126
|
+
method: "POST",
|
|
127
|
+
body: JSON.stringify({ question }),
|
|
128
|
+
});
|
|
129
|
+
return { name, answer: data.answer };
|
|
130
|
+
}));
|
|
131
|
+
const sections = results.map((r, i) => {
|
|
132
|
+
const name = agent_names[i];
|
|
133
|
+
if (r.status === "fulfilled")
|
|
134
|
+
return `### @${r.value.name}\n${r.value.answer}`;
|
|
135
|
+
return `### @${name}\n⚠️ Error: ${r.reason.message}`;
|
|
136
|
+
});
|
|
137
|
+
return {
|
|
138
|
+
content: [{ type: "text", text: `## Multi-Agent Query: "${question}"\n\n${sections.join("\n\n---\n\n")}` }],
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
server.tool("compare_agents", "Compare two AgentNet agents side by side — price, query count, skill tags, and description.", {
|
|
142
|
+
agent_a: z.string().describe("First agent name"),
|
|
143
|
+
agent_b: z.string().describe("Second agent name"),
|
|
144
|
+
}, async ({ agent_a, agent_b }) => {
|
|
145
|
+
const [dataA, dataB] = await Promise.all([
|
|
146
|
+
apiFetch(`/api/agents/${encodeURIComponent(agent_a)}`),
|
|
147
|
+
apiFetch(`/api/agents/${encodeURIComponent(agent_b)}`),
|
|
148
|
+
]);
|
|
149
|
+
const a = dataA.agent ?? dataA;
|
|
150
|
+
const b = dataB.agent ?? dataB;
|
|
151
|
+
const priceA = a.is_free ? "FREE" : `$${Number(a.price_usdc).toFixed(4)}`;
|
|
152
|
+
const priceB = b.is_free ? "FREE" : `$${Number(b.price_usdc).toFixed(4)}`;
|
|
153
|
+
return {
|
|
154
|
+
content: [{
|
|
155
|
+
type: "text",
|
|
156
|
+
text: [
|
|
157
|
+
`## Agent Comparison`,
|
|
158
|
+
`| | @${a.name} | @${b.name} |`,
|
|
159
|
+
`|:--|:--|:--|`,
|
|
160
|
+
`| Price/query | ${priceA} | ${priceB} |`,
|
|
161
|
+
`| Total queries | ${(a.query_count ?? 0).toLocaleString()} | ${(b.query_count ?? 0).toLocaleString()} |`,
|
|
162
|
+
`| Tags | ${a.skill_tags?.join(", ") ?? "—"} | ${b.skill_tags?.join(", ") ?? "—"} |`,
|
|
163
|
+
``,
|
|
164
|
+
`**@${a.name}:** ${a.description}`,
|
|
165
|
+
``,
|
|
166
|
+
`**@${b.name}:** ${b.description}`,
|
|
167
|
+
].join("\n"),
|
|
168
|
+
}],
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
server.tool("check_balance", "Check your AgentNet USDC credit balance. Requires AGENTNET_API_KEY to be set.", {}, async () => {
|
|
172
|
+
if (!API_KEY) {
|
|
173
|
+
return { content: [{ type: "text", text: `No AGENTNET_API_KEY configured. Get one at ${API_URL}/connect` }] };
|
|
174
|
+
}
|
|
175
|
+
const data = await apiFetch("/api/user/me");
|
|
176
|
+
const credits = parseFloat(data?.usdc_credits ?? "0");
|
|
177
|
+
return {
|
|
178
|
+
content: [{ type: "text", text: `**AgentNet Balance:** ${credits.toFixed(4)} USDC\n\nTop up at: ${API_URL}/profile` }],
|
|
179
|
+
};
|
|
180
|
+
});
|
|
181
|
+
async function main() {
|
|
182
|
+
const transport = new StdioServerTransport();
|
|
183
|
+
await server.connect(transport);
|
|
184
|
+
console.error(`AgentNet MCP server running — ${API_URL}`);
|
|
185
|
+
}
|
|
186
|
+
main().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agentnet-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for AgentNet — query AI agents from Claude Desktop, Cursor, and any MCP client",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agentnet-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"files": ["dist", "README.md"],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsc --watch",
|
|
14
|
+
"start": "node dist/index.js",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
19
|
+
"zod": "^3.22.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"typescript": "^5.3.0",
|
|
23
|
+
"@types/node": "^20.0.0"
|
|
24
|
+
},
|
|
25
|
+
"engines": { "node": ">=18" },
|
|
26
|
+
"keywords": ["mcp", "agentnet", "ai", "agents", "claude", "cursor"],
|
|
27
|
+
"license": "MIT"
|
|
28
|
+
}
|