agoragentic-mcp 1.3.0 → 1.3.2
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 +80 -38
- package/mcp-server.js +582 -743
- package/package.json +16 -13
- package/scripts/postinstall.js +11 -27
package/mcp-server.js
CHANGED
|
@@ -1,787 +1,626 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const readline = require('readline');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
const { version: PACKAGE_VERSION } = require('./package.json');
|
|
7
|
+
|
|
8
|
+
const REMOTE_MCP_URL = process.env.AGORAGENTIC_MCP_URL || 'https://agoragentic.com/api/mcp';
|
|
9
|
+
const AGORAGENTIC_BASE = process.env.AGORAGENTIC_BASE_URL || 'https://agoragentic.com';
|
|
10
|
+
const API_KEY = process.env.AGORAGENTIC_API_KEY || '';
|
|
11
|
+
const ACP_MODE = process.argv.includes('--acp');
|
|
12
|
+
|
|
13
|
+
const ACP_TOOLS = [
|
|
14
|
+
{
|
|
15
|
+
name: 'agoragentic_execute',
|
|
16
|
+
description: 'Route a task through Agent OS execute() with provider selection, fallback, receipts, and settlement.',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'agoragentic_match',
|
|
20
|
+
description: 'Preview routed providers before execution.',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'agoragentic_quote',
|
|
24
|
+
description: 'Create a bounded quote before paid execution.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'agoragentic_status',
|
|
28
|
+
description: 'Inspect execution status for an invocation.',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'agoragentic_receipt',
|
|
32
|
+
description: 'Fetch normalized receipt and settlement metadata.',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'agoragentic_browse_services',
|
|
36
|
+
description: 'Browse stable x402 edge resources.',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'agoragentic_call_service',
|
|
40
|
+
description: 'Call a stable x402 edge resource after payment challenge handling.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'agoragentic_edge_receipt',
|
|
44
|
+
description: 'Inspect x402 edge receipt metadata.',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'agoragentic_x402_test',
|
|
48
|
+
description: 'Exercise the free x402 pipeline canary.',
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
function buildJsonContent(data) {
|
|
53
|
+
return {
|
|
54
|
+
content: [
|
|
55
|
+
{
|
|
56
|
+
type: 'text',
|
|
57
|
+
text: typeof data === 'string' ? data : JSON.stringify(data, null, 2),
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function apiCall(method, path, body) {
|
|
64
|
+
const headers = {
|
|
65
|
+
'Content-Type': 'application/json',
|
|
66
|
+
'User-Agent': `agoragentic-mcp/${PACKAGE_VERSION}`,
|
|
67
|
+
};
|
|
68
|
+
if (API_KEY) {
|
|
69
|
+
headers.Authorization = `Bearer ${API_KEY}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const response = await fetch(`${AGORAGENTIC_BASE}${path}`, {
|
|
73
|
+
method,
|
|
74
|
+
headers,
|
|
75
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const text = await response.text();
|
|
79
|
+
let data;
|
|
80
|
+
try {
|
|
81
|
+
data = text ? JSON.parse(text) : {};
|
|
82
|
+
} catch {
|
|
83
|
+
data = { raw: text };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
return {
|
|
88
|
+
ok: false,
|
|
89
|
+
status: response.status,
|
|
90
|
+
error: data.error || data.message || response.statusText,
|
|
91
|
+
details: data,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return data;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function requireApiKey() {
|
|
99
|
+
if (API_KEY) return null;
|
|
100
|
+
return buildJsonContent({
|
|
101
|
+
ok: false,
|
|
102
|
+
error: 'missing_api_key',
|
|
103
|
+
message: 'Set AGORAGENTIC_API_KEY for authenticated Router / Marketplace execution tools. Use agoragentic_register to create a key.',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function buildFallbackToolList() {
|
|
108
|
+
return [
|
|
109
|
+
{
|
|
110
|
+
name: 'agoragentic_register',
|
|
111
|
+
description: 'Register an agent with Agoragentic and receive an API key for routed execution.',
|
|
112
|
+
inputSchema: {
|
|
113
|
+
type: 'object',
|
|
114
|
+
properties: {
|
|
115
|
+
name: { type: 'string', description: 'Agent name' },
|
|
116
|
+
agent_name: { type: 'string', description: 'Agent name, compatibility alias' },
|
|
117
|
+
intent: { type: 'string', description: 'buyer, seller, or both', default: 'buyer' },
|
|
118
|
+
description: { type: 'string', description: 'Short agent description' },
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: 'agoragentic_search',
|
|
124
|
+
description: 'Search public Agoragentic marketplace capabilities.',
|
|
125
|
+
inputSchema: {
|
|
126
|
+
type: 'object',
|
|
127
|
+
properties: {
|
|
128
|
+
query: { type: 'string', description: 'Search query' },
|
|
129
|
+
category: { type: 'string', description: 'Optional category filter' },
|
|
130
|
+
limit: { type: 'number', description: 'Maximum results to return', default: 10 },
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'agoragentic_match',
|
|
136
|
+
description: 'Preview Router / Marketplace provider matches for a task. No spend.',
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: {
|
|
140
|
+
task: { type: 'string', description: 'Task to route' },
|
|
141
|
+
max_cost: { type: 'number', description: 'Maximum USDC price per call' },
|
|
142
|
+
category: { type: 'string', description: 'Optional category filter' },
|
|
143
|
+
prefer_trusted: { type: 'boolean', description: 'Prefer trusted providers', default: true },
|
|
144
|
+
},
|
|
145
|
+
required: ['task'],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: 'agoragentic_execute',
|
|
150
|
+
description: 'Execute a task through the hosted Agoragentic Router / Marketplace. May spend according to listing price and account balance.',
|
|
151
|
+
inputSchema: {
|
|
152
|
+
type: 'object',
|
|
153
|
+
properties: {
|
|
154
|
+
task: { type: 'string', description: 'Task to execute' },
|
|
155
|
+
input: { type: 'object', description: 'Provider input payload', default: {} },
|
|
156
|
+
constraints: { type: 'object', description: 'Routing and budget constraints', default: {} },
|
|
157
|
+
quote_id: { type: 'string', description: 'Optional quote ID' },
|
|
158
|
+
intent_contract_id: { type: 'string', description: 'Optional Agent OS intent contract ID' },
|
|
159
|
+
},
|
|
160
|
+
required: ['task'],
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: 'agoragentic_execute_status',
|
|
165
|
+
description: 'Read status, output, cost, and receipt metadata for a routed execution.',
|
|
166
|
+
inputSchema: {
|
|
167
|
+
type: 'object',
|
|
168
|
+
properties: {
|
|
169
|
+
invocation_id: { type: 'string', description: 'Invocation ID from agoragentic_execute' },
|
|
170
|
+
},
|
|
171
|
+
required: ['invocation_id'],
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function mergeFallbackTools(remoteTools = []) {
|
|
178
|
+
const seen = new Set(remoteTools.map((tool) => tool.name));
|
|
179
|
+
const merged = [...remoteTools];
|
|
180
|
+
for (const tool of buildFallbackToolList()) {
|
|
181
|
+
if (!seen.has(tool.name)) {
|
|
182
|
+
merged.push(tool);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return merged;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function executeFallbackTool(name, args = {}) {
|
|
189
|
+
if (name === 'agoragentic_register') {
|
|
190
|
+
const agentName = args.agent_name || args.name || 'mcp-agent';
|
|
191
|
+
const data = await apiCall('POST', '/api/quickstart', {
|
|
192
|
+
name: agentName,
|
|
193
|
+
intent: args.intent || 'buyer',
|
|
194
|
+
description: args.description || 'Registered through agoragentic-mcp fallback tools.',
|
|
195
|
+
});
|
|
196
|
+
return buildJsonContent(data);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (name === 'agoragentic_search') {
|
|
200
|
+
const params = new URLSearchParams();
|
|
201
|
+
if (args.query) params.set('q', args.query);
|
|
202
|
+
if (args.category) params.set('category', args.category);
|
|
203
|
+
if (args.limit !== undefined) params.set('limit', String(args.limit));
|
|
204
|
+
const data = await apiCall('GET', `/api/capabilities?${params.toString()}`);
|
|
205
|
+
return buildJsonContent(data);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (name === 'agoragentic_match') {
|
|
209
|
+
const missing = requireApiKey();
|
|
210
|
+
if (missing) return missing;
|
|
211
|
+
const params = new URLSearchParams();
|
|
212
|
+
params.set('task', args.task);
|
|
213
|
+
if (args.max_cost !== undefined) params.set('max_cost', String(args.max_cost));
|
|
214
|
+
if (args.category) params.set('category', args.category);
|
|
215
|
+
if (args.prefer_trusted !== undefined) params.set('prefer_trusted', args.prefer_trusted ? 'true' : 'false');
|
|
216
|
+
const data = await apiCall('GET', `/api/execute/match?${params.toString()}`);
|
|
217
|
+
return buildJsonContent(data);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (name === 'agoragentic_execute') {
|
|
221
|
+
const missing = requireApiKey();
|
|
222
|
+
if (missing) return missing;
|
|
223
|
+
const payload = {
|
|
224
|
+
task: args.task,
|
|
225
|
+
input: args.input || {},
|
|
226
|
+
constraints: args.constraints || {},
|
|
227
|
+
};
|
|
228
|
+
if (args.quote_id) payload.quote_id = args.quote_id;
|
|
229
|
+
if (args.intent_contract_id) payload.intent_contract_id = args.intent_contract_id;
|
|
230
|
+
const data = await apiCall('POST', '/api/execute', payload);
|
|
231
|
+
return buildJsonContent(data);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (name === 'agoragentic_execute_status') {
|
|
235
|
+
const missing = requireApiKey();
|
|
236
|
+
if (missing) return missing;
|
|
237
|
+
const invocationId = String(args.invocation_id || '').replace(/[^a-zA-Z0-9\-_]/g, '');
|
|
238
|
+
if (!invocationId) return buildJsonContent({ ok: false, error: 'invalid_invocation_id' });
|
|
239
|
+
const data = await apiCall('GET', `/api/execute/status/${invocationId}`);
|
|
240
|
+
return buildJsonContent(data);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return buildJsonContent({
|
|
244
|
+
ok: false,
|
|
245
|
+
error: 'unknown_tool',
|
|
246
|
+
tool: name,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function buildRemoteTransport() {
|
|
251
|
+
const { StreamableHTTPClientTransport } = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
|
|
252
|
+
const headers = {};
|
|
253
|
+
if (API_KEY) {
|
|
254
|
+
headers.Authorization = `Bearer ${API_KEY}`;
|
|
63
255
|
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ─── MCP Server ──────────────────────────────────────────
|
|
67
|
-
|
|
68
|
-
const server = new Server(
|
|
69
|
-
{ name: "agoragentic", version: "2.0.0" },
|
|
70
|
-
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
// ─── Tools ───────────────────────────────────────────────
|
|
74
|
-
|
|
75
|
-
// Extracted so both MCP and ACP modes can share the same definitions
|
|
76
|
-
function getToolList() {
|
|
77
|
-
return [
|
|
78
|
-
// ── Core Marketplace ──
|
|
79
|
-
{
|
|
80
|
-
name: "agoragentic_register",
|
|
81
|
-
description: "Register as a new agent on Agoragentic. Returns an API key and access to the Starter Pack. Starter pack rewards are fee discounts, not free credits.",
|
|
82
|
-
annotations: { title: "Register Agent", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
83
|
-
inputSchema: {
|
|
84
|
-
type: "object",
|
|
85
|
-
properties: {
|
|
86
|
-
agent_name: { type: "string", description: "Your agent's display name (must be unique across the marketplace)" },
|
|
87
|
-
agent_type: { type: "string", enum: ["buyer", "seller", "both"], default: "both", description: "Agent role: buyer (consume services), seller (provide services), or both" }
|
|
88
|
-
},
|
|
89
|
-
required: ["agent_name"]
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
name: "agoragentic_search",
|
|
94
|
-
description: "Search Agoragentic for agent capabilities. Find tools, services, datasets, and skills available through the capability router. Returns names, descriptions, prices (USDC), and IDs you can use to invoke them.",
|
|
95
|
-
annotations: { title: "Search Capabilities", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
96
|
-
inputSchema: {
|
|
97
|
-
type: "object",
|
|
98
|
-
properties: {
|
|
99
|
-
query: { type: "string", description: "Search term to filter capabilities (e.g., 'summarize', 'translate', 'research')" },
|
|
100
|
-
category: { type: "string", description: "Category filter (e.g., research, creative, data, agent-upgrades, infrastructure)" },
|
|
101
|
-
max_price: { type: "number", description: "Maximum price in USDC to filter results by cost" },
|
|
102
|
-
limit: { type: "number", default: 10, description: "Maximum number of results to return (1 to 50)" }
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
name: "agoragentic_invoke",
|
|
108
|
-
description: "Invoke (call/use) a capability from the Agoragentic marketplace. Payment is automatic from your USDC balance. Returns the capability's output.",
|
|
109
|
-
annotations: { title: "Invoke Capability", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
110
|
-
inputSchema: {
|
|
111
|
-
type: "object",
|
|
112
|
-
properties: {
|
|
113
|
-
capability_id: { type: "string", description: "The capability ID returned from a search result" },
|
|
114
|
-
input: { type: "object", description: "Input payload for the capability as a JSON object", default: {} }
|
|
115
|
-
},
|
|
116
|
-
required: ["capability_id"]
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
name: "agoragentic_vault",
|
|
121
|
-
description: "View your agent's vault (inventory). Shows all items you own: skills, datasets, licenses, collectibles, and service results from previous invocations.",
|
|
122
|
-
annotations: { title: "View Vault", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
123
|
-
inputSchema: {
|
|
124
|
-
type: "object",
|
|
125
|
-
properties: {
|
|
126
|
-
item_type: { type: "string", description: "Filter by item type: skill, digital_asset, nft, license, subscription, or collectible" },
|
|
127
|
-
include_nfts: { type: "boolean", description: "Include on-chain NFTs minted on Base L2 blockchain", default: false },
|
|
128
|
-
limit: { type: "number", default: 20, description: "Maximum number of vault items to return" }
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
name: "agoragentic_categories",
|
|
134
|
-
description: "List all available marketplace categories and how many capabilities are in each.",
|
|
135
|
-
annotations: { title: "List Categories", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
136
|
-
inputSchema: { type: "object", properties: {} }
|
|
137
|
-
},
|
|
138
256
|
|
|
139
|
-
|
|
140
|
-
{
|
|
141
|
-
|
|
142
|
-
description: "Write a key value pair to your persistent agent memory. Survives across sessions, IDEs, and machines. Costs $0.10 per write via the marketplace.",
|
|
143
|
-
annotations: { title: "Write Memory", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
144
|
-
inputSchema: {
|
|
145
|
-
type: "object",
|
|
146
|
-
properties: {
|
|
147
|
-
key: { type: "string", description: "Memory key identifier, maximum 256 characters" },
|
|
148
|
-
value: { type: "string", description: "Value to store, maximum 64KB. Can be any string or serialized JSON." },
|
|
149
|
-
namespace: { type: "string", default: "default", description: "Namespace to organize keys into logical groups" },
|
|
150
|
-
ttl_seconds: { type: "number", description: "Automatic expiration in seconds. Omit for permanent storage." }
|
|
151
|
-
},
|
|
152
|
-
required: ["key", "value"]
|
|
153
|
-
}
|
|
257
|
+
return new StreamableHTTPClientTransport(new URL(REMOTE_MCP_URL), {
|
|
258
|
+
requestInit: {
|
|
259
|
+
headers,
|
|
154
260
|
},
|
|
155
|
-
|
|
156
|
-
name: "agoragentic_memory_read",
|
|
157
|
-
description: "Read from your persistent agent memory. Free, no cost to recall your own data. Returns a single key or lists all keys.",
|
|
158
|
-
annotations: { title: "Read Memory", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
159
|
-
inputSchema: {
|
|
160
|
-
type: "object",
|
|
161
|
-
properties: {
|
|
162
|
-
key: { type: "string", description: "Specific key to read. Omit to list all stored keys." },
|
|
163
|
-
namespace: { type: "string", default: "default", description: "Namespace to read from" },
|
|
164
|
-
prefix: { type: "string", description: "Filter keys by prefix when listing all keys" }
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
|
|
169
|
-
// ── Vault Secrets ──
|
|
170
|
-
{
|
|
171
|
-
name: "agoragentic_secret_store",
|
|
172
|
-
description: "Store an encrypted secret (API key, token, password) in your vault. AES 256 encrypted at rest. Costs $0.25 via the marketplace.",
|
|
173
|
-
annotations: { title: "Store Secret", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
174
|
-
inputSchema: {
|
|
175
|
-
type: "object",
|
|
176
|
-
properties: {
|
|
177
|
-
label: { type: "string", description: "Label for the secret, for example 'openai_key' or 'stripe_token'" },
|
|
178
|
-
secret: { type: "string", description: "The secret value to encrypt and store securely" },
|
|
179
|
-
hint: { type: "string", description: "Optional human readable hint to help you remember what this secret is for" }
|
|
180
|
-
},
|
|
181
|
-
required: ["label", "secret"]
|
|
182
|
-
}
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
name: "agoragentic_secret_retrieve",
|
|
186
|
-
description: "Retrieve a decrypted secret from your vault. Free, no cost to access your own credentials.",
|
|
187
|
-
annotations: { title: "Retrieve Secret", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
188
|
-
inputSchema: {
|
|
189
|
-
type: "object",
|
|
190
|
-
properties: {
|
|
191
|
-
label: { type: "string", description: "Label of the secret to retrieve. Omit to list all stored secrets." }
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
|
|
196
|
-
// ── Passport ──
|
|
197
|
-
{
|
|
198
|
-
name: "agoragentic_passport",
|
|
199
|
-
description: "Check your Agoragentic Passport NFT status, or get info about the passport system. Passports are on chain identity NFTs on Base L2.",
|
|
200
|
-
annotations: { title: "Agent Passport", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
201
|
-
inputSchema: {
|
|
202
|
-
type: "object",
|
|
203
|
-
properties: {
|
|
204
|
-
action: {
|
|
205
|
-
type: "string",
|
|
206
|
-
enum: ["check", "info", "verify"],
|
|
207
|
-
default: "check",
|
|
208
|
-
description: "Action to perform: check your passport status, info for system overview, or verify a wallet address"
|
|
209
|
-
},
|
|
210
|
-
wallet_address: { type: "string", description: "Wallet address to verify ownership. Only used when action is set to verify." }
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
];
|
|
261
|
+
});
|
|
215
262
|
}
|
|
216
263
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
status: "registered",
|
|
234
|
-
agent_id: data.agent?.id,
|
|
235
|
-
api_key: data.api_key,
|
|
236
|
-
fee_rate: "3.00%",
|
|
237
|
-
message: "Save your API key! Set it as AGORAGENTIC_API_KEY environment variable.",
|
|
238
|
-
next: "Use agoragentic_search to find capabilities, or agoragentic_invoke to call one directly"
|
|
239
|
-
}, null, 2)
|
|
240
|
-
}]
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
case "agoragentic_search": {
|
|
245
|
-
const params = new URLSearchParams({ limit: args.limit || 10, status: "active" });
|
|
246
|
-
if (args.query) params.set("search", args.query);
|
|
247
|
-
if (args.category) params.set("category", args.category);
|
|
248
|
-
|
|
249
|
-
const data = await apiCall("GET", `/api/capabilities?${params}`);
|
|
250
|
-
let capabilities = Array.isArray(data) ? data : (data.capabilities || []);
|
|
251
|
-
|
|
252
|
-
if (args.max_price !== undefined) {
|
|
253
|
-
capabilities = capabilities.filter(c => (c.price_per_unit || 0) <= args.max_price);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const results = capabilities.slice(0, args.limit || 10).map(c => ({
|
|
257
|
-
id: c.id,
|
|
258
|
-
name: c.name,
|
|
259
|
-
description: (c.description || "").substring(0, 200),
|
|
260
|
-
category: c.category,
|
|
261
|
-
price_usdc: c.price_per_unit,
|
|
262
|
-
seller: c.seller_name,
|
|
263
|
-
type: c.listing_type
|
|
264
|
-
}));
|
|
265
|
-
|
|
266
|
-
return {
|
|
267
|
-
content: [{
|
|
268
|
-
type: "text",
|
|
269
|
-
text: JSON.stringify({ total: results.length, capabilities: results }, null, 2)
|
|
270
|
-
}]
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
case "agoragentic_invoke": {
|
|
275
|
-
if (!API_KEY) {
|
|
276
|
-
return { content: [{ type: "text", text: "Error: Set AGORAGENTIC_API_KEY environment variable first. Use agoragentic_register to get one." }] };
|
|
277
|
-
}
|
|
278
|
-
const capId = String(args.capability_id || "").replace(/[^a-zA-Z0-9\-_]/g, "");
|
|
279
|
-
if (!capId) {
|
|
280
|
-
return { content: [{ type: "text", text: "Error: Invalid capability_id." }] };
|
|
281
|
-
}
|
|
282
|
-
const data = await apiCall("POST", `/api/invoke/${capId}`, {
|
|
283
|
-
input: args.input || {}
|
|
284
|
-
});
|
|
285
|
-
return {
|
|
286
|
-
content: [{
|
|
287
|
-
type: "text",
|
|
288
|
-
text: JSON.stringify({
|
|
289
|
-
invocation_id: data.invocation_id,
|
|
290
|
-
status: data.status,
|
|
291
|
-
output: data.response,
|
|
292
|
-
cost_usdc: data.cost,
|
|
293
|
-
balance_after: data.buyer_balance,
|
|
294
|
-
nft: data.nft || null,
|
|
295
|
-
vault_item: data.vault || null
|
|
296
|
-
}, null, 2)
|
|
297
|
-
}]
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
case "agoragentic_vault": {
|
|
302
|
-
if (!API_KEY) {
|
|
303
|
-
return { content: [{ type: "text", text: "Error: Set AGORAGENTIC_API_KEY environment variable first." }] };
|
|
304
|
-
}
|
|
305
|
-
const params = new URLSearchParams({ limit: args.limit || 20 });
|
|
306
|
-
if (args.item_type) params.set("type", args.item_type);
|
|
307
|
-
if (args.include_nfts) params.set("include", "nfts");
|
|
308
|
-
|
|
309
|
-
const data = await apiCall("GET", `/api/inventory?${params}`);
|
|
310
|
-
const vault = data.vault || {};
|
|
311
|
-
return {
|
|
312
|
-
content: [{
|
|
313
|
-
type: "text",
|
|
314
|
-
text: JSON.stringify({
|
|
315
|
-
agent: vault.agent_name,
|
|
316
|
-
total_items: vault.total_items,
|
|
317
|
-
items: (vault.items || []).map(i => ({
|
|
318
|
-
name: i.item_name,
|
|
319
|
-
type: i.item_type,
|
|
320
|
-
status: i.status,
|
|
321
|
-
acquired: i.acquired_at,
|
|
322
|
-
integrity_warning: i.integrity_warning
|
|
323
|
-
})),
|
|
324
|
-
nfts: data.nfts || null
|
|
325
|
-
}, null, 2)
|
|
326
|
-
}]
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
case "agoragentic_categories": {
|
|
331
|
-
const data = await apiCall("GET", "/api/categories");
|
|
332
|
-
return {
|
|
333
|
-
content: [{
|
|
334
|
-
type: "text",
|
|
335
|
-
text: JSON.stringify(data, null, 2)
|
|
336
|
-
}]
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// ── Vault Memory ──
|
|
341
|
-
|
|
342
|
-
case "agoragentic_memory_write": {
|
|
343
|
-
if (!API_KEY) {
|
|
344
|
-
return { content: [{ type: "text", text: "Error: API key required." }] };
|
|
345
|
-
}
|
|
346
|
-
// Find the Memory Slots listing and invoke through marketplace
|
|
347
|
-
const searchData = await apiCall("GET", "/api/capabilities?search=Vault+Memory+Slots&limit=1");
|
|
348
|
-
const listings = Array.isArray(searchData) ? searchData : (searchData.capabilities || []);
|
|
349
|
-
const memoryListing = listings.find(l => l.name === 'Vault Memory Slots');
|
|
350
|
-
|
|
351
|
-
if (memoryListing) {
|
|
352
|
-
const data = await apiCall("POST", `/api/invoke/${memoryListing.id}`, {
|
|
353
|
-
input: {
|
|
354
|
-
key: args.key,
|
|
355
|
-
value: args.value,
|
|
356
|
-
namespace: args.namespace || 'default',
|
|
357
|
-
ttl_seconds: args.ttl_seconds
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
return {
|
|
361
|
-
content: [{
|
|
362
|
-
type: "text",
|
|
363
|
-
text: JSON.stringify({
|
|
364
|
-
status: data.status,
|
|
365
|
-
output: data.response?.output || data.response,
|
|
366
|
-
cost: data.cost,
|
|
367
|
-
balance_after: data.buyer_balance
|
|
368
|
-
}, null, 2)
|
|
369
|
-
}]
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Fallback: direct API call
|
|
374
|
-
const data = await apiCall("POST", "/api/vault/memory", {
|
|
375
|
-
input: {
|
|
376
|
-
key: args.key,
|
|
377
|
-
value: args.value,
|
|
378
|
-
namespace: args.namespace || 'default',
|
|
379
|
-
ttl_seconds: args.ttl_seconds
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
case "agoragentic_memory_read": {
|
|
386
|
-
if (!API_KEY) {
|
|
387
|
-
return { content: [{ type: "text", text: "Error: API key required." }] };
|
|
388
|
-
}
|
|
389
|
-
const params = new URLSearchParams();
|
|
390
|
-
if (args.key) params.set("key", args.key);
|
|
391
|
-
if (args.namespace) params.set("namespace", args.namespace);
|
|
392
|
-
if (args.prefix) params.set("prefix", args.prefix);
|
|
393
|
-
|
|
394
|
-
const data = await apiCall("GET", `/api/vault/memory?${params}`);
|
|
395
|
-
return {
|
|
396
|
-
content: [{
|
|
397
|
-
type: "text",
|
|
398
|
-
text: JSON.stringify(data.output || data, null, 2)
|
|
399
|
-
}]
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// ── Vault Secrets ──
|
|
404
|
-
|
|
405
|
-
case "agoragentic_secret_store": {
|
|
406
|
-
if (!API_KEY) {
|
|
407
|
-
return { content: [{ type: "text", text: "Error: API key required." }] };
|
|
408
|
-
}
|
|
409
|
-
const searchData = await apiCall("GET", "/api/capabilities?search=Vault+Secrets+Locker&limit=1");
|
|
410
|
-
const listings = Array.isArray(searchData) ? searchData : (searchData.capabilities || []);
|
|
411
|
-
const secretsListing = listings.find(l => l.name === 'Vault Secrets Locker');
|
|
412
|
-
|
|
413
|
-
if (secretsListing) {
|
|
414
|
-
const data = await apiCall("POST", `/api/invoke/${secretsListing.id}`, {
|
|
415
|
-
input: {
|
|
416
|
-
label: args.label,
|
|
417
|
-
secret: args.secret,
|
|
418
|
-
hint: args.hint
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
return {
|
|
422
|
-
content: [{
|
|
423
|
-
type: "text",
|
|
424
|
-
text: JSON.stringify({
|
|
425
|
-
status: data.status,
|
|
426
|
-
output: data.response?.output || data.response,
|
|
427
|
-
cost: data.cost,
|
|
428
|
-
balance_after: data.buyer_balance
|
|
429
|
-
}, null, 2)
|
|
430
|
-
}]
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
const data = await apiCall("POST", "/api/vault/secrets", {
|
|
435
|
-
input: { label: args.label, secret: args.secret, hint: args.hint }
|
|
436
|
-
});
|
|
437
|
-
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
case "agoragentic_secret_retrieve": {
|
|
441
|
-
if (!API_KEY) {
|
|
442
|
-
return { content: [{ type: "text", text: "Error: API key required." }] };
|
|
443
|
-
}
|
|
444
|
-
const params = new URLSearchParams();
|
|
445
|
-
if (args.label) params.set("label", args.label);
|
|
446
|
-
|
|
447
|
-
const data = await apiCall("GET", `/api/vault/secrets?${params}`);
|
|
448
|
-
return {
|
|
449
|
-
content: [{
|
|
450
|
-
type: "text",
|
|
451
|
-
text: JSON.stringify(data.output || data, null, 2)
|
|
452
|
-
}]
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// ── Passport ──
|
|
457
|
-
|
|
458
|
-
case "agoragentic_passport": {
|
|
459
|
-
const action = args.action || "check";
|
|
460
|
-
|
|
461
|
-
if (action === "info") {
|
|
462
|
-
const data = await apiCall("GET", "/api/passport/info");
|
|
463
|
-
return { content: [{ type: "text", text: JSON.stringify(data.output || data, null, 2) }] };
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
if (action === "verify" && args.wallet_address) {
|
|
467
|
-
const data = await apiCall("GET", `/api/passport/verify/${args.wallet_address}`);
|
|
468
|
-
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
if (!API_KEY) {
|
|
472
|
-
return { content: [{ type: "text", text: "Error: API key required to check passport." }] };
|
|
473
|
-
}
|
|
474
|
-
const data = await apiCall("GET", "/api/passport/check");
|
|
475
|
-
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
default:
|
|
479
|
-
return { content: [{ type: "text", text: `Unknown tool: ${name}` }] };
|
|
480
|
-
}
|
|
481
|
-
} catch (err) {
|
|
482
|
-
return { content: [{ type: "text", text: `Error: ${err.message}` }] };
|
|
483
|
-
}
|
|
264
|
+
async function connectRemoteClient() {
|
|
265
|
+
const { Client } = require('@modelcontextprotocol/sdk/client/index.js');
|
|
266
|
+
const transport = buildRemoteTransport();
|
|
267
|
+
const client = new Client(
|
|
268
|
+
{ name: 'agoragentic-mcp', version: PACKAGE_VERSION },
|
|
269
|
+
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
client.onerror = (error) => {
|
|
273
|
+
if (!error) return;
|
|
274
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
275
|
+
console.error(`[agoragentic-mcp] remote client error: ${message}`);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
await client.connect(transport);
|
|
279
|
+
return { client, transport };
|
|
484
280
|
}
|
|
485
281
|
|
|
486
|
-
|
|
487
|
-
const {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
},
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
}
|
|
513
|
-
{
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
}
|
|
554
|
-
};
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
282
|
+
async function runMcpRelay() {
|
|
283
|
+
const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
|
|
284
|
+
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
285
|
+
const {
|
|
286
|
+
CallToolRequestSchema,
|
|
287
|
+
ListToolsRequestSchema,
|
|
288
|
+
ListResourcesRequestSchema,
|
|
289
|
+
ReadResourceRequestSchema,
|
|
290
|
+
ListPromptsRequestSchema,
|
|
291
|
+
GetPromptRequestSchema,
|
|
292
|
+
} = require('@modelcontextprotocol/sdk/types.js');
|
|
293
|
+
|
|
294
|
+
const server = new Server(
|
|
295
|
+
{ name: 'agoragentic', version: PACKAGE_VERSION },
|
|
296
|
+
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
let remoteSession = null;
|
|
300
|
+
try {
|
|
301
|
+
remoteSession = await connectRemoteClient();
|
|
302
|
+
} catch (error) {
|
|
303
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
304
|
+
console.error(`[agoragentic-mcp] remote relay unavailable, using local fallback tools: ${message}`);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (remoteSession) {
|
|
308
|
+
const { client } = remoteSession;
|
|
309
|
+
server.setRequestHandler(ListToolsRequestSchema, async (request) => {
|
|
310
|
+
const result = await client.listTools(request.params);
|
|
311
|
+
return {
|
|
312
|
+
...result,
|
|
313
|
+
tools: mergeFallbackTools(result.tools),
|
|
314
|
+
};
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
318
|
+
if (
|
|
319
|
+
request.params.name === 'agoragentic_match' ||
|
|
320
|
+
request.params.name === 'agoragentic_execute' ||
|
|
321
|
+
request.params.name === 'agoragentic_execute_status'
|
|
322
|
+
) {
|
|
323
|
+
return executeFallbackTool(request.params.name, request.params.arguments || {});
|
|
324
|
+
}
|
|
325
|
+
return client.callTool(request.params);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
|
|
329
|
+
return client.listResources(request.params);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
333
|
+
return client.readResource(request.params);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
server.setRequestHandler(ListPromptsRequestSchema, async (request) => {
|
|
337
|
+
return client.listPrompts(request.params);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
341
|
+
return client.getPrompt(request.params);
|
|
342
|
+
});
|
|
343
|
+
} else {
|
|
344
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
345
|
+
return { tools: buildFallbackToolList() };
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
349
|
+
return executeFallbackTool(request.params.name, request.params.arguments || {});
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
353
|
+
return { resources: [] };
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
server.setRequestHandler(ReadResourceRequestSchema, async () => {
|
|
357
|
+
throw new Error('Resources are unavailable while the remote Agoragentic MCP relay is unreachable.');
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
361
|
+
return { prompts: [] };
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
server.setRequestHandler(GetPromptRequestSchema, async () => {
|
|
365
|
+
throw new Error('Prompts are unavailable while the remote Agoragentic MCP relay is unreachable.');
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const stdio = new StdioServerTransport();
|
|
370
|
+
await server.connect(stdio);
|
|
371
|
+
|
|
372
|
+
const shutdown = async (signal) => {
|
|
373
|
+
console.error(`[agoragentic-mcp] shutting down on ${signal}`);
|
|
374
|
+
if (remoteSession) {
|
|
375
|
+
try {
|
|
376
|
+
await remoteSession.transport.terminateSession();
|
|
377
|
+
} catch {
|
|
378
|
+
// Ignore session teardown failures during local shutdown.
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
await remoteSession.transport.close();
|
|
382
|
+
} catch {
|
|
383
|
+
// Ignore transport close failures during local shutdown.
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
process.exit(0);
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
process.on('SIGINT', () => {
|
|
390
|
+
void shutdown('SIGINT');
|
|
391
|
+
});
|
|
392
|
+
process.on('SIGTERM', () => {
|
|
393
|
+
void shutdown('SIGTERM');
|
|
394
|
+
});
|
|
570
395
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
396
|
+
if (remoteSession) {
|
|
397
|
+
console.error(`[agoragentic-mcp] stdio relay ${PACKAGE_VERSION} connected to ${REMOTE_MCP_URL}`);
|
|
398
|
+
} else {
|
|
399
|
+
console.error(`[agoragentic-mcp] stdio fallback ${PACKAGE_VERSION} using ${AGORAGENTIC_BASE}`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function buildAcpInitializeResult() {
|
|
404
|
+
return {
|
|
405
|
+
protocolVersion: 1,
|
|
406
|
+
agentInfo: {
|
|
407
|
+
name: 'Agoragentic Agent OS',
|
|
408
|
+
version: PACKAGE_VERSION,
|
|
409
|
+
description:
|
|
410
|
+
'Agent OS integrations for deployed agents and swarms: execute-first routing, receipts, x402 edge calls, and Base USDC settlement.',
|
|
411
|
+
homepage: 'https://agoragentic.com',
|
|
582
412
|
},
|
|
583
|
-
{
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
413
|
+
agentCapabilities: {
|
|
414
|
+
tools: true,
|
|
415
|
+
streaming: false,
|
|
416
|
+
resources: false,
|
|
417
|
+
prompts: false,
|
|
418
|
+
loadSession: false,
|
|
419
|
+
promptCapabilities: {
|
|
420
|
+
image: false,
|
|
421
|
+
},
|
|
590
422
|
},
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
const { name, arguments: args } = request.params;
|
|
604
|
-
|
|
605
|
-
if (name === "quickstart") {
|
|
606
|
-
return {
|
|
607
|
-
description: "Get started with Agoragentic",
|
|
608
|
-
messages: [
|
|
609
|
-
{
|
|
610
|
-
role: "user",
|
|
611
|
-
content: {
|
|
612
|
-
type: "text",
|
|
613
|
-
text: `I want to get started with Agoragentic. Please:\n1. Register me as an agent named "${args?.agent_name || 'my-agent'}"\n2. Search for capabilities${args?.task ? ` related to: ${args.task}` : ''}\n3. Show me the top results and explain how to invoke one\n\nUse the agoragentic_register, agoragentic_search, and agoragentic_categories tools to help me.`
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
]
|
|
617
|
-
};
|
|
618
|
-
}
|
|
423
|
+
authMethods: [
|
|
424
|
+
{
|
|
425
|
+
type: 'env',
|
|
426
|
+
name: 'AGORAGENTIC_API_KEY',
|
|
427
|
+
configured: Boolean(API_KEY),
|
|
428
|
+
required: false,
|
|
429
|
+
instructions:
|
|
430
|
+
'Optional for public discovery and x402 edge calls. Required for authenticated execute/match/status/receipt. Create one with POST /api/quickstart and intent=buyer|seller|both.',
|
|
431
|
+
},
|
|
432
|
+
],
|
|
433
|
+
};
|
|
434
|
+
}
|
|
619
435
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
type: "text",
|
|
628
|
-
text: `I need to: ${args?.task || 'find a useful capability'}\n\nPlease:\n1. Search for matching capabilities${args?.max_budget ? ` under $${args.max_budget} USDC` : ''}\n2. Show me the best match with its price\n3. Invoke it if I confirm\n\nUse agoragentic_search and agoragentic_invoke tools.`
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
]
|
|
632
|
-
};
|
|
633
|
-
}
|
|
436
|
+
function buildAcpResponse(id, result) {
|
|
437
|
+
return {
|
|
438
|
+
jsonrpc: '2.0',
|
|
439
|
+
id,
|
|
440
|
+
result,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
634
443
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
type: "text",
|
|
643
|
-
text: `I want to sell a capability called "${args?.capability_name || 'my-service'}"${args?.price ? ` for $${args.price} USDC per call` : ''}.\n\nPlease walk me through:\n1. What information I need to provide\n2. The staking bond requirement ($5 USDC, refundable after 30 days)\n3. How to register and list it\n\nCheck agoragentic_categories for available categories first.`
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
]
|
|
647
|
-
};
|
|
648
|
-
}
|
|
444
|
+
function buildAcpError(id, code, message, data) {
|
|
445
|
+
return {
|
|
446
|
+
jsonrpc: '2.0',
|
|
447
|
+
id,
|
|
448
|
+
error: data ? { code, message, data } : { code, message },
|
|
449
|
+
};
|
|
450
|
+
}
|
|
649
451
|
|
|
650
|
-
|
|
651
|
-
});
|
|
452
|
+
function writeAcpMessage(message) {
|
|
453
|
+
process.stdout.write(`${JSON.stringify(message)}\n`);
|
|
454
|
+
}
|
|
652
455
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
// (JSON-RPC 2.0 over stdio) instead of MCP. This enables integration
|
|
657
|
-
// with ACP-compatible editors like Zed, JetBrains, VS Code via Copilot, etc.
|
|
658
|
-
//
|
|
659
|
-
// The ACP handshake adds: protocolVersion, agentCapabilities, agentInfo, authMethods
|
|
660
|
-
// Tool definitions and execution are identical to MCP mode.
|
|
456
|
+
function buildAcpSessionId() {
|
|
457
|
+
return `sess_${crypto.randomBytes(12).toString('hex')}`;
|
|
458
|
+
}
|
|
661
459
|
|
|
662
|
-
|
|
460
|
+
function extractAcpPromptText(content) {
|
|
461
|
+
if (typeof content === 'string') return content;
|
|
462
|
+
if (!Array.isArray(content)) return '';
|
|
463
|
+
return content
|
|
464
|
+
.map((part) => {
|
|
465
|
+
if (!part || typeof part !== 'object') return '';
|
|
466
|
+
if (part.type === 'text' && typeof part.text === 'string') return part.text;
|
|
467
|
+
return '';
|
|
468
|
+
})
|
|
469
|
+
.filter(Boolean)
|
|
470
|
+
.join('\n');
|
|
471
|
+
}
|
|
663
472
|
|
|
664
|
-
function
|
|
665
|
-
const
|
|
473
|
+
function buildAcpPromptReply(promptText) {
|
|
474
|
+
const suffix = promptText ? ` Prompt received: ${promptText.slice(0, 240)}` : '';
|
|
475
|
+
return [
|
|
476
|
+
'Agoragentic ACP adapter is a tool bridge, not a code-editing chat agent.',
|
|
477
|
+
'Use tools/list, then tools/call with agoragentic_execute, agoragentic_match, agoragentic_quote, agoragentic_receipt, or stable x402 service tools.',
|
|
478
|
+
suffix,
|
|
479
|
+
]
|
|
480
|
+
.filter(Boolean)
|
|
481
|
+
.join(' ');
|
|
482
|
+
}
|
|
666
483
|
|
|
484
|
+
async function runAcpAdapter() {
|
|
667
485
|
const rl = readline.createInterface({
|
|
668
486
|
input: process.stdin,
|
|
669
|
-
|
|
487
|
+
crlfDelay: Infinity,
|
|
670
488
|
terminal: false,
|
|
671
489
|
});
|
|
672
490
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
}
|
|
491
|
+
let remoteSession = null;
|
|
492
|
+
const acpSessions = new Map();
|
|
676
493
|
|
|
677
|
-
function
|
|
678
|
-
|
|
494
|
+
async function getRemoteSession() {
|
|
495
|
+
if (!remoteSession) {
|
|
496
|
+
remoteSession = await connectRemoteClient();
|
|
497
|
+
}
|
|
498
|
+
return remoteSession;
|
|
679
499
|
}
|
|
680
500
|
|
|
681
|
-
function
|
|
682
|
-
|
|
501
|
+
async function shutdownRemote() {
|
|
502
|
+
if (!remoteSession) return;
|
|
503
|
+
try {
|
|
504
|
+
await remoteSession.transport.terminateSession();
|
|
505
|
+
} catch {
|
|
506
|
+
// Ignore session teardown failures during local shutdown.
|
|
507
|
+
}
|
|
508
|
+
try {
|
|
509
|
+
await remoteSession.transport.close();
|
|
510
|
+
} catch {
|
|
511
|
+
// Ignore transport close failures during local shutdown.
|
|
512
|
+
}
|
|
513
|
+
remoteSession = null;
|
|
683
514
|
}
|
|
684
515
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
description: "Agoragentic agent marketplace — 174+ AI capabilities, USDC payments on Base L2",
|
|
697
|
-
},
|
|
698
|
-
authMethods: API_KEY
|
|
699
|
-
? [] // Already authenticated via env var
|
|
700
|
-
: [
|
|
701
|
-
{
|
|
702
|
-
type: "terminal",
|
|
703
|
-
description: "Set AGORAGENTIC_API_KEY environment variable. Get a key via the agoragentic_register tool.",
|
|
704
|
-
},
|
|
705
|
-
],
|
|
706
|
-
});
|
|
707
|
-
}
|
|
516
|
+
process.on('SIGINT', () => {
|
|
517
|
+
void shutdownRemote().finally(() => process.exit(0));
|
|
518
|
+
});
|
|
519
|
+
process.on('SIGTERM', () => {
|
|
520
|
+
void shutdownRemote().finally(() => process.exit(0));
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
console.error(`[agoragentic-mcp] ACP adapter ${PACKAGE_VERSION} ready`);
|
|
524
|
+
|
|
525
|
+
for await (const line of rl) {
|
|
526
|
+
if (!line.trim()) continue;
|
|
708
527
|
|
|
709
|
-
|
|
710
|
-
let msg;
|
|
528
|
+
let request;
|
|
711
529
|
try {
|
|
712
|
-
|
|
713
|
-
} catch {
|
|
714
|
-
|
|
715
|
-
|
|
530
|
+
request = JSON.parse(line);
|
|
531
|
+
} catch (error) {
|
|
532
|
+
writeAcpMessage(buildAcpError(null, -32700, 'Invalid JSON-RPC payload'));
|
|
533
|
+
continue;
|
|
716
534
|
}
|
|
717
535
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
536
|
+
const hasId = Object.prototype.hasOwnProperty.call(request, 'id');
|
|
537
|
+
const id = hasId ? request.id : null;
|
|
538
|
+
|
|
539
|
+
function writeResponse(message) {
|
|
540
|
+
if (hasId) writeAcpMessage(message);
|
|
721
541
|
}
|
|
722
542
|
|
|
723
543
|
try {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
annotations: t.annotations,
|
|
740
|
-
})),
|
|
741
|
-
});
|
|
742
|
-
break;
|
|
743
|
-
|
|
744
|
-
case "tools/call": {
|
|
745
|
-
const { name, arguments: callArgs } = msg.params || {};
|
|
746
|
-
const result = await executeToolCall(name, callArgs || {});
|
|
747
|
-
sendResult(msg.id, result);
|
|
748
|
-
break;
|
|
544
|
+
if (request.method === 'initialize') {
|
|
545
|
+
writeResponse(buildAcpResponse(id, buildAcpInitializeResult()));
|
|
546
|
+
} else if (request.method === 'session/new') {
|
|
547
|
+
const sessionId = buildAcpSessionId();
|
|
548
|
+
acpSessions.set(sessionId, {
|
|
549
|
+
cwd: request.params?.cwd || process.cwd(),
|
|
550
|
+
createdAt: new Date().toISOString(),
|
|
551
|
+
cancelled: false,
|
|
552
|
+
});
|
|
553
|
+
writeResponse(buildAcpResponse(id, { sessionId }));
|
|
554
|
+
} else if (request.method === 'session/prompt') {
|
|
555
|
+
const sessionId = request.params?.sessionId;
|
|
556
|
+
if (!sessionId || !acpSessions.has(sessionId)) {
|
|
557
|
+
writeResponse(buildAcpError(id, -32602, 'Unknown or missing ACP sessionId'));
|
|
558
|
+
continue;
|
|
749
559
|
}
|
|
750
560
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
561
|
+
const session = acpSessions.get(sessionId);
|
|
562
|
+
session.cancelled = false;
|
|
563
|
+
const promptText = extractAcpPromptText(request.params?.content);
|
|
564
|
+
const reply = buildAcpPromptReply(promptText);
|
|
565
|
+
|
|
566
|
+
writeAcpMessage({
|
|
567
|
+
jsonrpc: '2.0',
|
|
568
|
+
method: 'session/update',
|
|
569
|
+
params: {
|
|
570
|
+
sessionId,
|
|
571
|
+
update: {
|
|
572
|
+
sessionUpdate: 'agent_message_chunk',
|
|
573
|
+
content: {
|
|
574
|
+
type: 'text',
|
|
575
|
+
text: reply,
|
|
576
|
+
},
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
});
|
|
580
|
+
writeResponse(buildAcpResponse(id, { stopReason: session.cancelled ? 'cancelled' : 'end_turn' }));
|
|
581
|
+
} else if (request.method === 'session/cancel') {
|
|
582
|
+
const sessionId = request.params?.sessionId;
|
|
583
|
+
if (sessionId && acpSessions.has(sessionId)) {
|
|
584
|
+
acpSessions.get(sessionId).cancelled = true;
|
|
585
|
+
}
|
|
586
|
+
writeResponse(buildAcpResponse(id, { ok: true }));
|
|
587
|
+
} else if (request.method === 'tools/list') {
|
|
588
|
+
writeResponse(buildAcpResponse(id, { tools: ACP_TOOLS }));
|
|
589
|
+
} else if (request.method === 'tools/call') {
|
|
590
|
+
const { client } = await getRemoteSession();
|
|
591
|
+
const result = await client.callTool(request.params || {});
|
|
592
|
+
writeResponse(buildAcpResponse(id, result));
|
|
593
|
+
} else if (request.method === 'shutdown') {
|
|
594
|
+
await shutdownRemote();
|
|
595
|
+
writeResponse(buildAcpResponse(id, { ok: true }));
|
|
763
596
|
} else {
|
|
764
|
-
|
|
597
|
+
writeResponse(
|
|
598
|
+
buildAcpError(id, -32601, 'Unsupported ACP method', {
|
|
599
|
+
supported_methods: [
|
|
600
|
+
'initialize',
|
|
601
|
+
'session/new',
|
|
602
|
+
'session/prompt',
|
|
603
|
+
'session/cancel',
|
|
604
|
+
'tools/list',
|
|
605
|
+
'tools/call',
|
|
606
|
+
'shutdown',
|
|
607
|
+
],
|
|
608
|
+
})
|
|
609
|
+
);
|
|
765
610
|
}
|
|
611
|
+
} catch (error) {
|
|
612
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
613
|
+
writeResponse(buildAcpError(id, -32000, message));
|
|
766
614
|
}
|
|
767
|
-
});
|
|
768
|
-
|
|
769
|
-
rl.on("close", () => process.exit(0));
|
|
770
|
-
|
|
771
|
-
console.error("Agoragentic ACP Agent v2.0 running on stdio (ACP mode)");
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
// ─── Start ───────────────────────────────────────────────
|
|
775
|
-
|
|
776
|
-
async function main() {
|
|
777
|
-
if (ACP_MODE) {
|
|
778
|
-
createAcpServer();
|
|
779
|
-
} else {
|
|
780
|
-
const transport = new StdioServerTransport();
|
|
781
|
-
await server.connect(transport);
|
|
782
|
-
console.error("Agoragentic MCP Server v2.0 running on stdio");
|
|
783
615
|
}
|
|
616
|
+
|
|
617
|
+
await shutdownRemote();
|
|
784
618
|
}
|
|
785
619
|
|
|
786
|
-
|
|
620
|
+
const entrypoint = ACP_MODE ? runAcpAdapter : runMcpRelay;
|
|
787
621
|
|
|
622
|
+
entrypoint().catch((error) => {
|
|
623
|
+
const message = error instanceof Error ? error.stack || error.message : String(error);
|
|
624
|
+
console.error(`[agoragentic-mcp] fatal: ${message}`);
|
|
625
|
+
process.exit(1);
|
|
626
|
+
});
|