agoragentic-mcp 1.2.0 → 1.2.1

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/mcp-server.js CHANGED
@@ -1,35 +1,10 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Agoragentic MCP Server — v2.0
4
- * ==============================
5
- *
6
- * Model Context Protocol server that exposes the full Agoragentic marketplace
7
- * to any MCP-compatible client (Claude Desktop, VS Code, Cursor, etc.)
8
- *
9
- * v2.0 adds:
10
- * - Vault Memory (persistent KV store)
11
- * - Vault Secrets (encrypted credential storage)
12
- * - Agent Passport (NFT identity)
13
- * - x402 payment info
14
- *
15
- * Setup:
16
- * npm install @modelcontextprotocol/sdk
17
- * node mcp-server.js
18
- *
19
- * Configure in Claude Desktop (claude_desktop_config.json):
20
- * {
21
- * "mcpServers": {
22
- * "agoragentic": {
23
- * "command": "node",
24
- * "args": ["/path/to/mcp-server.js"],
25
- * "env": { "AGORAGENTIC_API_KEY": "amk_your_key" }
26
- * }
27
- * }
28
- * }
29
- */
30
-
31
- const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
32
- const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
5
+ const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
6
+ const { Client } = require('@modelcontextprotocol/sdk/client/index.js');
7
+ const { StreamableHTTPClientTransport } = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
33
8
  const {
34
9
  CallToolRequestSchema,
35
10
  ListToolsRequestSchema,
@@ -37,980 +12,104 @@ const {
37
12
  ReadResourceRequestSchema,
38
13
  ListPromptsRequestSchema,
39
14
  GetPromptRequestSchema,
40
- McpError,
41
- } = require("@modelcontextprotocol/sdk/types.js");
42
-
43
- const AGORAGENTIC_BASE = "https://agoragentic.com";
44
- const X402_EDGE_BASE = "https://x402.agoragentic.com";
45
- const API_KEY = process.env.AGORAGENTIC_API_KEY || "";
46
-
47
- // ─── HTTP helper ─────────────────────────────────────────
48
-
49
- async function apiCall(method, path, body = null) {
50
- const url = `${AGORAGENTIC_BASE}${path}`;
51
- const headers = { "Content-Type": "application/json" };
52
- if (API_KEY) headers["Authorization"] = `Bearer ${API_KEY}`;
53
-
54
- const controller = new AbortController();
55
- const timeout = setTimeout(() => controller.abort(), 30000);
56
-
57
- try {
58
- const options = { method, headers, signal: controller.signal };
59
- if (body) options.body = JSON.stringify(body);
60
-
61
- const resp = await fetch(url, options);
62
- return resp.json();
63
- } finally {
64
- clearTimeout(timeout);
65
- }
66
- }
67
-
68
- async function edgeRequest(method, path, body = null, extraHeaders = {}) {
69
- const url = `${X402_EDGE_BASE}${path}`;
70
- const headers = { ...extraHeaders };
71
-
72
- const controller = new AbortController();
73
- const timeout = setTimeout(() => controller.abort(), 30000);
74
-
75
- try {
76
- const options = { method, headers, signal: controller.signal };
77
- if (body !== null && body !== undefined) {
78
- options.headers["Content-Type"] = "application/json";
79
- options.body = JSON.stringify(body);
80
- }
81
-
82
- const resp = await fetch(url, options);
83
- const raw = await resp.text();
84
- return { response: resp, data: parseMaybeJson(raw) };
85
- } finally {
86
- clearTimeout(timeout);
87
- }
88
- }
89
-
90
- function parseMaybeJson(text) {
91
- if (!text) return null;
92
- try {
93
- return JSON.parse(text);
94
- } catch {
95
- return text;
96
- }
97
- }
98
-
99
- function slugifyService(value) {
100
- return String(value || "")
101
- .trim()
102
- .toLowerCase()
103
- .replace(/[^a-z0-9]+/g, "-")
104
- .replace(/^-+|-+$/g, "");
105
- }
15
+ } = require('@modelcontextprotocol/sdk/types.js');
16
+ const { version: PACKAGE_VERSION } = require('./package.json');
106
17
 
107
- function findEdgeService(services, slug) {
108
- const wanted = slugifyService(slug);
109
- if (!wanted) return null;
110
- return (services || []).find((service) => {
111
- const aliases = [service?.slug, ...(service?.route_aliases || [])]
112
- .map(slugifyService)
113
- .filter(Boolean);
114
- return aliases.includes(wanted);
115
- }) || null;
116
- }
18
+ const REMOTE_MCP_URL = process.env.AGORAGENTIC_MCP_URL || 'https://agoragentic.com/api/mcp';
19
+ const API_KEY = process.env.AGORAGENTIC_API_KEY || '';
117
20
 
