agoragentic-mcp 1.3.2 → 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 +161 -159
- package/mcp-server.js +439 -355
- package/package.json +55 -55
- package/scripts/postinstall.js +22 -22
package/mcp-server.js
CHANGED
|
@@ -1,52 +1,60 @@
|
|
|
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
|
-
|
|
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
8
|
const REMOTE_MCP_URL = process.env.AGORAGENTIC_MCP_URL || 'https://agoragentic.com/api/mcp';
|
|
9
9
|
const AGORAGENTIC_BASE = process.env.AGORAGENTIC_BASE_URL || 'https://agoragentic.com';
|
|
10
|
-
const
|
|
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();
|
|
11
19
|
const ACP_MODE = process.argv.includes('--acp');
|
|
12
|
-
|
|
20
|
+
|
|
13
21
|
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
|
-
},
|
|
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
|
+
},
|
|
50
58
|
];
|
|
51
59
|
|
|
52
60
|
function buildJsonContent(data) {
|
|
@@ -108,65 +116,128 @@ function buildFallbackToolList() {
|
|
|
108
116
|
return [
|
|
109
117
|
{
|
|
110
118
|
name: 'agoragentic_register',
|
|
111
|
-
description:
|
|
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.',
|
|
112
127
|
inputSchema: {
|
|
113
128
|
type: 'object',
|
|
114
129
|
properties: {
|
|
115
|
-
name: { type: 'string', description: '
|
|
116
|
-
agent_name: { type: 'string', description: '
|
|
117
|
-
intent: {
|
|
118
|
-
|
|
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"' },
|
|
119
139
|
},
|
|
120
140
|
},
|
|
121
141
|
},
|
|
122
142
|
{
|
|
123
143
|
name: 'agoragentic_search',
|
|
124
|
-
description:
|
|
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.',
|
|
125
151
|
inputSchema: {
|
|
126
152
|
type: 'object',
|
|
127
153
|
properties: {
|
|
128
|
-
query: { type: 'string', description: '
|
|
129
|
-
category: { type: 'string', description: '
|
|
130
|
-
limit: { type: 'number', description: 'Maximum results to return', default: 10 },
|
|
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 },
|
|
131
157
|
},
|
|
132
158
|
},
|
|
133
159
|
},
|
|
134
160
|
{
|
|
135
161
|
name: 'agoragentic_match',
|
|
136
|
-
description:
|
|
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.',
|
|
137
168
|
inputSchema: {
|
|
138
169
|
type: 'object',
|
|
139
170
|
properties: {
|
|
140
|
-
task: { type: 'string', description: '
|
|
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: '
|
|
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 },
|
|
144
175
|
},
|
|
145
176
|
required: ['task'],
|
|
146
177
|
},
|
|
147
178
|
},
|
|
148
179
|
{
|
|
149
180
|
name: 'agoragentic_execute',
|
|
150
|
-
description:
|
|
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.',
|
|
151
191
|
inputSchema: {
|
|
152
192
|
type: 'object',
|
|
153
193
|
properties: {
|
|
154
|
-
task: {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
+
},
|
|
159
221
|
},
|
|
160
222
|
required: ['task'],
|
|
161
223
|
},
|
|
162
224
|
},
|
|
163
225
|
{
|
|
164
226
|
name: 'agoragentic_execute_status',
|
|
165
|
-
description:
|
|
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.',
|
|
166
234
|
inputSchema: {
|
|
167
235
|
type: 'object',
|
|
168
236
|
properties: {
|
|
169
|
-
invocation_id: {
|
|
237
|
+
invocation_id: {
|
|
238
|
+
type: 'string',
|
|
239
|
+
description: 'The invocation_id string returned by a prior agoragentic_execute call, e.g. "inv_abc123def456"',
|
|
240
|
+
},
|
|
170
241
|
},
|
|
171
242
|
required: ['invocation_id'],
|
|
172
243
|
},
|
|
@@ -185,6 +256,8 @@ function mergeFallbackTools(remoteTools = []) {
|
|
|
185
256
|
return merged;
|
|
186
257
|
}
|
|
187
258
|
|
|
259
|
+
const FALLBACK_TOOL_NAMES = new Set(buildFallbackToolList().map((tool) => tool.name));
|
|
260
|
+
|
|
188
261
|
async function executeFallbackTool(name, args = {}) {
|
|
189
262
|
if (name === 'agoragentic_register') {
|
|
190
263
|
const agentName = args.agent_name || args.name || 'mcp-agent';
|
|
@@ -246,51 +319,51 @@ async function executeFallbackTool(name, args = {}) {
|
|
|
246
319
|
tool: name,
|
|
247
320
|
});
|
|
248
321
|
}
|
|
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}`;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return new StreamableHTTPClientTransport(new URL(REMOTE_MCP_URL), {
|
|
258
|
-
requestInit: {
|
|
259
|
-
headers,
|
|
260
|
-
},
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
|
|
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 };
|
|
280
|
-
}
|
|
281
|
-
|
|
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
|
-
|
|
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
|
+
|
|
294
367
|
const server = new Server(
|
|
295
368
|
{ name: 'agoragentic', version: PACKAGE_VERSION },
|
|
296
369
|
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
|
@@ -306,8 +379,23 @@ async function runMcpRelay() {
|
|
|
306
379
|
|
|
307
380
|
if (remoteSession) {
|
|
308
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
|
+
|
|
309
397
|
server.setRequestHandler(ListToolsRequestSchema, async (request) => {
|
|
310
|
-
const result = await
|
|
398
|
+
const result = await listRemoteTools(request.params);
|
|
311
399
|
return {
|
|
312
400
|
...result,
|
|
313
401
|
tools: mergeFallbackTools(result.tools),
|
|
@@ -315,11 +403,7 @@ async function runMcpRelay() {
|
|
|
315
403
|
});
|
|
316
404
|
|
|
317
405
|
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
|
-
) {
|
|
406
|
+
if (FALLBACK_TOOL_NAMES.has(request.params.name) && !(await hasRemoteTool(request.params.name))) {
|
|
323
407
|
return executeFallbackTool(request.params.name, request.params.arguments || {});
|
|
324
408
|
}
|
|
325
409
|
return client.callTool(request.params);
|
|
@@ -365,10 +449,10 @@ async function runMcpRelay() {
|
|
|
365
449
|
throw new Error('Prompts are unavailable while the remote Agoragentic MCP relay is unreachable.');
|
|
366
450
|
});
|
|
367
451
|
}
|
|
368
|
-
|
|
369
|
-
const stdio = new StdioServerTransport();
|
|
370
|
-
await server.connect(stdio);
|
|
371
|
-
|
|
452
|
+
|
|
453
|
+
const stdio = new StdioServerTransport();
|
|
454
|
+
await server.connect(stdio);
|
|
455
|
+
|
|
372
456
|
const shutdown = async (signal) => {
|
|
373
457
|
console.error(`[agoragentic-mcp] shutting down on ${signal}`);
|
|
374
458
|
if (remoteSession) {
|
|
@@ -385,242 +469,242 @@ async function runMcpRelay() {
|
|
|
385
469
|
}
|
|
386
470
|
process.exit(0);
|
|
387
471
|
};
|
|
388
|
-
|
|
389
|
-
process.on('SIGINT', () => {
|
|
390
|
-
void shutdown('SIGINT');
|
|
391
|
-
});
|
|
392
|
-
process.on('SIGTERM', () => {
|
|
393
|
-
void shutdown('SIGTERM');
|
|
394
|
-
});
|
|
395
|
-
|
|
472
|
+
|
|
473
|
+
process.on('SIGINT', () => {
|
|
474
|
+
void shutdown('SIGINT');
|
|
475
|
+
});
|
|
476
|
+
process.on('SIGTERM', () => {
|
|
477
|
+
void shutdown('SIGTERM');
|
|
478
|
+
});
|
|
479
|
+
|
|
396
480
|
if (remoteSession) {
|
|
397
481
|
console.error(`[agoragentic-mcp] stdio relay ${PACKAGE_VERSION} connected to ${REMOTE_MCP_URL}`);
|
|
398
482
|
} else {
|
|
399
483
|
console.error(`[agoragentic-mcp] stdio fallback ${PACKAGE_VERSION} using ${AGORAGENTIC_BASE}`);
|
|
400
484
|
}
|
|
401
485
|
}
|
|
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',
|
|
412
|
-
},
|
|
413
|
-
agentCapabilities: {
|
|
414
|
-
tools: true,
|
|
415
|
-
streaming: false,
|
|
416
|
-
resources: false,
|
|
417
|
-
prompts: false,
|
|
418
|
-
loadSession: false,
|
|
419
|
-
promptCapabilities: {
|
|
420
|
-
image: false,
|
|
421
|
-
},
|
|
422
|
-
},
|
|
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
|
-
}
|
|
435
|
-
|
|
436
|
-
function buildAcpResponse(id, result) {
|
|
437
|
-
return {
|
|
438
|
-
jsonrpc: '2.0',
|
|
439
|
-
id,
|
|
440
|
-
result,
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
|
|
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
|
-
}
|
|
451
|
-
|
|
452
|
-
function writeAcpMessage(message) {
|
|
453
|
-
process.stdout.write(`${JSON.stringify(message)}\n`);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
function buildAcpSessionId() {
|
|
457
|
-
return `sess_${crypto.randomBytes(12).toString('hex')}`;
|
|
458
|
-
}
|
|
459
|
-
|
|
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
|
-
}
|
|
472
|
-
|
|
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
|
-
}
|
|
483
|
-
|
|
484
|
-
async function runAcpAdapter() {
|
|
485
|
-
const rl = readline.createInterface({
|
|
486
|
-
input: process.stdin,
|
|
487
|
-
crlfDelay: Infinity,
|
|
488
|
-
terminal: false,
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
let remoteSession = null;
|
|
492
|
-
const acpSessions = new Map();
|
|
493
|
-
|
|
494
|
-
async function getRemoteSession() {
|
|
495
|
-
if (!remoteSession) {
|
|
496
|
-
remoteSession = await connectRemoteClient();
|
|
497
|
-
}
|
|
498
|
-
return remoteSession;
|
|
499
|
-
}
|
|
500
|
-
|
|
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;
|
|
514
|
-
}
|
|
515
|
-
|
|
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;
|
|
527
|
-
|
|
528
|
-
let request;
|
|
529
|
-
try {
|
|
530
|
-
request = JSON.parse(line);
|
|
531
|
-
} catch (error) {
|
|
532
|
-
writeAcpMessage(buildAcpError(null, -32700, 'Invalid JSON-RPC payload'));
|
|
533
|
-
continue;
|
|
534
|
-
}
|
|
535
|
-
|
|
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);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
try {
|
|
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;
|
|
559
|
-
}
|
|
560
|
-
|
|
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 }));
|
|
596
|
-
} else {
|
|
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
|
-
);
|
|
610
|
-
}
|
|
611
|
-
} catch (error) {
|
|
612
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
613
|
-
writeResponse(buildAcpError(id, -32000, message));
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
await shutdownRemote();
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
const entrypoint = ACP_MODE ? runAcpAdapter : runMcpRelay;
|
|
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
|
-
});
|
|
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
|
+
});
|