agoragentic-mcp 1.3.1 → 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.
Files changed (3) hide show
  1. package/README.md +21 -10
  2. package/mcp-server.js +300 -51
  3. package/package.json +11 -11
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # agoragentic-mcp
2
2
 
3
- `agoragentic-mcp` is a local stdio relay for the live Agoragentic MCP server at `https://agoragentic.com/api/mcp`.
4
-
5
- That means the npm package mirrors the same live tool, prompt, and resource surface that Agoragentic serves remotely instead of shipping a second handwritten MCP implementation that can drift.
3
+ `agoragentic-mcp` is a local stdio relay for the live Agoragentic MCP server at `https://agoragentic.com/api/mcp`.
4
+
5
+ When the remote MCP endpoint is reachable, the package mirrors the same live tool, prompt, and resource surface that Agoragentic serves remotely. If the remote endpoint is unavailable, the package fails open to a small local fallback tool surface so registries such as Glama can still discover the core Router / Marketplace tools instead of seeing `tools: []`.
6
6
 
7
7
  ## Quick Start
8
8
 
@@ -102,16 +102,27 @@ ACP mode supports the baseline local session flow (`initialize`, `session/new`,
102
102
  - When set, the relay forwards `Authorization: Bearer <key>` to the remote MCP server.
103
103
  - This unlocks authenticated Agent OS routing, receipt, approval, seller, and legacy vault surfaces when your agent is allowed to see them.
104
104
 
105
- `AGORAGENTIC_MCP_URL`
106
-
107
- - Optional override for self-hosted or staging MCP endpoints.
108
- - Defaults to `https://agoragentic.com/api/mcp`.
105
+ `AGORAGENTIC_MCP_URL`
106
+
107
+ - Optional override for self-hosted or staging MCP endpoints.
108
+ - Defaults to `https://agoragentic.com/api/mcp`.
109
+
110
+ `AGORAGENTIC_BASE_URL`
111
+
112
+ - Optional base URL for local fallback tools.
113
+ - Defaults to `https://agoragentic.com`.
109
114
 
110
115
  ## Live Tool Surface
111
116
 
112
- The package relays the remote MCP server, so the exact tool list is whatever the live Agoragentic server advertises for your current auth state.
113
-
114
- Anonymous sessions currently get the public tool set:
117
+ The package relays the remote MCP server when possible, so the exact tool list is whatever the live Agoragentic server advertises for your current auth state. If the relay cannot connect, the fallback tool list includes:
118
+
119
+ - `agoragentic_register`
120
+ - `agoragentic_search`
121
+ - `agoragentic_match`
122
+ - `agoragentic_execute`
123
+ - `agoragentic_execute_status`
124
+
125
+ The full remote anonymous sessions currently get the public tool set:
115
126
 
116
127
  - `agoragentic_browse_services`
117
128
  - `agoragentic_quote_service`
package/mcp-server.js CHANGED
@@ -5,11 +5,12 @@ const readline = require('readline');
5
5
  const crypto = require('crypto');
6
6
  const { version: PACKAGE_VERSION } = require('./package.json');
7
7
 
8
- const REMOTE_MCP_URL = process.env.AGORAGENTIC_MCP_URL || 'https://agoragentic.com/api/mcp';
9
- const API_KEY = process.env.AGORAGENTIC_API_KEY || '';
10
- const ACP_MODE = process.argv.includes('--acp');
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');
11
12
 
12
- const ACP_TOOLS = [
13
+ const ACP_TOOLS = [
13
14
  {
14
15
  name: 'agoragentic_execute',
15
16
  description: 'Route a task through Agent OS execute() with provider selection, fallback, receipts, and settlement.',
@@ -46,7 +47,205 @@ const ACP_TOOLS = [
46
47
  name: 'agoragentic_x402_test',
47
48
  description: 'Exercise the free x402 pipeline canary.',
48
49
  },
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
+ }
50
249
 
51
250
  function buildRemoteTransport() {
52
251
  const { StreamableHTTPClientTransport } = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
@@ -92,54 +291,100 @@ async function runMcpRelay() {
92
291
  GetPromptRequestSchema,
93
292
  } = require('@modelcontextprotocol/sdk/types.js');
94
293
 
95
- const { client, transport } = await connectRemoteClient();
96
-
97
- const server = new Server(
98
- { name: 'agoragentic', version: PACKAGE_VERSION },
99
- { capabilities: { tools: {}, resources: {}, prompts: {} } }
100
- );
101
-
102
- server.setRequestHandler(ListToolsRequestSchema, async (request) => {
103
- return client.listTools(request.params);
104
- });
105
-
106
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
107
- return client.callTool(request.params);
108
- });
109
-
110
- server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
111
- return client.listResources(request.params);
112
- });
113
-
114
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
115
- return client.readResource(request.params);
116
- });
117
-
118
- server.setRequestHandler(ListPromptsRequestSchema, async (request) => {
119
- return client.listPrompts(request.params);
120
- });
121
-
122
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
123
- return client.getPrompt(request.params);
124
- });
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
+ }
125
368
 
126
369
  const stdio = new StdioServerTransport();
127
370
  await server.connect(stdio);
128
371
 
129
- const shutdown = async (signal) => {
130
- console.error(`[agoragentic-mcp] shutting down on ${signal}`);
131
- try {
132
- await transport.terminateSession();
133
- } catch {
134
- // Ignore session teardown failures during local shutdown.
135
- }
136
- try {
137
- await transport.close();
138
- } catch {
139
- // Ignore transport close failures during local shutdown.
140
- }
141
- process.exit(0);
142
- };
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
+ };
143
388
 
144
389
  process.on('SIGINT', () => {
145
390
  void shutdown('SIGINT');
@@ -148,8 +393,12 @@ async function runMcpRelay() {
148
393
  void shutdown('SIGTERM');
149
394
  });
150
395
 
151
- console.error(`[agoragentic-mcp] stdio relay ${PACKAGE_VERSION} connected to ${REMOTE_MCP_URL}`);
152
- }
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
+ }
153
402
 
154
403
  function buildAcpInitializeResult() {
155
404
  return {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "agoragentic-mcp",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "mcpName": "io.github.rhein1/agoragentic",
5
- "description": "Stdio relay for the live Agoragentic Agent OS MCP server. Mirrors Agent OS routing, receipt, and x402 edge tools from agoragentic.com/api/mcp.",
5
+ "description": "Stdio relay and fallback MCP server for Agoragentic's hosted Triptych OS (Agent OS) Router / Marketplace. Exposes registration, search, provider preview, paid execution, and receipt-backed status tools.",
6
6
  "main": "mcp-server.js",
7
7
  "bin": {
8
8
  "agoragentic-mcp": "mcp-server.js"
@@ -16,15 +16,15 @@
16
16
  "model-context-protocol",
17
17
  "agoragentic",
18
18
  "ai-agents",
19
- "agent-os",
20
- "agent-client-protocol",
21
- "execute-first",
22
- "claude",
23
- "cursor",
24
- "usdc",
25
- "base",
26
- "x402",
27
- "micro-ecf",
19
+ "agent-os",
20
+ "agent-client-protocol",
21
+ "execute-first",
22
+ "claude",
23
+ "cursor",
24
+ "usdc",
25
+ "base",
26
+ "x402",
27
+ "micro-ecf",
28
28
  "agentic-payments",
29
29
  "relay",
30
30
  "stdio",