118
- function decodeBase64JsonHeader(value) {
119
- if (!value || typeof value !== "string") return null;
120
- try {
121
- return JSON.parse(Buffer.from(value, "base64").toString("utf8"));
122
- } catch {
123
- return null;
21
+ function buildRemoteTransport() {
22
+ const headers = {};
23
+ if (API_KEY) {
24
+ headers.Authorization = `Bearer ${API_KEY}`;
124
25
  }
125
- }
126
26
 
127
- function buildMcpPaymentRequiredData({ toolName, args, response, routing = null, trust = null }) {
128
- const paymentRequiredHeader = response.headers.get("payment-required") || null;
129
- const decodedRequirements = decodeBase64JsonHeader(paymentRequiredHeader);
130
- const challenges = Array.isArray(decodedRequirements)
131
- ? decodedRequirements.map((challenge, index) => ({
132
- id: challenge?.extra?.quote_id || `challenge_${index + 1}`,
133
- method: challenge?.scheme || "exact",
134
- intent: "charge",
135
- network: challenge?.network || null,
136
- resource: challenge?.resource || null,
137
- request: {
138
- max_amount_required: challenge?.maxAmountRequired || null,
139
- asset: challenge?.asset || null,
140
- pay_to: challenge?.payTo || null,
141
- description: challenge?.description || null,
142
- extra: challenge?.extra || null,
143
- },
144
- }))
145
- : [];
146
-
147
- return {
148
- protocol: "x402",
149
- payment_required_header: paymentRequiredHeader,
150
- authenticate_header: response.headers.get("www-authenticate") || null,
151
- challenges,
152
- routing,
153
- trust,
154
- retry_tool_call: {
155
- name: toolName,
156
- arguments: {
157
- ...(args || {}),
158
- payment_signature: "<PAYMENT-SIGNATURE>",
159
- },
160
- payment_argument: "payment_signature",
161
- accepted_http_headers: ["PAYMENT-SIGNATURE", "Authorization: Payment"],
27
+ return new StreamableHTTPClientTransport(new URL(REMOTE_MCP_URL), {
28
+ requestInit: {
29
+ headers,
162
30
  },
163
- };
31
+ });
164
32
  }
165
33
 
166
- function buildMcpPaymentSuccessBody(response, body) {
167
- return {
168
- status_code: response.status,
169
- payment_receipt: response.headers.get("payment-receipt") || null,
170
- payment_response_header: response.headers.get("payment-response") || null,
171
- body,
34
+ async function connectRemoteClient() {
35
+ const transport = buildRemoteTransport();
36
+ const client = new Client(
37
+ { name: 'agoragentic-mcp', version: PACKAGE_VERSION },
38
+ { capabilities: { tools: {}, resources: {}, prompts: {} } }
39
+ );
40
+
41
+ client.onerror = (error) => {
42
+ if (!error) return;
43
+ const message = error instanceof Error ? error.message : String(error);
44
+ console.error(`[agoragentic-mcp] remote client error: ${message}`);
172
45
  };
173
- }
174
46
 
175
- function summarizeEdgeService(service, options = {}) {
176
- const includeSchemas = options.includeSchemas === true;
177
- const includeTrust = options.includeTrust !== false;
178
- return {
179
- slug: service.slug,
180
- name: service.name,
181
- description: service.description,
182
- category: service.category,
183
- status: service.status,
184
- price_usdc: service.price_usdc,
185
- payable_url: service.payable_url,
186
- route_aliases: service.route_aliases || [],
187
- safe_to_retry: service.execution_contract?.safe_to_retry ?? service.trust?.safe_to_retry ?? null,
188
- max_runtime_ms: service.execution_contract?.max_runtime_ms ?? service.trust?.max_runtime_ms ?? null,
189
- payment_contract: service.payment_contract || null,
190
- sample_input: service.sample_input || null,
191
- trust: includeTrust ? (service.trust || null) : undefined,
192
- input_schema: includeSchemas ? (service.input_schema || null) : undefined,
193
- output_schema: includeSchemas ? (service.output_schema || null) : undefined,
194
- };
47
+ await client.connect(transport);
48
+ return { client, transport };
195
49
  }
196
50
 
197
- async function loadEdgeServices() {
198
- const { response, data } = await edgeRequest("GET", "/services/index.json");
199
- if (!response.ok) {
200
- throw new Error(`x402 edge service index failed with HTTP ${response.status}`);
201
- }
202
- return Array.isArray(data?.services) ? data.services : [];
203
- }
204
-
205
- // ─── MCP Server ──────────────────────────────────────────
206
-
207
- const server = new Server(
208
- { name: "agoragentic", version: "2.0.0" },
209
- { capabilities: { tools: {}, resources: {}, prompts: {} } }
210
- );
211
-
212
- // ─── Tools ───────────────────────────────────────────────
213
-
214
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
215
- tools: [
216
- // ── Core Marketplace ──
217
- {
218
- name: "agoragentic_browse_services",
219
- description: "Browse stable anonymous x402 services on x402.agoragentic.com. Use this as the accountless buyer catalog for bounded paid resources.",
220
- annotations: { title: "Browse x402 Services", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
221
- inputSchema: {
222
- type: "object",
223
- properties: {
224
- limit: { type: "number", default: 10, description: "Maximum number of services to return." },
225
- include_schemas: { type: "boolean", default: false, description: "Include full input/output schemas in the response." },
226
- include_trust: { type: "boolean", default: true, description: "Include trust and settlement metadata in the response." }
227
- }
228
- }
229
- },
230
- {
231
- name: "agoragentic_quote_service",
232
- description: "Quote one stable x402 service by slug. Returns price, retry behavior, trust metadata, sample input, and the exact payable URL without spending.",
233
- annotations: { title: "Quote x402 Service", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
234
- inputSchema: {
235
- type: "object",
236
- properties: {
237
- slug: { type: "string", description: "Stable x402 service slug, for example text-summarizer." },
238
- max_price_usdc: { type: "number", description: "Optional safety bound. The tool errors if the quoted service exceeds this price." },
239
- include_schemas: { type: "boolean", default: true, description: "Include full input/output schemas in the response." },
240
- include_trust: { type: "boolean", default: true, description: "Include trust and settlement metadata in the response." }
241
- },
242
- required: ["slug"]
243
- }
244
- },
245
- {
246
- name: "agoragentic_call_service",
247
- description: "Call one stable x402 service by slug. The first unpaid attempt returns an x402 Payment Required payload. Retry the same tool call with payment_signature to complete the paid call.",
248
- annotations: { title: "Call x402 Service", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
249
- inputSchema: {
250
- type: "object",
251
- properties: {
252
- slug: { type: "string", description: "Stable x402 service slug, for example text-summarizer." },
253
- payload: { type: "object", description: "JSON payload sent to the stable edge route.", default: {} },
254
- payment_signature: { type: "string", description: "Optional PAYMENT-SIGNATURE value used on the paid retry." },
255
- max_price_usdc: { type: "number", description: "Optional safety bound. The tool errors if the quoted service exceeds this price." }
256
- },
257
- required: ["slug"]
258
- }
259
- },
260
- {
261
- name: "agoragentic_edge_receipt",
262
- description: "Fetch one anonymous x402 edge receipt by receipt ID from x402.agoragentic.com.",
263
- annotations: { title: "Get x402 Edge Receipt", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
264
- inputSchema: {
265
- type: "object",
266
- properties: {
267
- receipt_id: { type: "string", description: "Stable edge receipt identifier, usually returned in the Payment-Receipt header." }
268
- },
269
- required: ["receipt_id"]
270
- }
271
- },
272
- {
273
- name: "agoragentic_register",
274
- 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.",
275
- annotations: { title: "Register Agent", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
276
- inputSchema: {
277
- type: "object",
278
- properties: {
279
- agent_name: { type: "string", description: "Your agent's display name (must be unique across the marketplace)" },
280
- agent_type: { type: "string", enum: ["buyer", "seller", "both"], default: "both", description: "Agent role: buyer (consume services), seller (provide services), or both" }
281
- },
282
- required: ["agent_name"]
283
- }
284
- },
285
- {
286
- name: "agoragentic_search",
287
- 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.",
288
- annotations: { title: "Search Capabilities", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
289
- inputSchema: {
290
- type: "object",
291
- properties: {
292
- query: { type: "string", description: "Search term to filter capabilities (e.g., 'summarize', 'translate', 'research')" },
293
- category: { type: "string", description: "Category filter (e.g., research, creative, data, agent-upgrades, infrastructure)" },
294
- max_price: { type: "number", description: "Maximum price in USDC to filter results by cost" },
295
- limit: { type: "number", default: 10, description: "Maximum number of results to return (1 to 50)" }
296
- }
297
- }
298
- },
299
- {
300
- name: "agoragentic_invoke",
301
- description: "Invoke (call/use) a capability from the Agoragentic marketplace. Payment is automatic from your USDC balance. Returns the capability's output.",
302
- annotations: { title: "Invoke Capability", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
303
- inputSchema: {
304
- type: "object",
305
- properties: {
306
- capability_id: { type: "string", description: "The capability ID returned from a search result" },
307
- input: { type: "object", description: "Input payload for the capability as a JSON object", default: {} }
308
- },
309
- required: ["capability_id"]
310
- }
311
- },
312
- {
313
- name: "agoragentic_vault",
314
- description: "View your agent's vault (inventory). Shows all items you own: skills, datasets, licenses, collectibles, and service results from previous invocations.",
315
- annotations: { title: "View Vault", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
316
- inputSchema: {
317
- type: "object",
318
- properties: {
319
- item_type: { type: "string", description: "Filter by item type: skill, digital_asset, nft, license, subscription, or collectible" },
320
- include_nfts: { type: "boolean", description: "Include on-chain NFTs minted on Base L2 blockchain", default: false },
321
- limit: { type: "number", default: 20, description: "Maximum number of vault items to return" }
322
- }
323
- }
324
- },
325
- {
326
- name: "agoragentic_categories",
327
- description: "List all available marketplace categories and how many capabilities are in each.",
328
- annotations: { title: "List Categories", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
329
- inputSchema: { type: "object", properties: {} }
330
- },
331
-
332
- // ── Vault Memory ──
333
- {
334
- name: "agoragentic_memory_write",
335
- 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.",
336
- annotations: { title: "Write Memory", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
337
- inputSchema: {
338
- type: "object",
339
- properties: {
340
- key: { type: "string", description: "Memory key identifier, maximum 256 characters" },
341
- value: { type: "string", description: "Value to store, maximum 64KB. Can be any string or serialized JSON." },
342
- namespace: { type: "string", default: "default", description: "Namespace to organize keys into logical groups" },
343
- ttl_seconds: { type: "number", description: "Automatic expiration in seconds. Omit for permanent storage." }
344
- },
345
- required: ["key", "value"]
346
- }
347
- },
348
- {
349
- name: "agoragentic_memory_read",
350
- description: "Read from your persistent agent memory. Free, no cost to recall your own data. Returns a single key or lists all keys.",
351
- annotations: { title: "Read Memory", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
352
- inputSchema: {
353
- type: "object",
354
- properties: {
355
- key: { type: "string", description: "Specific key to read. Omit to list all stored keys." },
356
- namespace: { type: "string", default: "default", description: "Namespace to read from" },
357
- prefix: { type: "string", description: "Filter keys by prefix when listing all keys" }
358
- }
359
- }
360
- },
361
-
362
- // ── Vault Secrets ──
363
- {
364
- name: "agoragentic_secret_store",
365
- description: "Store an encrypted secret (API key, token, password) in your vault. AES 256 encrypted at rest. Costs $0.25 via the marketplace.",
366
- annotations: { title: "Store Secret", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
367
- inputSchema: {
368
- type: "object",
369
- properties: {
370
- label: { type: "string", description: "Label for the secret, for example 'openai_key' or 'stripe_token'" },
371
- secret: { type: "string", description: "The secret value to encrypt and store securely" },
372
- hint: { type: "string", description: "Optional human readable hint to help you remember what this secret is for" }
373
- },
374
- required: ["label", "secret"]
375
- }
376
- },
377
- {
378
- name: "agoragentic_secret_retrieve",
379
- description: "Retrieve a decrypted secret from your vault. Free, no cost to access your own credentials.",
380
- annotations: { title: "Retrieve Secret", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
381
- inputSchema: {
382
- type: "object",
383
- properties: {
384
- label: { type: "string", description: "Label of the secret to retrieve. Omit to list all stored secrets." }
385
- }
386
- }
387
- },
388
-
389
- // ── Passport ──
390
- {
391
- name: "agoragentic_passport",
392
- description: "Check your Agoragentic Passport NFT status, or get info about the passport system. Passports are on chain identity NFTs on Base L2.",
393
- annotations: { title: "Agent Passport", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
394
- inputSchema: {
395
- type: "object",
396
- properties: {
397
- action: {
398
- type: "string",
399
- enum: ["check", "info", "verify"],
400
- default: "check",
401
- description: "Action to perform: check your passport status, info for system overview, or verify a wallet address"
402
- },
403
- wallet_address: { type: "string", description: "Wallet address to verify ownership. Only used when action is set to verify." }
404
- }
405
- }
406
- }
407
- ]
408
- }));
409
-
410
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
411
- const { name, arguments: args } = request.params;
412
-
413
- try {
414
- switch (name) {
415
- case "agoragentic_browse_services": {
416
- const services = await loadEdgeServices();
417
- const limit = Math.max(1, Math.min(Number(args.limit) || 10, 50));
418
- const summaries = services
419
- .filter((service) => service && service.status === "available")
420
- .slice(0, limit)
421
- .map((service) => summarizeEdgeService(service, {
422
- includeSchemas: args.include_schemas === true,
423
- includeTrust: args.include_trust !== false,
424
- }));
51
+ async function main() {
52
+ const { client, transport } = await connectRemoteClient();
425
53
 
426
- return {
427
- content: [{
428
- type: "text",
429
- text: JSON.stringify({
430
- mode: "anonymous_x402_edge",
431
- service_count: summaries.length,
432
- base_url: X402_EDGE_BASE,
433
- services: summaries,
434
- }, null, 2)
435
- }]
436
- };
437
- }
54
+ const server = new Server(
55
+ { name: 'agoragentic', version: PACKAGE_VERSION },
56
+ { capabilities: { tools: {}, resources: {}, prompts: {} } }
57
+ );
438
58
 
439
- case "agoragentic_quote_service": {
440
- const services = await loadEdgeServices();
441
- const service = findEdgeService(services, args.slug);
442
- if (!service) {
443
- return {
444
- content: [{
445
- type: "text",
446
- text: JSON.stringify({
447
- error: "unknown_service",
448
- slug: args.slug || null,
449
- message: "Stable x402 service slug not found on the edge."
450
- }, null, 2)
451
- }],
452
- isError: true
453
- };
454
- }
59
+ server.setRequestHandler(ListToolsRequestSchema, async (request) => {
60
+ return client.listTools(request.params);
61
+ });
455
62
 
456
- const quotedPrice = Number.parseFloat(service.price_usdc);
457
- if (Number.isFinite(Number(args.max_price_usdc)) && Number.isFinite(quotedPrice) && quotedPrice > Number(args.max_price_usdc)) {
458
- return {
459
- content: [{
460
- type: "text",
461
- text: JSON.stringify({
462
- error: "price_exceeds_max",
463
- slug: service.slug,
464
- quoted_price_usdc: service.price_usdc,
465
- max_price_usdc: Number(args.max_price_usdc)
466
- }, null, 2)
467
- }],
468
- isError: true
469
- };
470
- }
63
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
64
+ return client.callTool(request.params);
65
+ });
471
66
 
472
- return {
473
- content: [{
474
- type: "text",
475
- text: JSON.stringify({
476
- mode: "anonymous_x402_edge_quote",
477
- ...summarizeEdgeService(service, {
478
- includeSchemas: args.include_schemas !== false,
479
- includeTrust: args.include_trust !== false,
480
- })
481
- }, null, 2)
482
- }]
483
- };
484
- }
67
+ server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
68
+ return client.listResources(request.params);
69
+ });
485
70
 
486
- case "agoragentic_call_service": {
487
- const services = await loadEdgeServices();
488
- const service = findEdgeService(services, args.slug);
489
- if (!service) {
490
- return {
491
- content: [{
492
- type: "text",
493
- text: JSON.stringify({
494
- error: "unknown_service",
495
- slug: args.slug || null,
496
- message: "Stable x402 service slug not found on the edge."
497
- }, null, 2)
498
- }],
499
- isError: true
500
- };
501
- }
71
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
72
+ return client.readResource(request.params);
73
+ });
502
74
 
503
- const quotedPrice = Number.parseFloat(service.price_usdc);
504
- if (Number.isFinite(Number(args.max_price_usdc)) && Number.isFinite(quotedPrice) && quotedPrice > Number(args.max_price_usdc)) {
505
- return {
506
- content: [{
507
- type: "text",
508
- text: JSON.stringify({
509
- error: "price_exceeds_max",
510
- slug: service.slug,
511
- quoted_price_usdc: service.price_usdc,
512
- max_price_usdc: Number(args.max_price_usdc)
513
- }, null, 2)
514
- }],
515
- isError: true
516
- };
517
- }
75
+ server.setRequestHandler(ListPromptsRequestSchema, async (request) => {
76
+ return client.listPrompts(request.params);
77
+ });
518
78
 
519
- const requestHeaders = {};
520
- if (args.payment_signature) {
521
- requestHeaders["PAYMENT-SIGNATURE"] = String(args.payment_signature);
522
- }
79
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
80
+ return client.getPrompt(request.params);
81
+ });
523
82
 
524
- const { response, data } = await edgeRequest(
525
- "POST",
526
- `/v1/${encodeURIComponent(service.slug)}`,
527
- args.payload || {},
528
- requestHeaders
529
- );
83
+ const stdio = new StdioServerTransport();
84
+ await server.connect(stdio);
530
85
 
531
- if (response.status === 402) {
532
- throw new McpError(-32042, "Payment Required", buildMcpPaymentRequiredData({
533
- toolName: "agoragentic_call_service",
534
- args: {
535
- slug: service.slug,
536
- payload: args.payload || {},
537
- max_price_usdc: args.max_price_usdc
538
- },
539
- response,
540
- routing: {
541
- provider_count: 1,
542
- selected_provider: service.slug,
543
- route: `/v1/${service.slug}`,
544
- },
545
- trust: service.trust || {
546
- status: service.status || "reachable",
547
- method: "x402_edge_service_index",
548
- }
549
- }));
550
- }
86
+ const shutdown = async (signal) => {
87
+ console.error(`[agoragentic-mcp] shutting down on ${signal}`);
88
+ try {
89
+ await transport.terminateSession();
90
+ } catch {
91
+ // Ignore session teardown failures during local shutdown.
92
+ }
93
+ try {
94
+ await transport.close();
95
+ } catch {
96
+ // Ignore transport close failures during local shutdown.
97
+ }
98
+ process.exit(0);
99
+ };
551
100
 
552
- return {
553
- content: [{
554
- type: "text",
555
- text: JSON.stringify({
556
- slug: service.slug,
557
- service_name: service.name,
558
- payable_url: service.payable_url,
559
- ...buildMcpPaymentSuccessBody(response, data)
560
- }, null, 2)
561
- }],
562
- isError: response.status >= 400
563
- };
564
- }
101
+ process.on('SIGINT', () => {
102
+ void shutdown('SIGINT');
103
+ });
104
+ process.on('SIGTERM', () => {
105
+ void shutdown('SIGTERM');
106
+ });
565
107
 
566
- case "agoragentic_edge_receipt": {
567
- const { response, data } = await edgeRequest(
568
- "GET",
569
- `/v1/receipts/${encodeURIComponent(String(args.receipt_id || ""))}`
570
- );
571
- return {
572
- content: [{
573
- type: "text",
574
- text: JSON.stringify({
575
- status_code: response.status,
576
- receipt: data
577
- }, null, 2)
578
- }],
579
- isError: response.status >= 400
580
- };
581
- }
108
+ console.error(`[agoragentic-mcp] stdio relay ${PACKAGE_VERSION} connected to ${REMOTE_MCP_URL}`);
109
+ }
582
110
 
583
- case "agoragentic_register": {
584
- const data = await apiCall("POST", "/api/quickstart", {
585
- name: args.agent_name,
586
- type: args.agent_type || "both"
587
- });
588
- return {
589
- content: [{
590
- type: "text",
591
- text: JSON.stringify({
592
- status: "registered",
593
- agent_id: data.agent?.id,
594
- api_key: data.api_key,
595
- fee_rate: "3.00%",
596
- message: "Save your API key! Set it as AGORAGENTIC_API_KEY environment variable.",
597
- next: "Use agoragentic_search to find capabilities, or agoragentic_invoke to call one directly"
598
- }, null, 2)
599
- }]
600
- };
601
- }
602
-
603
- case "agoragentic_search": {
604
- const params = new URLSearchParams({ limit: args.limit || 10, status: "active" });
605
- if (args.query) params.set("search", args.query);
606
- if (args.category) params.set("category", args.category);
607
-
608
- const data = await apiCall("GET", `/api/capabilities?${params}`);
609
- let capabilities = Array.isArray(data) ? data : (data.capabilities || []);
610
-
611
- if (args.max_price !== undefined) {
612
- capabilities = capabilities.filter(c => (c.price_per_unit || 0) <= args.max_price);
613
- }
614
-
615
- const results = capabilities.slice(0, args.limit || 10).map(c => ({
616
- id: c.id,
617
- name: c.name,
618
- description: (c.description || "").substring(0, 200),
619
- category: c.category,
620
- price_usdc: c.price_per_unit,
621
- seller: c.seller_name,
622
- type: c.listing_type
623
- }));
624
-
625
- return {
626
- content: [{
627
- type: "text",
628
- text: JSON.stringify({ total: results.length, capabilities: results }, null, 2)
629
- }]
630
- };
631
- }
632
-
633
- case "agoragentic_invoke": {
634
- if (!API_KEY) {
635
- return { content: [{ type: "text", text: "Error: Set AGORAGENTIC_API_KEY environment variable first. Use agoragentic_register to get one." }] };
636
- }
637
- const capId = String(args.capability_id || "").replace(/[^a-zA-Z0-9\-_]/g, "");
638
- if (!capId) {
639
- return { content: [{ type: "text", text: "Error: Invalid capability_id." }] };
640
- }
641
- const data = await apiCall("POST", `/api/invoke/${capId}`, {
642
- input: args.input || {}
643
- });
644
- return {
645
- content: [{
646
- type: "text",
647
- text: JSON.stringify({
648
- invocation_id: data.invocation_id,
649
- status: data.status,
650
- output: data.response,
651
- cost_usdc: data.cost,
652
- balance_after: data.buyer_balance,
653
- nft: data.nft || null,
654
- vault_item: data.vault || null
655
- }, null, 2)
656
- }]
657
- };
658
- }
659
-
660
- case "agoragentic_vault": {
661
- if (!API_KEY) {
662
- return { content: [{ type: "text", text: "Error: Set AGORAGENTIC_API_KEY environment variable first." }] };
663
- }
664
- const params = new URLSearchParams({ limit: args.limit || 20 });
665
- if (args.item_type) params.set("type", args.item_type);
666
- if (args.include_nfts) params.set("include", "nfts");
667
-
668
- const data = await apiCall("GET", `/api/inventory?${params}`);
669
- const vault = data.vault || {};
670
- return {
671
- content: [{
672
- type: "text",
673
- text: JSON.stringify({
674
- agent: vault.agent_name,
675
- total_items: vault.total_items,
676
- items: (vault.items || []).map(i => ({
677
- name: i.item_name,
678
- type: i.item_type,
679
- status: i.status,
680
- acquired: i.acquired_at,
681
- integrity_warning: i.integrity_warning
682
- })),
683
- nfts: data.nfts || null
684
- }, null, 2)
685
- }]
686
- };
687
- }
688
-
689
- case "agoragentic_categories": {
690
- const data = await apiCall("GET", "/api/categories");
691
- return {
692
- content: [{
693
- type: "text",
694
- text: JSON.stringify(data, null, 2)
695
- }]
696
- };
697
- }
698
-
699
- // ── Vault Memory ──
700
-
701
- case "agoragentic_memory_write": {
702
- if (!API_KEY) {
703
- return { content: [{ type: "text", text: "Error: API key required." }] };
704
- }
705
- // Find the Memory Slots listing and invoke through marketplace
706
- const searchData = await apiCall("GET", "/api/capabilities?search=Vault+Memory+Slots&limit=1");
707
- const listings = Array.isArray(searchData) ? searchData : (searchData.capabilities || []);
708
- const memoryListing = listings.find(l => l.name === 'Vault Memory Slots');
709
-
710
- if (memoryListing) {
711
- const data = await apiCall("POST", `/api/invoke/${memoryListing.id}`, {
712
- input: {
713
- key: args.key,
714
- value: args.value,
715
- namespace: args.namespace || 'default',
716
- ttl_seconds: args.ttl_seconds
717
- }
718
- });
719
- return {
720
- content: [{
721
- type: "text",
722
- text: JSON.stringify({
723
- status: data.status,
724
- output: data.response?.output || data.response,
725
- cost: data.cost,
726
- balance_after: data.buyer_balance
727
- }, null, 2)
728
- }]
729
- };
730
- }
731
-
732
- // Fallback: direct API call
733
- const data = await apiCall("POST", "/api/vault/memory", {
734
- input: {
735
- key: args.key,
736
- value: args.value,
737
- namespace: args.namespace || 'default',
738
- ttl_seconds: args.ttl_seconds
739
- }
740
- });
741
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
742
- }
743
-
744
- case "agoragentic_memory_read": {
745
- if (!API_KEY) {
746
- return { content: [{ type: "text", text: "Error: API key required." }] };
747
- }
748
- const params = new URLSearchParams();
749
- if (args.key) params.set("key", args.key);
750
- if (args.namespace) params.set("namespace", args.namespace);
751
- if (args.prefix) params.set("prefix", args.prefix);
752
-
753
- const data = await apiCall("GET", `/api/vault/memory?${params}`);
754
- return {
755
- content: [{
756
- type: "text",
757
- text: JSON.stringify(data.output || data, null, 2)
758
- }]
759
- };
760
- }
761
-
762
- // ── Vault Secrets ──
763
-
764
- case "agoragentic_secret_store": {
765
- if (!API_KEY) {
766
- return { content: [{ type: "text", text: "Error: API key required." }] };
767
- }
768
- const searchData = await apiCall("GET", "/api/capabilities?search=Vault+Secrets+Locker&limit=1");
769
- const listings = Array.isArray(searchData) ? searchData : (searchData.capabilities || []);
770
- const secretsListing = listings.find(l => l.name === 'Vault Secrets Locker');
771
-
772
- if (secretsListing) {
773
- const data = await apiCall("POST", `/api/invoke/${secretsListing.id}`, {
774
- input: {
775
- label: args.label,
776
- secret: args.secret,
777
- hint: args.hint
778
- }
779
- });
780
- return {
781
- content: [{
782
- type: "text",
783
- text: JSON.stringify({
784
- status: data.status,
785
- output: data.response?.output || data.response,
786
- cost: data.cost,
787
- balance_after: data.buyer_balance
788
- }, null, 2)
789
- }]
790
- };
791
- }
792
-
793
- const data = await apiCall("POST", "/api/vault/secrets", {
794
- input: { label: args.label, secret: args.secret, hint: args.hint }
795
- });
796
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
797
- }
798
-
799
- case "agoragentic_secret_retrieve": {
800
- if (!API_KEY) {
801
- return { content: [{ type: "text", text: "Error: API key required." }] };
802
- }
803
- const params = new URLSearchParams();
804
- if (args.label) params.set("label", args.label);
805
-
806
- const data = await apiCall("GET", `/api/vault/secrets?${params}`);
807
- return {
808
- content: [{
809
- type: "text",
810
- text: JSON.stringify(data.output || data, null, 2)
811
- }]
812
- };
813
- }
814
-
815
- // ── Passport ──
816
-
817
- case "agoragentic_passport": {
818
- const action = args.action || "check";
819
-
820
- if (action === "info") {
821
- const data = await apiCall("GET", "/api/passport/info");
822
- return { content: [{ type: "text", text: JSON.stringify(data.output || data, null, 2) }] };
823
- }
824
-
825
- if (action === "verify" && args.wallet_address) {
826
- const data = await apiCall("GET", `/api/passport/verify/${args.wallet_address}`);
827
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
828
- }
829
-
830
- if (!API_KEY) {
831
- return { content: [{ type: "text", text: "Error: API key required to check passport." }] };
832
- }
833
- const data = await apiCall("GET", "/api/passport/check");
834
- return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
835
- }
836
-
837
- default:
838
- return { content: [{ type: "text", text: `Unknown tool: ${name}` }] };
839
- }
840
- } catch (err) {
841
- return { content: [{ type: "text", text: `Error: ${err.message}` }] };
842
- }
843
- });
844
-
845
- // ─── Resources ───────────────────────────────────────────
846
-
847
- server.setRequestHandler(ListResourcesRequestSchema, async () => ({
848
- resources: [
849
- {
850
- uri: "agoragentic://marketplace/manifest",
851
- name: "Agoragentic Marketplace Manifest",
852
- description: "Machine-readable marketplace discovery manifest",
853
- mimeType: "application/json"
854
- },
855
- {
856
- uri: "agoragentic://marketplace/docs",
857
- name: "Agoragentic API Documentation",
858
- description: "Full API reference",
859
- mimeType: "text/html"
860
- },
861
- {
862
- uri: "agoragentic://vault/info",
863
- name: "Vault Enhancement Services",
864
- description: "Info about Memory Slots, Secrets Locker, and Config Snapshots",
865
- mimeType: "application/json"
866
- },
867
- {
868
- uri: "agoragentic://passport/info",
869
- name: "Agent Passport Info",
870
- description: "NFT-based identity system details",
871
- mimeType: "application/json"
872
- }
873
- ]
874
- }));
875
-
876
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
877
- const { uri } = request.params;
878
-
879
- if (uri === "agoragentic://marketplace/manifest") {
880
- const data = await apiCall("GET", "/.well-known/agent-marketplace.json");
881
- return {
882
- contents: [{
883
- uri,
884
- mimeType: "application/json",
885
- text: JSON.stringify(data, null, 2)
886
- }]
887
- };
888
- }
889
-
890
- if (uri === "agoragentic://marketplace/docs") {
891
- return {
892
- contents: [{
893
- uri,
894
- mimeType: "text/plain",
895
- text: "Full API documentation available at: https://agoragentic.com/docs.html"
896
- }]
897
- };
898
- }
899
-
900
- if (uri === "agoragentic://vault/info") {
901
- const data = await apiCall("GET", "/api/vault/info");
902
- return {
903
- contents: [{
904
- uri,
905
- mimeType: "application/json",
906
- text: JSON.stringify(data, null, 2)
907
- }]
908
- };
909
- }
910
-
911
- if (uri === "agoragentic://passport/info") {
912
- const data = await apiCall("GET", "/api/passport/info");
913
- return {
914
- contents: [{
915
- uri,
916
- mimeType: "application/json",
917
- text: JSON.stringify(data, null, 2)
918
- }]
919
- };
920
- }
921
-
922
- throw new Error(`Unknown resource: ${uri}`);
923
- });
924
-
925
- // ─── Prompts ─────────────────────────────────────────────
926
-
927
- server.setRequestHandler(ListPromptsRequestSchema, async () => ({
928
- prompts: [
929
- {
930
- name: "quickstart",
931
- description: "Get started with Agoragentic. Walks you through registration, searching for capabilities, and making your first invocation.",
932
- arguments: [
933
- { name: "agent_name", description: "Name for your new agent", required: true },
934
- { name: "task", description: "What you want to accomplish (e.g., 'summarize documents', 'translate text')", required: false }
935
- ]
936
- },
937
- {
938
- name: "find_and_invoke",
939
- description: "Search for a capability matching your task and invoke it in one guided workflow.",
940
- arguments: [
941
- { name: "task", description: "Description of what you want to accomplish", required: true },
942
- { name: "max_budget", description: "Maximum USDC budget for the invocation", required: false }
943
- ]
944
- },
945
- {
946
- name: "sell_capability",
947
- description: "Guide for listing your own capability for sale on the Agoragentic marketplace.",
948
- arguments: [
949
- { name: "capability_name", description: "Name of the capability you want to sell", required: true },
950
- { name: "price", description: "Price in USDC per invocation", required: false }
951
- ]
952
- }
953
- ]
954
- }));
955
-
956
- server.setRequestHandler(GetPromptRequestSchema, async (request) => {
957
- const { name, arguments: args } = request.params;
958
-
959
- if (name === "quickstart") {
960
- return {
961
- description: "Get started with Agoragentic",
962
- messages: [
963
- {
964
- role: "user",
965
- content: {
966
- type: "text",
967
- 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.`
968
- }
969
- }
970
- ]
971
- };
972
- }
973
-
974
- if (name === "find_and_invoke") {
975
- return {
976
- description: "Find and invoke a capability",
977
- messages: [
978
- {
979
- role: "user",
980
- content: {
981
- type: "text",
982
- 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.`
983
- }
984
- }
985
- ]
986
- };
987
- }
988
-
989
- if (name === "sell_capability") {
990
- return {
991
- description: "List a capability for sale",
992
- messages: [
993
- {
994
- role: "user",
995
- content: {
996
- type: "text",
997
- 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.`
998
- }
999
- }
1000
- ]
1001
- };
1002
- }
1003
-
1004
- throw new Error(`Unknown prompt: ${name}`);
1005
- });
1006
-
1007
- // ─── Start ───────────────────────────────────────────────
1008
-
1009
- async function main() {
1010
- const transport = new StdioServerTransport();
1011
- await server.connect(transport);
1012
- console.error("Agoragentic MCP Server v2.0 running on stdio");
1013
- }
1014
-
1015
- main().catch(console.error);
1016
-
111
+ main().catch((error) => {
112
+ const message = error instanceof Error ? error.stack || error.message : String(error);
113
+ console.error(`[agoragentic-mcp] fatal: ${message}`);
114
+ process.exit(1);
115
+ });