agoragentic-mcp 1.3.1 → 1.3.3
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 -21
- package/README.md +182 -169
- package/mcp-server.js +710 -377
- package/package.json +47 -47
- package/scripts/postinstall.js +22 -22
package/mcp-server.js
CHANGED
|
@@ -1,377 +1,710 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
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
|
|
10
|
-
const
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
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 RAW_API_KEY = process.env.AGORAGENTIC_API_KEY || '';
|
|
11
|
+
const PLACEHOLDER_API_KEYS = new Set([
|
|
12
|
+
'amk_glama_test_placeholder',
|
|
13
|
+
'amk_your_key',
|
|
14
|
+
'amk_your_key_here',
|
|
15
|
+
'amk_placeholder',
|
|
16
|
+
'amk_test_placeholder',
|
|
17
|
+
]);
|
|
18
|
+
const API_KEY = PLACEHOLDER_API_KEYS.has(RAW_API_KEY.trim()) ? '' : RAW_API_KEY.trim();
|
|
19
|
+
const ACP_MODE = process.argv.includes('--acp');
|
|
20
|
+
|
|
21
|
+
const ACP_TOOLS = [
|
|
22
|
+
{
|
|
23
|
+
name: 'agoragentic_execute',
|
|
24
|
+
description: 'Route a task through Agent OS execute() with provider selection, fallback, receipts, and settlement.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'agoragentic_match',
|
|
28
|
+
description: 'Preview routed providers before execution.',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'agoragentic_quote',
|
|
32
|
+
description: 'Create a bounded quote before paid execution.',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'agoragentic_status',
|
|
36
|
+
description: 'Inspect execution status for an invocation.',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'agoragentic_receipt',
|
|
40
|
+
description: 'Fetch normalized receipt and settlement metadata.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'agoragentic_browse_services',
|
|
44
|
+
description: 'Browse stable x402 edge resources.',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'agoragentic_call_service',
|
|
48
|
+
description: 'Call a stable x402 edge resource after payment challenge handling.',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'agoragentic_edge_receipt',
|
|
52
|
+
description: 'Inspect x402 edge receipt metadata.',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'agoragentic_x402_test',
|
|
56
|
+
description: 'Exercise the free x402 pipeline canary.',
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
function buildJsonContent(data) {
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: 'text',
|
|
65
|
+
text: typeof data === 'string' ? data : JSON.stringify(data, null, 2),
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function apiCall(method, path, body) {
|
|
72
|
+
const headers = {
|
|
73
|
+
'Content-Type': 'application/json',
|
|
74
|
+
'User-Agent': `agoragentic-mcp/${PACKAGE_VERSION}`,
|
|
75
|
+
};
|
|
76
|
+
if (API_KEY) {
|
|
77
|
+
headers.Authorization = `Bearer ${API_KEY}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const response = await fetch(`${AGORAGENTIC_BASE}${path}`, {
|
|
81
|
+
method,
|
|
82
|
+
headers,
|
|
83
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const text = await response.text();
|
|
87
|
+
let data;
|
|
88
|
+
try {
|
|
89
|
+
data = text ? JSON.parse(text) : {};
|
|
90
|
+
} catch {
|
|
91
|
+
data = { raw: text };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!response.ok) {
|
|
95
|
+
return {
|
|
96
|
+
ok: false,
|
|
97
|
+
status: response.status,
|
|
98
|
+
error: data.error || data.message || response.statusText,
|
|
99
|
+
details: data,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return data;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function requireApiKey() {
|
|
107
|
+
if (API_KEY) return null;
|
|
108
|
+
return buildJsonContent({
|
|
109
|
+
ok: false,
|
|
110
|
+
error: 'missing_api_key',
|
|
111
|
+
message: 'Set AGORAGENTIC_API_KEY for authenticated Router / Marketplace execution tools. Use agoragentic_register to create a key.',
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function buildFallbackToolList() {
|
|
116
|
+
return [
|
|
117
|
+
{
|
|
118
|
+
name: 'agoragentic_register',
|
|
119
|
+
description:
|
|
120
|
+
'Register a new agent with the Agoragentic marketplace and receive an API key. ' +
|
|
121
|
+
'Use this as the first step before calling agoragentic_execute, agoragentic_match, or agoragentic_execute_status. ' +
|
|
122
|
+
'This tool is idempotent: registering the same agent name returns the existing API key. ' +
|
|
123
|
+
'No AGORAGENTIC_API_KEY environment variable is required to call this tool. ' +
|
|
124
|
+
'Side effects: creates a persistent agent record on the Agoragentic server if one does not exist. ' +
|
|
125
|
+
'Returns JSON with fields: ok (boolean), api_key (string), agent_id (string), and wallet address. ' +
|
|
126
|
+
'On error, returns JSON with ok:false and an error message string.',
|
|
127
|
+
inputSchema: {
|
|
128
|
+
type: 'object',
|
|
129
|
+
properties: {
|
|
130
|
+
name: { type: 'string', description: 'Human-readable agent name, e.g. "my-research-agent"' },
|
|
131
|
+
agent_name: { type: 'string', description: 'Alias for the name field (backward compatibility). Prefer using name instead.' },
|
|
132
|
+
intent: {
|
|
133
|
+
type: 'string',
|
|
134
|
+
enum: ['buyer', 'seller', 'both'],
|
|
135
|
+
description: 'Agent marketplace role: "buyer" to consume services, "seller" to provide them, or "both"',
|
|
136
|
+
default: 'buyer',
|
|
137
|
+
},
|
|
138
|
+
description: { type: 'string', description: 'One-line summary of what this agent does, e.g. "Summarizes web pages on demand"' },
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: 'agoragentic_search',
|
|
144
|
+
description:
|
|
145
|
+
'Search the public Agoragentic marketplace for available agent capabilities and services. ' +
|
|
146
|
+
'Use this to discover what services exist before calling agoragentic_execute or agoragentic_match. ' +
|
|
147
|
+
'This is a read-only operation with no side effects and no USDC spend. ' +
|
|
148
|
+
'No AGORAGENTIC_API_KEY is required. ' +
|
|
149
|
+
'Returns JSON with an array of matching capabilities, each containing id, name, description, category, and price_usdc. ' +
|
|
150
|
+
'Returns an empty array when no capabilities match the query.',
|
|
151
|
+
inputSchema: {
|
|
152
|
+
type: 'object',
|
|
153
|
+
properties: {
|
|
154
|
+
query: { type: 'string', description: 'Free-text search query, e.g. "text summarization" or "web scraping"' },
|
|
155
|
+
category: { type: 'string', description: 'Filter results by category slug, e.g. "ai-ml", "data", or "web". Omit to search all categories.' },
|
|
156
|
+
limit: { type: 'number', description: 'Maximum number of results to return (1\u2013100)', default: 10 },
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: 'agoragentic_match',
|
|
162
|
+
description:
|
|
163
|
+
'Preview which providers the Agoragentic Router would select for a given task, without executing or spending USDC. ' +
|
|
164
|
+
'Use this before agoragentic_execute to compare providers, check pricing, and verify availability. ' +
|
|
165
|
+
'This is a read-only, non-destructive operation with no side effects. ' +
|
|
166
|
+
'Requires the AGORAGENTIC_API_KEY environment variable to be set. Returns ok:false with error "missing_api_key" if the key is absent. ' +
|
|
167
|
+
'Returns JSON with matched providers including their trust scores, estimated cost in USDC, and routing rationale.',
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
properties: {
|
|
171
|
+
task: { type: 'string', description: 'Natural-language task description to match against providers, e.g. "summarize this article"' },
|
|
172
|
+
max_cost: { type: 'number', description: 'Maximum acceptable USDC price per call. Providers above this price are excluded from results.' },
|
|
173
|
+
category: { type: 'string', description: 'Optional category filter to narrow provider matches, e.g. "ai-ml"' },
|
|
174
|
+
prefer_trusted: { type: 'boolean', description: 'When true, rank verified and trusted providers higher in results', default: true },
|
|
175
|
+
},
|
|
176
|
+
required: ['task'],
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: 'agoragentic_execute',
|
|
181
|
+
description:
|
|
182
|
+
'Execute a task through the Agoragentic Router / Marketplace. The Router selects a provider, invokes it, and returns the result with a receipt. ' +
|
|
183
|
+
'IMPORTANT: This tool MAY SPEND USDC from the authenticated agent wallet based on the matched provider listing price. ' +
|
|
184
|
+
'This tool is NOT idempotent \u2014 each call creates a new invocation and may incur a charge. ' +
|
|
185
|
+
'Prefer agoragentic_match first to preview providers and pricing without spending. ' +
|
|
186
|
+
'Use agoragentic_search to discover available capabilities before executing. ' +
|
|
187
|
+
'Do NOT call this tool for read-only discovery; use agoragentic_search or agoragentic_match instead. ' +
|
|
188
|
+
'Requires the AGORAGENTIC_API_KEY environment variable. Returns ok:false with error "missing_api_key" if the key is absent. ' +
|
|
189
|
+
'On success, returns JSON with: invocation_id (string), output (provider result object), cost_usdc (number), provider_id (string), and receipt metadata. ' +
|
|
190
|
+
'On failure, returns JSON with ok:false, a status code, and error details describing routing or provider errors.',
|
|
191
|
+
inputSchema: {
|
|
192
|
+
type: 'object',
|
|
193
|
+
properties: {
|
|
194
|
+
task: {
|
|
195
|
+
type: 'string',
|
|
196
|
+
description: 'Natural-language task for the Router to match and execute, e.g. "summarize this article" or "scrape https://example.com"',
|
|
197
|
+
},
|
|
198
|
+
input: {
|
|
199
|
+
type: 'object',
|
|
200
|
+
description:
|
|
201
|
+
'Structured input payload forwarded to the matched provider. Shape depends on the provider API. ' +
|
|
202
|
+
'Example: {"text": "Hello world", "max_sentences": 3}',
|
|
203
|
+
default: {},
|
|
204
|
+
},
|
|
205
|
+
constraints: {
|
|
206
|
+
type: 'object',
|
|
207
|
+
description:
|
|
208
|
+
'Optional routing and budget constraints. Supported fields: max_cost (number, maximum USDC per call), ' +
|
|
209
|
+
'provider_id (string, pin to a specific provider), category (string). ' +
|
|
210
|
+
'Example: {"max_cost": 0.05, "category": "ai-ml"}',
|
|
211
|
+
default: {},
|
|
212
|
+
},
|
|
213
|
+
quote_id: {
|
|
214
|
+
type: 'string',
|
|
215
|
+
description: 'ID from a prior agoragentic_quote call to lock in a pre-agreed price. Omit for standard dynamic pricing.',
|
|
216
|
+
},
|
|
217
|
+
intent_contract_id: {
|
|
218
|
+
type: 'string',
|
|
219
|
+
description: 'Agent OS intent contract ID for auditable intent-to-execution tracking. Omit if not using intent contracts.',
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
required: ['task'],
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: 'agoragentic_execute_status',
|
|
227
|
+
description:
|
|
228
|
+
'Check the status, output, cost, and receipt of a previous agoragentic_execute invocation. ' +
|
|
229
|
+
'Use this to poll for results of async executions or to retrieve receipt metadata after completion. ' +
|
|
230
|
+
'This is a read-only operation with no side effects and no USDC spend. ' +
|
|
231
|
+
'Requires the AGORAGENTIC_API_KEY environment variable. Returns ok:false with error "missing_api_key" if the key is absent. ' +
|
|
232
|
+
'Returns JSON with: status ("pending", "completed", or "failed"), output (provider result), cost_usdc, provider_id, receipt_id, and timestamps. ' +
|
|
233
|
+
'Returns ok:false with error "invalid_invocation_id" if the ID is empty or contains disallowed characters.',
|
|
234
|
+
inputSchema: {
|
|
235
|
+
type: 'object',
|
|
236
|
+
properties: {
|
|
237
|
+
invocation_id: {
|
|
238
|
+
type: 'string',
|
|
239
|
+
description: 'The invocation_id string returned by a prior agoragentic_execute call, e.g. "inv_abc123def456"',
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
required: ['invocation_id'],
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function mergeFallbackTools(remoteTools = []) {
|
|
249
|
+
const seen = new Set(remoteTools.map((tool) => tool.name));
|
|
250
|
+
const merged = [...remoteTools];
|
|
251
|
+
for (const tool of buildFallbackToolList()) {
|
|
252
|
+
if (!seen.has(tool.name)) {
|
|
253
|
+
merged.push(tool);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return merged;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const FALLBACK_TOOL_NAMES = new Set(buildFallbackToolList().map((tool) => tool.name));
|
|
260
|
+
|
|
261
|
+
async function executeFallbackTool(name, args = {}) {
|
|
262
|
+
if (name === 'agoragentic_register') {
|
|
263
|
+
const agentName = args.agent_name || args.name || 'mcp-agent';
|
|
264
|
+
const data = await apiCall('POST', '/api/quickstart', {
|
|
265
|
+
name: agentName,
|
|
266
|
+
intent: args.intent || 'buyer',
|
|
267
|
+
description: args.description || 'Registered through agoragentic-mcp fallback tools.',
|
|
268
|
+
});
|
|
269
|
+
return buildJsonContent(data);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (name === 'agoragentic_search') {
|
|
273
|
+
const params = new URLSearchParams();
|
|
274
|
+
if (args.query) params.set('q', args.query);
|
|
275
|
+
if (args.category) params.set('category', args.category);
|
|
276
|
+
if (args.limit !== undefined) params.set('limit', String(args.limit));
|
|
277
|
+
const data = await apiCall('GET', `/api/capabilities?${params.toString()}`);
|
|
278
|
+
return buildJsonContent(data);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (name === 'agoragentic_match') {
|
|
282
|
+
const missing = requireApiKey();
|
|
283
|
+
if (missing) return missing;
|
|
284
|
+
const params = new URLSearchParams();
|
|
285
|
+
params.set('task', args.task);
|
|
286
|
+
if (args.max_cost !== undefined) params.set('max_cost', String(args.max_cost));
|
|
287
|
+
if (args.category) params.set('category', args.category);
|
|
288
|
+
if (args.prefer_trusted !== undefined) params.set('prefer_trusted', args.prefer_trusted ? 'true' : 'false');
|
|
289
|
+
const data = await apiCall('GET', `/api/execute/match?${params.toString()}`);
|
|
290
|
+
return buildJsonContent(data);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (name === 'agoragentic_execute') {
|
|
294
|
+
const missing = requireApiKey();
|
|
295
|
+
if (missing) return missing;
|
|
296
|
+
const payload = {
|
|
297
|
+
task: args.task,
|
|
298
|
+
input: args.input || {},
|
|
299
|
+
constraints: args.constraints || {},
|
|
300
|
+
};
|
|
301
|
+
if (args.quote_id) payload.quote_id = args.quote_id;
|
|
302
|
+
if (args.intent_contract_id) payload.intent_contract_id = args.intent_contract_id;
|
|
303
|
+
const data = await apiCall('POST', '/api/execute', payload);
|
|
304
|
+
return buildJsonContent(data);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (name === 'agoragentic_execute_status') {
|
|
308
|
+
const missing = requireApiKey();
|
|
309
|
+
if (missing) return missing;
|
|
310
|
+
const invocationId = String(args.invocation_id || '').replace(/[^a-zA-Z0-9\-_]/g, '');
|
|
311
|
+
if (!invocationId) return buildJsonContent({ ok: false, error: 'invalid_invocation_id' });
|
|
312
|
+
const data = await apiCall('GET', `/api/execute/status/${invocationId}`);
|
|
313
|
+
return buildJsonContent(data);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return buildJsonContent({
|
|
317
|
+
ok: false,
|
|
318
|
+
error: 'unknown_tool',
|
|
319
|
+
tool: name,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function buildRemoteTransport() {
|
|
324
|
+
const { StreamableHTTPClientTransport } = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
|
|
325
|
+
const headers = {};
|
|
326
|
+
if (API_KEY) {
|
|
327
|
+
headers.Authorization = `Bearer ${API_KEY}`;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return new StreamableHTTPClientTransport(new URL(REMOTE_MCP_URL), {
|
|
331
|
+
requestInit: {
|
|
332
|
+
headers,
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async function connectRemoteClient() {
|
|
338
|
+
const { Client } = require('@modelcontextprotocol/sdk/client/index.js');
|
|
339
|
+
const transport = buildRemoteTransport();
|
|
340
|
+
const client = new Client(
|
|
341
|
+
{ name: 'agoragentic-mcp', version: PACKAGE_VERSION },
|
|
342
|
+
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
client.onerror = (error) => {
|
|
346
|
+
if (!error) return;
|
|
347
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
348
|
+
console.error(`[agoragentic-mcp] remote client error: ${message}`);
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
await client.connect(transport);
|
|
352
|
+
return { client, transport };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async function runMcpRelay() {
|
|
356
|
+
const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
|
|
357
|
+
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
358
|
+
const {
|
|
359
|
+
CallToolRequestSchema,
|
|
360
|
+
ListToolsRequestSchema,
|
|
361
|
+
ListResourcesRequestSchema,
|
|
362
|
+
ReadResourceRequestSchema,
|
|
363
|
+
ListPromptsRequestSchema,
|
|
364
|
+
GetPromptRequestSchema,
|
|
365
|
+
} = require('@modelcontextprotocol/sdk/types.js');
|
|
366
|
+
|
|
367
|
+
const server = new Server(
|
|
368
|
+
{ name: 'agoragentic', version: PACKAGE_VERSION },
|
|
369
|
+
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
let remoteSession = null;
|
|
373
|
+
try {
|
|
374
|
+
remoteSession = await connectRemoteClient();
|
|
375
|
+
} catch (error) {
|
|
376
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
377
|
+
console.error(`[agoragentic-mcp] remote relay unavailable, using local fallback tools: ${message}`);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (remoteSession) {
|
|
381
|
+
const { client } = remoteSession;
|
|
382
|
+
let remoteToolNames = null;
|
|
383
|
+
|
|
384
|
+
async function listRemoteTools(params = {}) {
|
|
385
|
+
const result = await client.listTools(params);
|
|
386
|
+
remoteToolNames = new Set((result.tools || []).map((tool) => tool.name));
|
|
387
|
+
return result;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
async function hasRemoteTool(name) {
|
|
391
|
+
if (!remoteToolNames) {
|
|
392
|
+
await listRemoteTools();
|
|
393
|
+
}
|
|
394
|
+
return remoteToolNames.has(name);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
server.setRequestHandler(ListToolsRequestSchema, async (request) => {
|
|
398
|
+
const result = await listRemoteTools(request.params);
|
|
399
|
+
return {
|
|
400
|
+
...result,
|
|
401
|
+
tools: mergeFallbackTools(result.tools),
|
|
402
|
+
};
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
406
|
+
if (FALLBACK_TOOL_NAMES.has(request.params.name) && !(await hasRemoteTool(request.params.name))) {
|
|
407
|
+
return executeFallbackTool(request.params.name, request.params.arguments || {});
|
|
408
|
+
}
|
|
409
|
+
return client.callTool(request.params);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
|
|
413
|
+
return client.listResources(request.params);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
417
|
+
return client.readResource(request.params);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
server.setRequestHandler(ListPromptsRequestSchema, async (request) => {
|
|
421
|
+
return client.listPrompts(request.params);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
425
|
+
return client.getPrompt(request.params);
|
|
426
|
+
});
|
|
427
|
+
} else {
|
|
428
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
429
|
+
return { tools: buildFallbackToolList() };
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
433
|
+
return executeFallbackTool(request.params.name, request.params.arguments || {});
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
437
|
+
return { resources: [] };
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
server.setRequestHandler(ReadResourceRequestSchema, async () => {
|
|
441
|
+
throw new Error('Resources are unavailable while the remote Agoragentic MCP relay is unreachable.');
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
445
|
+
return { prompts: [] };
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
server.setRequestHandler(GetPromptRequestSchema, async () => {
|
|
449
|
+
throw new Error('Prompts are unavailable while the remote Agoragentic MCP relay is unreachable.');
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const stdio = new StdioServerTransport();
|
|
454
|
+
await server.connect(stdio);
|
|
455
|
+
|
|
456
|
+
const shutdown = async (signal) => {
|
|
457
|
+
console.error(`[agoragentic-mcp] shutting down on ${signal}`);
|
|
458
|
+
if (remoteSession) {
|
|
459
|
+
try {
|
|
460
|
+
await remoteSession.transport.terminateSession();
|
|
461
|
+
} catch {
|
|
462
|
+
// Ignore session teardown failures during local shutdown.
|
|
463
|
+
}
|
|
464
|
+
try {
|
|
465
|
+
await remoteSession.transport.close();
|
|
466
|
+
} catch {
|
|
467
|
+
// Ignore transport close failures during local shutdown.
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
process.exit(0);
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
process.on('SIGINT', () => {
|
|
474
|
+
void shutdown('SIGINT');
|
|
475
|
+
});
|
|
476
|
+
process.on('SIGTERM', () => {
|
|
477
|
+
void shutdown('SIGTERM');
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
if (remoteSession) {
|
|
481
|
+
console.error(`[agoragentic-mcp] stdio relay ${PACKAGE_VERSION} connected to ${REMOTE_MCP_URL}`);
|
|
482
|
+
} else {
|
|
483
|
+
console.error(`[agoragentic-mcp] stdio fallback ${PACKAGE_VERSION} using ${AGORAGENTIC_BASE}`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function buildAcpInitializeResult() {
|
|
488
|
+
return {
|
|
489
|
+
protocolVersion: 1,
|
|
490
|
+
agentInfo: {
|
|
491
|
+
name: 'Agoragentic Agent OS',
|
|
492
|
+
version: PACKAGE_VERSION,
|
|
493
|
+
description:
|
|
494
|
+
'Agent OS integrations for deployed agents and swarms: execute-first routing, receipts, x402 edge calls, and Base USDC settlement.',
|
|
495
|
+
homepage: 'https://agoragentic.com',
|
|
496
|
+
},
|
|
497
|
+
agentCapabilities: {
|
|
498
|
+
tools: true,
|
|
499
|
+
streaming: false,
|
|
500
|
+
resources: false,
|
|
501
|
+
prompts: false,
|
|
502
|
+
loadSession: false,
|
|
503
|
+
promptCapabilities: {
|
|
504
|
+
image: false,
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
authMethods: [
|
|
508
|
+
{
|
|
509
|
+
type: 'env',
|
|
510
|
+
name: 'AGORAGENTIC_API_KEY',
|
|
511
|
+
configured: Boolean(API_KEY),
|
|
512
|
+
required: false,
|
|
513
|
+
instructions:
|
|
514
|
+
'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.',
|
|
515
|
+
},
|
|
516
|
+
],
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function buildAcpResponse(id, result) {
|
|
521
|
+
return {
|
|
522
|
+
jsonrpc: '2.0',
|
|
523
|
+
id,
|
|
524
|
+
result,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function buildAcpError(id, code, message, data) {
|
|
529
|
+
return {
|
|
530
|
+
jsonrpc: '2.0',
|
|
531
|
+
id,
|
|
532
|
+
error: data ? { code, message, data } : { code, message },
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function writeAcpMessage(message) {
|
|
537
|
+
process.stdout.write(`${JSON.stringify(message)}\n`);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function buildAcpSessionId() {
|
|
541
|
+
return `sess_${crypto.randomBytes(12).toString('hex')}`;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function extractAcpPromptText(content) {
|
|
545
|
+
if (typeof content === 'string') return content;
|
|
546
|
+
if (!Array.isArray(content)) return '';
|
|
547
|
+
return content
|
|
548
|
+
.map((part) => {
|
|
549
|
+
if (!part || typeof part !== 'object') return '';
|
|
550
|
+
if (part.type === 'text' && typeof part.text === 'string') return part.text;
|
|
551
|
+
return '';
|
|
552
|
+
})
|
|
553
|
+
.filter(Boolean)
|
|
554
|
+
.join('\n');
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function buildAcpPromptReply(promptText) {
|
|
558
|
+
const suffix = promptText ? ` Prompt received: ${promptText.slice(0, 240)}` : '';
|
|
559
|
+
return [
|
|
560
|
+
'Agoragentic ACP adapter is a tool bridge, not a code-editing chat agent.',
|
|
561
|
+
'Use tools/list, then tools/call with agoragentic_execute, agoragentic_match, agoragentic_quote, agoragentic_receipt, or stable x402 service tools.',
|
|
562
|
+
suffix,
|
|
563
|
+
]
|
|
564
|
+
.filter(Boolean)
|
|
565
|
+
.join(' ');
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
async function runAcpAdapter() {
|
|
569
|
+
const rl = readline.createInterface({
|
|
570
|
+
input: process.stdin,
|
|
571
|
+
crlfDelay: Infinity,
|
|
572
|
+
terminal: false,
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
let remoteSession = null;
|
|
576
|
+
const acpSessions = new Map();
|
|
577
|
+
|
|
578
|
+
async function getRemoteSession() {
|
|
579
|
+
if (!remoteSession) {
|
|
580
|
+
remoteSession = await connectRemoteClient();
|
|
581
|
+
}
|
|
582
|
+
return remoteSession;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
async function shutdownRemote() {
|
|
586
|
+
if (!remoteSession) return;
|
|
587
|
+
try {
|
|
588
|
+
await remoteSession.transport.terminateSession();
|
|
589
|
+
} catch {
|
|
590
|
+
// Ignore session teardown failures during local shutdown.
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
await remoteSession.transport.close();
|
|
594
|
+
} catch {
|
|
595
|
+
// Ignore transport close failures during local shutdown.
|
|
596
|
+
}
|
|
597
|
+
remoteSession = null;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
process.on('SIGINT', () => {
|
|
601
|
+
void shutdownRemote().finally(() => process.exit(0));
|
|
602
|
+
});
|
|
603
|
+
process.on('SIGTERM', () => {
|
|
604
|
+
void shutdownRemote().finally(() => process.exit(0));
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
console.error(`[agoragentic-mcp] ACP adapter ${PACKAGE_VERSION} ready`);
|
|
608
|
+
|
|
609
|
+
for await (const line of rl) {
|
|
610
|
+
if (!line.trim()) continue;
|
|
611
|
+
|
|
612
|
+
let request;
|
|
613
|
+
try {
|
|
614
|
+
request = JSON.parse(line);
|
|
615
|
+
} catch (error) {
|
|
616
|
+
writeAcpMessage(buildAcpError(null, -32700, 'Invalid JSON-RPC payload'));
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const hasId = Object.prototype.hasOwnProperty.call(request, 'id');
|
|
621
|
+
const id = hasId ? request.id : null;
|
|
622
|
+
|
|
623
|
+
function writeResponse(message) {
|
|
624
|
+
if (hasId) writeAcpMessage(message);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
try {
|
|
628
|
+
if (request.method === 'initialize') {
|
|
629
|
+
writeResponse(buildAcpResponse(id, buildAcpInitializeResult()));
|
|
630
|
+
} else if (request.method === 'session/new') {
|
|
631
|
+
const sessionId = buildAcpSessionId();
|
|
632
|
+
acpSessions.set(sessionId, {
|
|
633
|
+
cwd: request.params?.cwd || process.cwd(),
|
|
634
|
+
createdAt: new Date().toISOString(),
|
|
635
|
+
cancelled: false,
|
|
636
|
+
});
|
|
637
|
+
writeResponse(buildAcpResponse(id, { sessionId }));
|
|
638
|
+
} else if (request.method === 'session/prompt') {
|
|
639
|
+
const sessionId = request.params?.sessionId;
|
|
640
|
+
if (!sessionId || !acpSessions.has(sessionId)) {
|
|
641
|
+
writeResponse(buildAcpError(id, -32602, 'Unknown or missing ACP sessionId'));
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const session = acpSessions.get(sessionId);
|
|
646
|
+
session.cancelled = false;
|
|
647
|
+
const promptText = extractAcpPromptText(request.params?.content);
|
|
648
|
+
const reply = buildAcpPromptReply(promptText);
|
|
649
|
+
|
|
650
|
+
writeAcpMessage({
|
|
651
|
+
jsonrpc: '2.0',
|
|
652
|
+
method: 'session/update',
|
|
653
|
+
params: {
|
|
654
|
+
sessionId,
|
|
655
|
+
update: {
|
|
656
|
+
sessionUpdate: 'agent_message_chunk',
|
|
657
|
+
content: {
|
|
658
|
+
type: 'text',
|
|
659
|
+
text: reply,
|
|
660
|
+
},
|
|
661
|
+
},
|
|
662
|
+
},
|
|
663
|
+
});
|
|
664
|
+
writeResponse(buildAcpResponse(id, { stopReason: session.cancelled ? 'cancelled' : 'end_turn' }));
|
|
665
|
+
} else if (request.method === 'session/cancel') {
|
|
666
|
+
const sessionId = request.params?.sessionId;
|
|
667
|
+
if (sessionId && acpSessions.has(sessionId)) {
|
|
668
|
+
acpSessions.get(sessionId).cancelled = true;
|
|
669
|
+
}
|
|
670
|
+
writeResponse(buildAcpResponse(id, { ok: true }));
|
|
671
|
+
} else if (request.method === 'tools/list') {
|
|
672
|
+
writeResponse(buildAcpResponse(id, { tools: ACP_TOOLS }));
|
|
673
|
+
} else if (request.method === 'tools/call') {
|
|
674
|
+
const { client } = await getRemoteSession();
|
|
675
|
+
const result = await client.callTool(request.params || {});
|
|
676
|
+
writeResponse(buildAcpResponse(id, result));
|
|
677
|
+
} else if (request.method === 'shutdown') {
|
|
678
|
+
await shutdownRemote();
|
|
679
|
+
writeResponse(buildAcpResponse(id, { ok: true }));
|
|
680
|
+
} else {
|
|
681
|
+
writeResponse(
|
|
682
|
+
buildAcpError(id, -32601, 'Unsupported ACP method', {
|
|
683
|
+
supported_methods: [
|
|
684
|
+
'initialize',
|
|
685
|
+
'session/new',
|
|
686
|
+
'session/prompt',
|
|
687
|
+
'session/cancel',
|
|
688
|
+
'tools/list',
|
|
689
|
+
'tools/call',
|
|
690
|
+
'shutdown',
|
|
691
|
+
],
|
|
692
|
+
})
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
} catch (error) {
|
|
696
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
697
|
+
writeResponse(buildAcpError(id, -32000, message));
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
await shutdownRemote();
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const entrypoint = ACP_MODE ? runAcpAdapter : runMcpRelay;
|
|
705
|
+
|
|
706
|
+
entrypoint().catch((error) => {
|
|
707
|
+
const message = error instanceof Error ? error.stack || error.message : String(error);
|
|
708
|
+
console.error(`[agoragentic-mcp] fatal: ${message}`);
|
|
709
|
+
process.exit(1);
|
|
710
|
+
});
|