@apiosk/mcp 1.3.1 → 2.0.0

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 (92) hide show
  1. package/README.md +84 -537
  2. package/assets/brand/apiosk-a-20260921.png +0 -0
  3. package/assets/brand/apple-touch-icon.png +0 -0
  4. package/assets/brand/favicon-dark.ico +0 -0
  5. package/assets/brand/favicon-light.ico +0 -0
  6. package/assets/brand/favicon.ico +0 -0
  7. package/assets/brand/icon-192.png +0 -0
  8. package/assets/brand/icon-512.png +0 -0
  9. package/assets/brand/icon-maskable-512.png +0 -0
  10. package/assets/brand/inter-OFL.txt +93 -0
  11. package/assets/brand/inter-latin-400-normal.woff2 +0 -0
  12. package/assets/brand/inter-latin-500-normal.woff2 +0 -0
  13. package/assets/brand/inter-latin-600-normal.woff2 +0 -0
  14. package/assets/brand/mark-20260905-transparent.svg +1 -0
  15. package/assets/brand/mark-20260918.svg +1 -0
  16. package/assets/brand/mark-black-20260918.svg +1 -0
  17. package/assets/brand/mark-dark-20260905-transparent.png +0 -0
  18. package/assets/brand/mark-dark-20260918.png +0 -0
  19. package/assets/brand/mark-dark-96.png +0 -0
  20. package/assets/brand/mark-light-20260905-transparent.png +0 -0
  21. package/assets/brand/mark-light-20260918.png +0 -0
  22. package/assets/brand/mark-light-96.png +0 -0
  23. package/assets/brand/wordmark-black-320.png +0 -0
  24. package/assets/brand/wordmark-white-320.png +0 -0
  25. package/docs/branding.md +11 -0
  26. package/docs/marketplace-submission-2026-04-08.md +4 -0
  27. package/docs/marketplace-submission-2026-08-20.md +157 -0
  28. package/docs/openai-plugin-submission-2026-09-05.md +143 -0
  29. package/docs/sepa-rail.md +13 -13
  30. package/dxt.json +26 -22
  31. package/index.mjs +3 -1
  32. package/logo-optimized-light.png +0 -0
  33. package/package.json +15 -13
  34. package/plugin/apiosk/.codex-plugin/plugin.json +45 -0
  35. package/plugin/apiosk/.mcp.json +8 -0
  36. package/plugin/apiosk/assets/icon-dark.png +0 -0
  37. package/plugin/apiosk/assets/icon.png +0 -0
  38. package/plugin/apiosk/assets/icon.svg +1 -0
  39. package/plugin/apiosk/assets/logo.png +0 -0
  40. package/plugin/apiosk/skills/apiosk/SKILL.md +33 -0
  41. package/plugin/apiosk/skills/apiosk/agents/openai.yaml +12 -0
  42. package/server.json +98 -9
  43. package/server.mjs +258 -71
  44. package/src/approval-feedback.mjs +21 -0
  45. package/src/brand-routes.mjs +28 -0
  46. package/src/create-server.mjs +168 -9
  47. package/src/display-money.mjs +20 -0
  48. package/src/display-text.mjs +67 -0
  49. package/src/gateway-client.mjs +42 -0
  50. package/src/gateway-v2-ask.mjs +50 -0
  51. package/src/gateway-v2-card-account.mjs +23 -0
  52. package/src/gateway-v2-card-actions.mjs +67 -0
  53. package/src/gateway-v2-card-answer-text.mjs +130 -0
  54. package/src/gateway-v2-card-answer.mjs +68 -0
  55. package/src/gateway-v2-card-blocks.mjs +166 -0
  56. package/src/gateway-v2-card-body.mjs +259 -0
  57. package/src/gateway-v2-card-budget.mjs +21 -0
  58. package/src/gateway-v2-card-cbs.mjs +56 -0
  59. package/src/gateway-v2-card-choices.mjs +4 -0
  60. package/src/gateway-v2-card-clarification.mjs +25 -0
  61. package/src/gateway-v2-card-compact.mjs +34 -0
  62. package/src/gateway-v2-card-events.mjs +41 -0
  63. package/src/gateway-v2-card-presentation.mjs +125 -0
  64. package/src/gateway-v2-card-research.mjs +63 -0
  65. package/src/gateway-v2-card-result.mjs +32 -0
  66. package/src/gateway-v2-card-search.mjs +39 -0
  67. package/src/gateway-v2-card-sources.mjs +32 -0
  68. package/src/gateway-v2-card-style.mjs +76 -0
  69. package/src/gateway-v2-card-verdict.mjs +66 -0
  70. package/src/gateway-v2-card.mjs +91 -0
  71. package/src/gateway-v2-contracts.json +66 -0
  72. package/src/gateway-v2-instructions.md +113 -0
  73. package/src/gateway-v2-recovery.mjs +18 -0
  74. package/src/gateway-v2-report-links.mjs +14 -0
  75. package/src/gateway-v2-workflows.mjs +17 -0
  76. package/src/gateway-v2.mjs +179 -0
  77. package/src/oauth.mjs +603 -364
  78. package/src/observability.mjs +210 -0
  79. package/src/result-presentation.mjs +9 -0
  80. package/src/runtime.mjs +24 -3091
  81. package/src/settlement-disclosure.mjs +26 -0
  82. package/src/source-groups.mjs +91 -0
  83. package/src/source-value-format.mjs +25 -0
  84. package/src/tool-result.mjs +20 -0
  85. package/src/ui-bridge.mjs +185 -0
  86. package/src/well-known-routes.mjs +132 -0
  87. package/src/funding-options.mjs +0 -255
  88. package/src/gateway-management.mjs +0 -107
  89. package/src/listing-metadata.mjs +0 -287
  90. package/src/local-config.mjs +0 -256
  91. package/src/payment-guidance.mjs +0 -307
  92. package/src/wallet-store.mjs +0 -476
package/src/runtime.mjs CHANGED
@@ -1,3102 +1,35 @@
1
- import { ApioskClient, ApioskPaymentRequiredError } from "@apiosk/sdk";
2
- import { privateKeyToAccount } from "viem/accounts";
1
+ // The runtime: Gateway v2, for every transport.
2
+ //
3
+ // Since 2.0 there is one runtime. The hosted server and the stdio package both
4
+ // speak the Gateway v2 agent contract (src/gateway-v2.mjs): browse sources,
5
+ // plan and price one task, approve it once, follow it to its result. The 1.x
6
+ // tools that called the agent gateway's /v1/ask, /v1/select, /v1/run,
7
+ // /v1/plans and /v1/jobs are gone; the agent gateway answers those routes with
8
+ // 410 and names their v2 replacements.
9
+ //
10
+ // What is left here is the one decision that belongs to the runtime rather than
11
+ // to the tools: which gateway. `APIOSK_GATEWAY_V2_URL` overrides it (a local or
12
+ // staging gateway); unset, it is the production gateway.
3
13
 
4
- import { buildFundingOptions } from "./funding-options.mjs";
5
- import { requestGatewayManagement } from "./gateway-management.mjs";
6
- import { buildListingMetadata, resolveCategory } from "./listing-metadata.mjs";
7
- import {
8
- buildDiscoveryPaymentHint,
9
- buildPaymentGuidance,
10
- buildPaymentGuide,
11
- } from "./payment-guidance.mjs";
12
- import {
13
- createApioskLocalConfigPaths,
14
- parseConnectString,
15
- readLocalApioskConfig,
16
- saveLocalApioskDashboardSession,
17
- saveLocalApioskConfig,
18
- } from "./local-config.mjs";
19
- import { createLocalWalletStore } from "./wallet-store.mjs";
14
+ import { createV2Runtime } from "./gateway-v2.mjs";
20
15
 
21
- const DEFAULT_LIMIT = 25;
22
- const CACHE_TTL_MS = 60_000;
23
- const DEFAULT_GATEWAY_BASE_URL = "https://gateway.apiosk.com";
16
+ export const DEFAULT_GATEWAY_V2_URL = "https://gateway.apiosk.com";
24
17
 
25
- const DASHBOARD_WALLET_TOOLS = [
26
- {
27
- name: "apiosk_list_wallets",
28
- description: "List the signed-in user's managed Apiosk wallets. Requires an Apiosk dashboard session from local env auth or hosted MCP authorization.",
29
- inputSchema: {
30
- type: "object",
31
- additionalProperties: false,
32
- properties: {},
33
- },
34
- },
35
- {
36
- name: "apiosk_create_wallet",
37
- description: "Create or import a managed Apiosk wallet for the signed-in user.",
38
- inputSchema: {
39
- type: "object",
40
- required: ["label"],
41
- properties: {
42
- label: { type: "string" },
43
- mode: {
44
- type: "string",
45
- enum: ["create", "import_private_key", "import_phrase"],
46
- },
47
- secret: {
48
- type: "string",
49
- description: "Private key or recovery phrase when importing.",
50
- },
51
- daily_limit_usdc: { type: "number" },
52
- per_tx_limit_usdc: { type: "number" },
53
- },
54
- },
55
- },
56
- {
57
- name: "apiosk_update_wallet",
58
- description: "Update wallet label, status, display metadata, or spending limits.",
59
- inputSchema: {
60
- type: "object",
61
- required: ["wallet_id"],
62
- properties: {
63
- wallet_id: { type: "string" },
64
- label: { type: "string" },
65
- status: {
66
- type: "string",
67
- enum: ["active", "paused", "revoked"],
68
- },
69
- daily_limit_usdc: { type: "number" },
70
- per_tx_limit_usdc: { type: "number" },
71
- color: { type: "string" },
72
- icon: { type: "string" },
73
- },
74
- },
75
- },
76
- {
77
- name: "apiosk_delete_wallet",
78
- description: "Delete one of the signed-in user's wallets.",
79
- inputSchema: {
80
- type: "object",
81
- required: ["wallet_id"],
82
- properties: {
83
- wallet_id: { type: "string" },
84
- },
85
- },
86
- },
87
- {
88
- name: "apiosk_get_wallet_activity",
89
- description: "Fetch recent transactions and activity for one managed wallet.",
90
- inputSchema: {
91
- type: "object",
92
- required: ["wallet_id"],
93
- properties: {
94
- wallet_id: { type: "string" },
95
- page: { type: "number" },
96
- limit: { type: "number" },
97
- },
98
- },
99
- },
100
- {
101
- name: "apiosk_create_wallet_connect_string",
102
- description: "Rotate or create a connect token for a managed wallet and return the new connect string.",
103
- inputSchema: {
104
- type: "object",
105
- required: ["wallet_id"],
106
- properties: {
107
- wallet_id: { type: "string" },
108
- token_name: { type: "string" },
109
- revoke_existing: { type: "boolean" },
110
- private_key: {
111
- type: "string",
112
- description: "Optional legacy recovery key when requested by the backend.",
113
- },
114
- },
115
- },
116
- },
117
- {
118
- name: "apiosk_list_wallet_api_keys",
119
- description: "List API keys / connect tokens for a managed wallet.",
120
- inputSchema: {
121
- type: "object",
122
- required: ["wallet_id"],
123
- properties: {
124
- wallet_id: { type: "string" },
125
- },
126
- },
127
- },
128
- {
129
- name: "apiosk_create_wallet_api_key",
130
- description: "Create a new API key / connect token for a managed wallet.",
131
- inputSchema: {
132
- type: "object",
133
- required: ["wallet_id"],
134
- properties: {
135
- wallet_id: { type: "string" },
136
- name: { type: "string" },
137
- expiration_days: { type: "number" },
138
- revoke_existing: { type: "boolean" },
139
- private_key: {
140
- type: "string",
141
- description: "Optional legacy recovery key when requested by the backend.",
142
- },
143
- },
144
- },
145
- },
146
- {
147
- name: "apiosk_update_wallet_api_key",
148
- description: "Rename, revoke, or extend an existing managed wallet API key.",
149
- inputSchema: {
150
- type: "object",
151
- required: ["wallet_id", "key_id"],
152
- properties: {
153
- wallet_id: { type: "string" },
154
- key_id: { type: "string" },
155
- name: { type: "string" },
156
- expiration_days: { type: "number" },
157
- revoke: { type: "boolean" },
158
- },
159
- },
160
- },
161
- {
162
- name: "apiosk_delete_wallet_api_key",
163
- description: "Delete an existing managed wallet API key permanently.",
164
- inputSchema: {
165
- type: "object",
166
- required: ["wallet_id", "key_id"],
167
- properties: {
168
- wallet_id: { type: "string" },
169
- key_id: { type: "string" },
170
- },
171
- },
172
- },
173
- {
174
- name: "apiosk_show_wallet_funding",
175
- description:
176
- "Show the buyer's Apiosk wallet address with a scannable QR code so they can fund it manually by sending USDC on Base mainnet from another wallet or exchange. Defaults to the user's first managed wallet when wallet_id is omitted. Always reminds the buyer that only Base mainnet USDC is accepted.",
177
- inputSchema: {
178
- type: "object",
179
- properties: {
180
- wallet_id: {
181
- type: "string",
182
- description:
183
- "Optional. Wallet to show funding info for. Defaults to the user's first managed wallet.",
184
- },
185
- },
186
- },
187
- },
188
- ];
189
-
190
- const LOCAL_ACCOUNT_AND_CREDITS_TOOLS = [
191
- {
192
- name: "apiosk_create_account",
193
- description:
194
- "Create an Apiosk dashboard account for credits-based payments. Local stdio only; saves the returned dashboard session token locally when available.",
195
- inputSchema: {
196
- type: "object",
197
- required: ["email", "password"],
198
- properties: {
199
- email: { type: "string" },
200
- password: { type: "string" },
201
- save_session: {
202
- type: "boolean",
203
- description: "Defaults to true when a session token is returned immediately.",
204
- },
205
- },
206
- },
207
- },
208
- {
209
- name: "apiosk_sign_in",
210
- description:
211
- "Sign into an existing Apiosk dashboard account and save the session token locally for credits and managed-wallet tools.",
212
- inputSchema: {
213
- type: "object",
214
- required: ["email", "password"],
215
- properties: {
216
- email: { type: "string" },
217
- password: { type: "string" },
218
- save_session: {
219
- type: "boolean",
220
- description: "Defaults to true.",
221
- },
222
- },
223
- },
224
- },
225
- ];
226
-
227
- const REMOTE_CREDITS_TOOLS = [];
228
-
229
- const LOCAL_WALLET_TOOLS = [
230
- {
231
- name: "apiosk_wallet_list",
232
- description: "List locally stored Apiosk wallets and show which one is active for autonomous pay and publish.",
233
- inputSchema: {
234
- type: "object",
235
- additionalProperties: false,
236
- properties: {},
237
- },
238
- },
239
- {
240
- name: "apiosk_wallet_create",
241
- description: "Create or import a local wallet for autonomous Apiosk payments and publishing. The active wallet is mirrored to ~/.apiosk/wallet.json and ~/.apiosk/wallet.txt for legacy compatibility.",
242
- inputSchema: {
243
- type: "object",
244
- required: ["label"],
245
- properties: {
246
- label: { type: "string" },
247
- mode: {
248
- type: "string",
249
- enum: ["create", "import_private_key", "import_phrase"],
250
- description: "Defaults to create.",
251
- },
252
- secret: {
253
- type: "string",
254
- description: "Private key or recovery phrase when importing.",
255
- },
256
- set_active: {
257
- type: "boolean",
258
- description: "Defaults to true.",
259
- },
260
- return_secret: {
261
- type: "boolean",
262
- description: "Only enable this when the user explicitly asks to see the private key.",
263
- },
264
- save_secret: {
265
- type: "boolean",
266
- description: "When true, also export the secret key to a local file.",
267
- },
268
- save_to: {
269
- type: "string",
270
- description: "Optional output path for the exported secret key.",
271
- },
272
- save_format: {
273
- type: "string",
274
- enum: ["json", "txt"],
275
- },
276
- include_qr_data_url: {
277
- type: "boolean",
278
- description: "Include a QR data URL alongside the terminal QR when the client can render images.",
279
- },
280
- },
281
- },
282
- },
283
- {
284
- name: "apiosk_get_started",
285
- description: "Set up local Apiosk access by importing a connect string or creating/selecting a wallet, then run a discovery probe and a small test call.",
286
- inputSchema: {
287
- type: "object",
288
- properties: {
289
- connect_string: {
290
- type: "string",
291
- description: "Optional dashboard connect string to save locally for managed access.",
292
- },
293
- connect_token: {
294
- type: "string",
295
- description: "Optional connect token to save locally when you do not have the full connect string.",
296
- },
297
- connect_authorization: {
298
- type: "string",
299
- description: "Optional Authorization header value to save alongside a managed connect token.",
300
- },
301
- connect_header_name: {
302
- type: "string",
303
- description: "Optional connect header override. Defaults to X-Apiosk-Connect-Token.",
304
- },
305
- wallet_address: {
306
- type: "string",
307
- description: "Optional wallet address to save with a managed connect token.",
308
- },
309
- wallet_id: {
310
- type: "string",
311
- description: "Optional existing local wallet id to select before running the test call.",
312
- },
313
- wallet_label: {
314
- type: "string",
315
- description: "Label to use if a new local wallet must be created. Defaults to Apiosk wallet.",
316
- },
317
- create_wallet: {
318
- type: "boolean",
319
- description: "Defaults to true when no managed connect token is configured.",
320
- },
321
- test_slug: {
322
- type: "string",
323
- description: "Optional API slug for the test call. Defaults to the first discovered API.",
324
- },
325
- test_operation: {
326
- type: "string",
327
- description: "Optional explicit operation for the test call.",
328
- },
329
- test_input: {
330
- description: "Optional JSON input for the test call. Defaults to an empty object.",
331
- },
332
- include_qr_data_url: {
333
- type: "boolean",
334
- description: "Include QR image data when funding instructions are returned.",
335
- },
336
- },
337
- },
338
- },
339
- {
340
- name: "apiosk_configure",
341
- description: "Show a structured Apiosk control menu for a wallet, including funding instructions, QR code output when available, and wallet/pay/publish/data actions.",
342
- inputSchema: {
343
- type: "object",
344
- properties: {
345
- wallet_id: { type: "string" },
346
- section: {
347
- type: "string",
348
- enum: ["overview", "funding", "wallet", "payments", "publish", "data", "security"],
349
- },
350
- funding_provider: {
351
- type: "string",
352
- enum: ["manual", "transak", "onramper"],
353
- },
354
- include_qr_data_url: {
355
- type: "boolean",
356
- description: "Include a QR data URL alongside the terminal QR when supported by the client.",
357
- },
358
- },
359
- },
360
- },
361
- {
362
- name: "apiosk_wallet_select",
363
- description: "Select the active local wallet used by apiosk_execute, dynamic API tools, and publish tools.",
364
- inputSchema: {
365
- type: "object",
366
- required: ["wallet_id"],
367
- properties: {
368
- wallet_id: { type: "string" },
369
- include_qr_data_url: {
370
- type: "boolean",
371
- description: "Include a QR data URL alongside the terminal QR when supported by the client.",
372
- },
373
- },
374
- },
375
- },
376
- {
377
- name: "apiosk_wallet_update",
378
- description: "Rename a local wallet or mark it active.",
379
- inputSchema: {
380
- type: "object",
381
- required: ["wallet_id"],
382
- properties: {
383
- wallet_id: { type: "string" },
384
- label: { type: "string" },
385
- set_active: { type: "boolean" },
386
- },
387
- },
388
- },
389
- {
390
- name: "apiosk_wallet_delete",
391
- description: "Delete a local wallet from the MCP keystore.",
392
- inputSchema: {
393
- type: "object",
394
- required: ["wallet_id"],
395
- properties: {
396
- wallet_id: { type: "string" },
397
- },
398
- },
399
- },
400
- {
401
- name: "apiosk_wallet_reveal_secret",
402
- description: "Reveal a local wallet's private key. Only call this when the user explicitly asks to see the secret key.",
403
- inputSchema: {
404
- type: "object",
405
- required: ["wallet_id"],
406
- properties: {
407
- wallet_id: { type: "string" },
408
- },
409
- },
410
- },
411
- {
412
- name: "apiosk_wallet_save_secret",
413
- description: "Save a local wallet's private key to a file without going through the dashboard.",
414
- inputSchema: {
415
- type: "object",
416
- required: ["wallet_id"],
417
- properties: {
418
- wallet_id: { type: "string" },
419
- path: {
420
- type: "string",
421
- description: "Optional output path. Defaults to a file under ~/.apiosk/exports.",
422
- },
423
- format: {
424
- type: "string",
425
- enum: ["json", "txt"],
426
- },
427
- },
428
- },
429
- },
430
- ];
431
-
432
- const PUBLISH_TOOLS = [
433
- {
434
- name: "apiosk_publish_api",
435
- description: "Publish a new API on Apiosk using the active local wallet or APIOSK_PRIVATE_KEY. No dashboard required.",
436
- inputSchema: {
437
- type: "object",
438
- required: ["name", "slug", "endpoint_url", "price_usd", "description"],
439
- properties: {
440
- wallet_id: { type: "string" },
441
- name: { type: "string" },
442
- slug: { type: "string" },
443
- endpoint_url: { type: "string" },
444
- price_usd: { type: "number" },
445
- description: { type: "string" },
446
- category: { type: "string" },
447
- listing_group: {
448
- type: "string",
449
- enum: ["api", "datasets", "compute"],
450
- },
451
- listing_metadata: {
452
- type: "object",
453
- additionalProperties: true,
454
- description: "Optional MCP-native metadata override. Defaults are generated automatically.",
455
- },
456
- },
457
- },
458
- },
459
- {
460
- name: "apiosk_list_my_apis",
461
- description: "List the APIs owned by the active local wallet or APIOSK_PRIVATE_KEY.",
462
- inputSchema: {
463
- type: "object",
464
- properties: {
465
- wallet_id: { type: "string" },
466
- },
467
- },
468
- },
469
- {
470
- name: "apiosk_update_api",
471
- description: "Update a published Apiosk API with signed wallet auth.",
472
- inputSchema: {
473
- type: "object",
474
- required: ["slug"],
475
- properties: {
476
- wallet_id: { type: "string" },
477
- slug: { type: "string" },
478
- endpoint_url: { type: "string" },
479
- price_usd: { type: "number" },
480
- description: { type: "string" },
481
- active: { type: "boolean" },
482
- listing_metadata: {
483
- type: "object",
484
- additionalProperties: true,
485
- },
486
- },
487
- },
488
- },
489
- {
490
- name: "apiosk_delete_api",
491
- description: "Delete or deactivate one of your published Apiosk APIs with signed wallet auth.",
492
- inputSchema: {
493
- type: "object",
494
- required: ["slug"],
495
- properties: {
496
- wallet_id: { type: "string" },
497
- slug: { type: "string" },
498
- },
499
- },
500
- },
501
- ];
502
-
503
- const HELP_TOOL = {
504
- name: "apiosk_help",
505
- description: "Explain what Apiosk MCP is, how to connect it, how auth and x402 payments work, the settlement rails (USDC and credits), and the recommended workflow for discovery, wallets, and publishing.",
506
- annotations: {
507
- readOnlyHint: true,
508
- openWorldHint: false,
509
- destructiveHint: false,
510
- },
511
- inputSchema: {
512
- type: "object",
513
- properties: {
514
- topic: {
515
- type: "string",
516
- enum: ["overview", "setup", "auth", "workflow", "payments", "rails", "wallets", "publish", "configure"],
517
- description: "Optional help topic. Defaults to overview. Use 'rails' to learn how settlement works across USDC and credits.",
518
- },
519
- },
520
- },
521
- };
522
-
523
- const EXPLORE_TOOL = {
524
- name: "apiosk_explore",
525
- description: "Browse Apiosk listing groups and explore one group at a time before narrowing with search.",
526
- annotations: {
527
- readOnlyHint: true,
528
- openWorldHint: false,
529
- destructiveHint: false,
530
- },
531
- inputSchema: {
532
- type: "object",
533
- properties: {
534
- listing_type: {
535
- type: "string",
536
- enum: ["api", "dataset", "service", "connector", "skill", "product"],
537
- },
538
- search: {
539
- type: "string",
540
- description: "Optional free-text search when listing_type is set.",
541
- },
542
- sort: {
543
- type: "string",
544
- enum: ["name", "price", "newest"],
545
- },
546
- order: {
547
- type: "string",
548
- enum: ["asc", "desc"],
549
- },
550
- limit: {
551
- type: "number",
552
- },
553
- offset: {
554
- type: "number",
555
- },
556
- },
557
- },
558
- };
559
-
560
- const SEARCH_TOOL = {
561
- name: "apiosk_search",
562
- description: "Search and browse the Apiosk catalog. Use this first when you need to find APIs by capability, price, or category.",
563
- annotations: {
564
- readOnlyHint: true,
565
- openWorldHint: false,
566
- destructiveHint: false,
567
- },
568
- inputSchema: {
569
- type: "object",
570
- properties: {
571
- search: {
572
- type: "string",
573
- description: "Free-text search over API names and descriptions.",
574
- },
575
- category: {
576
- type: "string",
577
- description: "Optional category filter.",
578
- },
579
- sort: {
580
- type: "string",
581
- enum: ["name", "price", "newest"],
582
- description: "Sort order for results.",
583
- },
584
- order: {
585
- type: "string",
586
- enum: ["asc", "desc"],
587
- description: "Sort direction.",
588
- },
589
- limit: {
590
- type: "number",
591
- description: "Maximum number of APIs to return.",
592
- },
593
- offset: {
594
- type: "number",
595
- description: "Pagination offset.",
596
- },
597
- },
598
- },
599
- };
600
-
601
- const METADATA_TOOL_INPUT_SCHEMA = {
602
- type: "object",
603
- required: ["slug"],
604
- properties: {
605
- slug: {
606
- type: "string",
607
- description: "Apiosk API slug, for example 'agent-json-diff'.",
608
- },
609
- },
610
- };
611
-
612
- const GET_API_TOOL = {
613
- name: "apiosk_get_api",
614
- description: "Fetch full listing detail and agent metadata for a specific Apiosk API slug.",
615
- annotations: {
616
- readOnlyHint: true,
617
- openWorldHint: false,
618
- destructiveHint: false,
619
- },
620
- inputSchema: METADATA_TOOL_INPUT_SCHEMA,
621
- };
622
-
623
- const METADATA_TOOL = {
624
- name: "apiosk_metadata",
625
- description: "Fetch full listing detail and agent metadata for a specific Apiosk API slug.",
626
- annotations: {
627
- readOnlyHint: true,
628
- openWorldHint: false,
629
- destructiveHint: false,
630
- },
631
- inputSchema: METADATA_TOOL_INPUT_SCHEMA,
632
- };
633
-
634
- const EXECUTE_TOOL = {
635
- name: "apiosk_execute",
636
- description: "Execute any Apiosk API by slug through the uniform /execute contract.",
637
- annotations: {
638
- readOnlyHint: false,
639
- openWorldHint: true,
640
- destructiveHint: true,
641
- },
642
- inputSchema: {
643
- type: "object",
644
- required: ["slug"],
645
- properties: {
646
- slug: {
647
- type: "string",
648
- description: "Apiosk API slug.",
649
- },
650
- operation: {
651
- type: "string",
652
- description: "Optional explicit operation id or path.",
653
- },
654
- input: {
655
- type: "object",
656
- additionalProperties: true,
657
- description: "Raw JSON body for the default operation, or the envelope input field when operation is provided.",
658
- },
659
- query: {
660
- type: "object",
661
- additionalProperties: true,
662
- description: "Optional query override when using the execute envelope.",
663
- },
664
- path_params: {
665
- type: "object",
666
- additionalProperties: true,
667
- description: "Optional path parameter override when using the execute envelope.",
668
- },
669
- },
670
- },
671
- };
672
-
673
- const HEALTH_TOOL = {
674
- name: "apiosk_health",
675
- description: "Report Apiosk MCP runtime status and the configured gateway base URL.",
676
- annotations: {
677
- readOnlyHint: true,
678
- openWorldHint: false,
679
- destructiveHint: false,
680
- },
681
- inputSchema: {
682
- type: "object",
683
- additionalProperties: false,
684
- properties: {},
685
- },
686
- };
687
-
688
- const PAYMENT_GUIDE_TOOL = {
689
- name: "apiosk_payment_guide",
690
- description:
691
- "Explain how to pay through the Apiosk gateway. Returns a buyer guide (how an agent settles a paid API call over USDC/x402 or credits, tailored to the current auth) and a provider guide (how to publish an API and get paid). Pass slug to scope buyer guidance to one listing, or role to pick a side.",
692
- annotations: {
693
- readOnlyHint: true,
694
- openWorldHint: false,
695
- destructiveHint: false,
696
- },
697
- inputSchema: {
698
- type: "object",
699
- properties: {
700
- role: {
701
- type: "string",
702
- enum: ["buyer", "provider", "both"],
703
- description: "Which side to explain. Defaults to both.",
704
- },
705
- slug: {
706
- type: "string",
707
- description: "Optional API slug to scope buyer guidance (price, payment steps) to one listing.",
708
- },
709
- },
710
- },
711
- };
712
-
713
- const DISCOVERY_TOOLS = [
714
- HELP_TOOL,
715
- PAYMENT_GUIDE_TOOL,
716
- EXPLORE_TOOL,
717
- SEARCH_TOOL,
718
- GET_API_TOOL,
719
- EXECUTE_TOOL,
720
- ];
721
- // Discovery + payment guidance available to every remote buyer, even before
722
- // they authorize (these tools are public / read-only).
723
- const HOSTED_DISCOVERY_TOOLS = [
724
- HELP_TOOL,
725
- PAYMENT_GUIDE_TOOL,
726
- SEARCH_TOOL,
727
- EXPLORE_TOOL,
728
- GET_API_TOOL,
729
- METADATA_TOOL,
730
- EXECUTE_TOOL,
731
- HEALTH_TOOL,
732
- ];
733
-
734
- // Managed buyer tools that work over request-scoped dashboard auth on the
735
- // hosted endpoint: prepaid credits + full managed agent-wallet CRUD. These are
736
- // protected (the OAuth layer requires authorization before they run).
737
- // apiosk_show_wallet_funding is intentionally excluded — it resolves a local /
738
- // env wallet, which does not exist on the hosted surface.
739
- const HOSTED_MANAGED_TOOLS = [
740
- ...REMOTE_CREDITS_TOOLS,
741
- ...DASHBOARD_WALLET_TOOLS.filter((tool) => tool.name !== "apiosk_show_wallet_funding"),
742
- ];
743
-
744
- // The hosted/remote surface is fully capable: discovery, payment guidance,
745
- // generic + dynamic per-API execution, credits, and managed-wallet management.
746
- // Publishing stays local/portal-only because it requires a client-side signing
747
- // key the hosted server never holds.
748
- const HOSTED_REMOTE_TOOLS = [...HOSTED_DISCOVERY_TOOLS, ...HOSTED_MANAGED_TOOLS];
749
-
750
- const ALL_STATIC_TOOLS = [
751
- ...DISCOVERY_TOOLS,
752
- ...HOSTED_REMOTE_TOOLS,
753
- ...LOCAL_ACCOUNT_AND_CREDITS_TOOLS,
754
- ...LOCAL_WALLET_TOOLS,
755
- ...DASHBOARD_WALLET_TOOLS,
756
- ...PUBLISH_TOOLS,
757
- ];
758
-
759
- const PUBLIC_STATIC_TOOL_NAMES = new Set(
760
- DISCOVERY_TOOLS.map((tool) => tool.name).filter((name) => name !== "apiosk_execute")
761
- );
762
- const REMOTE_PROTECTED_STATIC_TOOL_NAMES = new Set([
763
- "apiosk_execute",
764
- ...REMOTE_CREDITS_TOOLS.map((tool) => tool.name),
765
- ...DASHBOARD_WALLET_TOOLS.map((tool) => tool.name),
766
- ]);
767
-
768
- function trimString(value) {
769
- return String(value || "").trim();
770
- }
771
-
772
- function sanitizeToolName(name, fallback) {
773
- const candidate = String(name || fallback || "apiosk_tool")
774
- .trim()
775
- .replace(/[^A-Za-z0-9_-]+/g, "-")
776
- .replace(/^-+|-+$/g, "");
777
-
778
- return candidate || "apiosk_tool";
18
+ /** The Gateway v2 origin this process talks to. */
19
+ export function resolveGatewayV2Url(env = process.env) {
20
+ return String(env?.APIOSK_GATEWAY_V2_URL || "").trim() || DEFAULT_GATEWAY_V2_URL;
779
21
  }
780
22
 
781
23
  /**
782
- * Map the gateway's boolean active/verified flags onto the provider-portal's
783
- * three-state listing lifecycle (live | pending | disabled). The portal writes
784
- * apis.status (active|inactive) and the gateway auto-verifies owner-owned
785
- * (KYB-gated) listings, so for a catalog reader: not active -> disabled,
786
- * active but unverified -> pending review, active + verified -> live.
24
+ * Build the MCP runtime.
787
25
  *
788
- * NOTE: the public catalog (GET /v1/apis) hard-filters to active AND verified
789
- * rows, so entries built from it are always "live" — the field documents that
790
- * invariant for catalog readers. The pending/disabled branches are reached when
791
- * this runs over a richer object (e.g. a get_api_detail response, which returns
792
- * the real active/verified flags).
26
+ * @param {object} options
27
+ * @param {object} [options.env] environment, for the gateway URL and a stdio token
28
+ * @param {boolean} [options.hostedAuthEnabled] hosted: never fall back to a machine-wide token
29
+ * @param {Function} [options.fetchImpl] fetch, for tests
793
30
  */
794
- function deriveListingStatus(api) {
795
- if (api.active === false) return "disabled";
796
- if (api.verified === false) return "pending";
797
- return "live";
798
- }
799
-
800
- function buildCatalogEntry(api, toolName) {
801
- return {
802
- slug: api.slug,
803
- name: api.name,
804
- description: api.description,
805
- category: api.category,
806
- listing_type:
807
- api.listing_type ||
808
- api.listing_metadata?.provider?.listing_type ||
809
- null,
810
- price_usd: api.price_usd ?? api.listing_metadata?.cost_per_call ?? null,
811
- docs_url: api.docs_url ?? api.listing_metadata?.provider?.docs_url ?? null,
812
- tool_name: toolName,
813
- default_operation: api.listing_metadata?.default_operation ?? null,
814
- tags: api.listing_metadata?.tags ?? [],
815
- mcp_native: api.listing_metadata?.mcp_native ?? false,
816
- // Listing lifecycle from the gateway /v1/apis flags so a catalog reader can
817
- // tell live from pending-verification from disabled, matching the portal.
818
- status: deriveListingStatus(api),
819
- active: api.active ?? true,
820
- verified: api.verified ?? false,
821
- };
822
- }
823
-
824
- function buildDynamicTools(catalog, reservedTools) {
825
- const tools = [];
826
- const toolIndex = new Map();
827
-
828
- // Per-API "dynamic" tools surface one MCP tool per gateway listing, which
829
- // DUPLICATES every paid API. Exposing them also lets the provider-portal MCP
830
- // import create separate /tools/<slug> gateway endpoints that undercut (or
831
- // give away for free) the real API listings. Agents must instead discover via
832
- // apiosk_search / apiosk_explore and call any API through apiosk_execute,
833
- // which settles the real per-call price. So these are OFF by default; set
834
- // APIOSK_MCP_DYNAMIC_TOOLS=true to opt back in.
835
- if (process.env.APIOSK_MCP_DYNAMIC_TOOLS !== "true") {
836
- return { tools, toolIndex };
837
- }
838
-
839
- const usedNames = new Set(reservedTools.map((tool) => tool.name));
840
-
841
- for (const api of catalog) {
842
- if (api.active === false) continue;
843
-
844
- const rawName = api.listing_metadata?.mcp_tool?.name || api.slug;
845
- let toolName = sanitizeToolName(rawName, api.slug);
846
- if (usedNames.has(toolName)) {
847
- toolName = sanitizeToolName(`${toolName}-${api.slug}`, api.slug);
848
- }
849
-
850
- usedNames.add(toolName);
851
-
852
- const price = api.price_usd ?? api.listing_metadata?.cost_per_call;
853
- const dynamicDescriptionParts = [
854
- api.listing_metadata?.mcp_tool?.description || api.description || api.name,
855
- `Apiosk slug: ${api.slug}.`,
856
- ];
857
-
858
- if (price !== undefined && price !== null) {
859
- dynamicDescriptionParts.push(`Cost per call: $${price}.`);
860
- }
861
-
862
- if (api.listing_metadata?.default_operation) {
863
- dynamicDescriptionParts.push(`Default operation: ${api.listing_metadata.default_operation}.`);
864
- }
865
-
866
- dynamicDescriptionParts.push("This tool executes the API through Apiosk's uniform /execute contract.");
867
-
868
- tools.push({
869
- name: toolName,
870
- description: dynamicDescriptionParts.join(" "),
871
- inputSchema: api.listing_metadata?.mcp_tool?.inputSchema || {
872
- type: "object",
873
- additionalProperties: true,
874
- },
875
- annotations: api.listing_metadata?.mcp_tool?.annotations,
876
- });
877
-
878
- toolIndex.set(toolName, {
879
- api,
880
- toolName,
881
- });
882
- }
883
-
884
- return { tools, toolIndex };
885
- }
886
-
887
- function resolveDashboardUserToken(env = process.env, savedConfig = null) {
888
- return trimString(
889
- env.APIOSK_DASHBOARD_JWT ||
890
- env.APIOSK_USER_JWT ||
891
- env.APIOSK_AUTH_HEADER_VALUE ||
892
- env.APIOSK_SESSION_TOKEN ||
893
- savedConfig?.dashboard_session_token
894
- );
895
- }
896
-
897
- function resolveControlPlaneUrl(env = process.env, savedConfig = null) {
898
- return trimString(
899
- env.APIOSK_CONTROL_PLANE_URL || savedConfig?.control_plane_url || "https://mcp.apiosk.com"
900
- ).replace(
901
- /\/+$/,
902
- ""
903
- );
904
- }
905
-
906
- function resolveGatewayBaseUrl(env = process.env, savedConfig = null) {
907
- return trimString(env.APIOSK_GATEWAY || savedConfig?.gateway_url || DEFAULT_GATEWAY_BASE_URL).replace(
908
- /\/+$/,
909
- ""
910
- );
911
- }
912
-
913
- function summarizeSavedConnectConfig(savedConfig = null, env = process.env) {
914
- if (!savedConfig) return null;
915
-
916
- return {
917
- gateway_url: trimString(savedConfig.gateway_url || DEFAULT_GATEWAY_BASE_URL),
918
- agent_wallet_address: trimString(savedConfig.agent_wallet_address || ""),
919
- connect_header_name: trimString(savedConfig.connect_header_name || "X-Apiosk-Connect-Token"),
920
- connect_token_saved: Boolean(trimString(savedConfig.connect_token)),
921
- connect_authorization_saved:
922
- Boolean(trimString(savedConfig.connect_authorization || env.APIOSK_CONNECT_AUTHORIZATION)),
923
- };
924
- }
925
-
926
- function buildAuthState({ env = process.env, savedConfig = null, activeWallet = null } = {}) {
927
- const envConnectToken = trimString(env.APIOSK_CONNECT_TOKEN);
928
- const savedConnectToken = trimString(savedConfig?.connect_token);
929
- const envAuthorization = trimString(env.APIOSK_CONNECT_AUTHORIZATION);
930
- const savedAuthorization = trimString(savedConfig?.connect_authorization);
931
- const envWalletAddress = trimString(env.APIOSK_WALLET_ADDRESS).toLowerCase();
932
- const savedWalletAddress = trimString(savedConfig?.agent_wallet_address).toLowerCase();
933
-
934
- let mode = "none";
935
- if (envConnectToken || savedConnectToken) {
936
- mode = "connect_token";
937
- } else if (envAuthorization || savedAuthorization) {
938
- mode = "authorization";
939
- } else if (trimString(env.APIOSK_PRIVATE_KEY)) {
940
- mode = "env_private_key";
941
- } else if (activeWallet?.source === "local_store") {
942
- mode = "local_wallet";
943
- } else if (envWalletAddress || savedWalletAddress || activeWallet?.address) {
944
- mode = "wallet_address";
945
- }
946
-
947
- return {
948
- mode,
949
- connect_token_configured: Boolean(envConnectToken || savedConnectToken),
950
- authorization_configured: Boolean(envAuthorization || savedAuthorization),
951
- wallet_address:
952
- activeWallet?.address ||
953
- envWalletAddress ||
954
- savedWalletAddress ||
955
- null,
956
- gateway_url: resolveGatewayBaseUrl(env, savedConfig),
957
- };
958
- }
959
-
960
- function resolveRequestDashboardUserToken(authInfo = null) {
961
- const extra = authInfo?.extra;
962
- if (!extra || typeof extra !== "object") return "";
963
-
964
- return trimString(
965
- extra.dashboardSessionToken ||
966
- extra.dashboard_session_token ||
967
- extra.userToken ||
968
- extra.user_token ||
969
- ""
970
- );
971
- }
972
-
973
- function hasRequestScopedDashboardAccess(authInfo = null) {
974
- return Boolean(resolveRequestDashboardUserToken(authInfo));
975
- }
976
-
977
- function buildRequestScopedDashboardHeaders(authInfo = null) {
978
- const token = resolveRequestDashboardUserToken(authInfo);
979
- if (!token) return null;
980
-
981
- return {
982
- "x-apiosk-user-jwt": token,
983
- authorization: `Bearer ${token}`,
984
- };
985
- }
986
-
987
- function pickMessage(payload, fallback) {
988
- if (payload && typeof payload === "object") {
989
- if (typeof payload.message === "string" && payload.message.trim()) {
990
- return payload.message.trim();
991
- }
992
- if (typeof payload.error === "string" && payload.error.trim()) {
993
- return payload.error.trim();
994
- }
995
- }
996
-
997
- return fallback;
998
- }
999
-
1000
- function asObject(value) {
1001
- return value && typeof value === "object" && !Array.isArray(value) ? value : null;
1002
- }
1003
-
1004
- function createDashboardWalletManagerFromEnv(env = process.env) {
1005
- const dashboardUrl = resolveControlPlaneUrl(env);
1006
- const token = resolveDashboardUserToken(env);
1007
-
1008
- return {
1009
- isConfigured() {
1010
- return Boolean(token);
1011
- },
1012
- async request(path, init = {}) {
1013
- if (!token) {
1014
- throw new Error(
1015
- "Wallet tools require APIOSK_DASHBOARD_JWT or APIOSK_USER_JWT in the local MCP environment."
1016
- );
1017
- }
1018
-
1019
- const headers = new Headers(init.headers || {});
1020
- headers.set("accept", "application/json");
1021
- headers.set("x-apiosk-user-jwt", token);
1022
- headers.set("authorization", `Bearer ${token}`);
1023
-
1024
- let body = init.body;
1025
- if (body !== undefined && body !== null && typeof body !== "string") {
1026
- headers.set("content-type", "application/json");
1027
- body = JSON.stringify(body);
1028
- }
1029
-
1030
- const response = await fetch(`${dashboardUrl}${path}`, {
1031
- ...init,
1032
- body,
1033
- cache: "no-store",
1034
- headers,
1035
- });
1036
-
1037
- const text = await response.text();
1038
- let payload = null;
1039
- try {
1040
- payload = text ? JSON.parse(text) : null;
1041
- } catch {
1042
- payload = text || null;
1043
- }
1044
-
1045
- if (!response.ok) {
1046
- throw new Error(
1047
- pickMessage(payload, `Dashboard wallet request failed with HTTP ${response.status}`)
1048
- );
1049
- }
1050
-
1051
- return payload;
1052
- },
1053
- };
1054
- }
1055
-
1056
- function content(value) {
1057
- const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
1058
- const result = {
1059
- content: [
1060
- {
1061
- type: "text",
1062
- text,
1063
- },
1064
- ],
1065
- };
1066
-
1067
- if (value && typeof value === "object" && !Array.isArray(value)) {
1068
- result.structuredContent = value;
1069
- }
1070
-
1071
- return result;
1072
- }
1073
-
1074
- function errorContent(value) {
1075
- return {
1076
- content: [
1077
- {
1078
- type: "text",
1079
- text: typeof value === "string" ? value : JSON.stringify(value, null, 2),
1080
- },
1081
- ],
1082
- isError: true,
1083
- };
1084
- }
1085
-
1086
- function summarizeDynamicToolResult(tool, result) {
1087
- if (!result || typeof result !== "object") return null;
1088
- if (result.status !== "success") return null;
1089
-
1090
- const payload =
1091
- result.result && typeof result.result === "object" && !Array.isArray(result.result)
1092
- ? result.result
1093
- : null;
1094
- if (!payload) return null;
1095
-
1096
- const orderId = payload.order_id;
1097
- const pizzaType = payload.pizza?.type || payload.pizza_type;
1098
- const pizzaSize = payload.pizza?.size || payload.size;
1099
- const address = payload.address;
1100
- const receiptUrl = payload.receipt_url;
1101
-
1102
- if (orderId && pizzaType && pizzaSize) {
1103
- const parts = [
1104
- `${tool.api.name || tool.api.slug} order confirmed.`,
1105
- `${pizzaSize} ${pizzaType}.`,
1106
- `Order ID ${orderId}.`,
1107
- ];
1108
- if (address) parts.push(`Delivery: ${address}.`);
1109
- if (receiptUrl) parts.push(`Receipt: ${receiptUrl}.`);
1110
- if (typeof result.cost === "number" && Number.isFinite(result.cost)) {
1111
- parts.push(`Cost: $${result.cost.toFixed(2)}.`);
1112
- }
1113
- return parts.join(" ");
1114
- }
1115
-
1116
- return null;
1117
- }
1118
-
1119
- function buildHelpPayload(topic = "overview", options = {}) {
1120
- const selectedTopic = String(topic || "overview");
1121
- const localWalletsEnabled = Boolean(options.localWalletsEnabled);
1122
- const hostedAuthEnabled = Boolean(options.hostedAuthEnabled);
1123
-
1124
- const topics = {
1125
- overview: {
1126
- topic: "overview",
1127
- summary:
1128
- "Apiosk MCP turns the Apiosk gateway into agent-native tools so Claude, Codex, and other MCP clients can discover APIs, inspect metadata, create local wallets, execute paid endpoints, and publish APIs from one surface.",
1129
- what_you_get: [
1130
- "Built-in discovery tools for explore, search, detail lookup, and generic execute",
1131
- "Dynamic API-specific tools generated from Apiosk listing metadata",
1132
- "Autonomous payment with APIOSK_PRIVATE_KEY or a selected local wallet",
1133
- localWalletsEnabled
1134
- ? "Local wallet and publish tools that do not require the dashboard"
1135
- : hostedAuthEnabled
1136
- ? "Managed wallet CRUD and prepaid-credits tools over your authorized session; publishing is local/portal-only"
1137
- : "Wallet and publish tools are disabled in this server mode unless local wallets are enabled",
1138
- ],
1139
- next_steps: [
1140
- localWalletsEnabled
1141
- ? "Use apiosk_get_started first to save a connect string or create a wallet and run a test call"
1142
- : "Start with apiosk_search or apiosk_explore to discover APIs",
1143
- "Use apiosk_explore or apiosk_search to find an API",
1144
- "Use apiosk_payment_guide to learn exactly how to pay (buyer) or publish (provider)",
1145
- "Use apiosk_get_api to inspect detail and metadata",
1146
- "Use the API-specific dynamic tool or apiosk_execute to call it",
1147
- localWalletsEnabled
1148
- ? "Use apiosk_wallet_create to create a local wallet for autonomous pay and publish"
1149
- : "Run the local stdio package to unlock local wallet and publish tools",
1150
- ],
1151
- },
1152
- setup: {
1153
- topic: "setup",
1154
- summary: "Connect Apiosk MCP over remote HTTP or local stdio.",
1155
- claude_code: [
1156
- "Remote HTTP: claude mcp add --transport http apiosk https://mcp.apiosk.com/mcp",
1157
- "Verify: claude mcp get apiosk",
1158
- ],
1159
- local_stdio: [
1160
- "npx -y @apiosk/mcp",
1161
- "Or add a stdio config that runs npx -y @apiosk/mcp",
1162
- "Then call apiosk_get_started to save a managed connect string or create a local wallet and run a test call",
1163
- "Use apiosk_create_account or apiosk_sign_in when you want a human to fund credits through Adyen and let the agent spend those credits later",
1164
- ],
1165
- endpoint: "https://mcp.apiosk.com/mcp",
1166
- local_wallet_note:
1167
- "Local wallet creation, secret export, and publish tools are intended for the local stdio package. The public HTTP deployment keeps them disabled by default.",
1168
- },
1169
- auth: {
1170
- topic: "auth",
1171
- summary: "Apiosk supports multiple auth paths depending on how you want the agent to pay or identify itself.",
1172
- auth_modes: [
1173
- "APIOSK_PRIVATE_KEY: automatic x402 settlement for paid endpoints and signed publish requests",
1174
- "Local wallet tools: create/select a wallet inside the MCP package and use it automatically for pay and publish",
1175
- "APIOSK_CONNECT_TOKEN: dashboard-managed access token",
1176
- "APIOSK_CONNECT_AUTHORIZATION: custom Authorization header",
1177
- "APIOSK_WALLET_ADDRESS: wallet-aware flows without a token",
1178
- "APIOSK_X_PAYMENT: manually attach a prebuilt x402 proof",
1179
- "APIOSK_DASHBOARD_JWT or APIOSK_USER_JWT: unlock dashboard-managed wallet CRUD tools",
1180
- "Saved local dashboard session from apiosk_sign_in or apiosk_create_account: reuse credits and managed-wallet tools without exporting a JWT manually",
1181
- ],
1182
- notes: [
1183
- "Connect token is the simplest managed option",
1184
- "Private key or a selected local wallet is the best fit for autonomous paid execution",
1185
- "Dashboard JWT is optional and only needed for managed-wallet dashboard endpoints",
1186
- ],
1187
- },
1188
- workflow: {
1189
- topic: "workflow",
1190
- summary: "Recommended usage pattern for agents working with Apiosk.",
1191
- steps: [
1192
- localWalletsEnabled
1193
- ? "Call apiosk_get_started first when you want the MCP package to configure local auth and prove the setup with a test call"
1194
- : "Call apiosk_search or apiosk_explore to find a capability",
1195
- "Call apiosk_explore to browse listing groups or apiosk_search to find a capability",
1196
- "Read tool_name from search results when a dynamic tool is available",
1197
- "Call apiosk_get_api when you need detail, docs_url, or listing metadata",
1198
- "Use the dynamic tool directly for the cleanest invocation shape",
1199
- "Use apiosk_execute when you need to force a slug, operation, query, or path_params envelope",
1200
- localWalletsEnabled
1201
- ? "Create or select a local wallet before making paid calls or publishing APIs"
1202
- : "Use APIOSK_PRIVATE_KEY if you need autonomous payment on the public server mode",
1203
- ],
1204
- },
1205
- payments: {
1206
- topic: "payments",
1207
- summary: "Paid Apiosk APIs can return x402 payment requirements if the client is not configured to settle automatically.",
1208
- behavior: [
1209
- "With APIOSK_PRIVATE_KEY configured, the SDK can auto-settle supported x402 flows",
1210
- "With a selected local wallet, the MCP package uses that wallet automatically for paid execution",
1211
- "Without auto-pay, paid calls return structured payment_required data",
1212
- "The MCP server surfaces payment errors with hints that explain how to configure settlement",
1213
- ],
1214
- recommended_setup: [
1215
- "Use apiosk_get_started in the local stdio package for the shortest path from install to first paid-capable call",
1216
- "Use apiosk_wallet_create and apiosk_wallet_select in the local stdio package for the cleanest autonomous workflow",
1217
- "Use APIOSK_PRIVATE_KEY when you want deterministic wallet selection from environment variables",
1218
- "Use APIOSK_CONNECT_TOKEN when access is managed in the dashboard",
1219
- "Use the buyer portal to top up a prepaid credits balance and let the agent keep spending from it",
1220
- ],
1221
- see_also:
1222
- "Call apiosk_help with topic='rails' to learn how the same connect token settles over USDC and prepaid credits, including the rail fallback order.",
1223
- },
1224
- rails: {
1225
- topic: "rails",
1226
- summary:
1227
- "Apiosk supports two settlement rails. A single buyer connect token can settle paid calls over USDC (x402 on Base) or prepaid credits. The gateway picks the rail per call; the agent does not need to know which one is used.",
1228
- settlement_rails: [
1229
- "usdc_x402: on-chain USDC on Base (chain 8453), settled per call via an x402 payment proof from the agent wallet or APIOSK_PRIVATE_KEY.",
1230
- "credits: prepaid balance topped up once by a human via the buyer portal, then spent down per call.",
1231
- ],
1232
- rail_fallback_order: [
1233
- "1. USDC / x402 wallet when the agent can produce a payment proof.",
1234
- "2. Prepaid credits balance.",
1235
- "A 402 is only returned when none of the buyer's enabled rails can cover the call.",
1236
- ],
1237
- connect_string_note:
1238
- "The connect string identifies the buyer's managed wallet and connect token; APIO_WALLET_* limits bound the USDC rail. See help topic 'setup' for the connect string format.",
1239
- provider_settlement: {
1240
- what_it_is:
1241
- "The settlement_rails above are how BUYERS pay. This is the PROVIDER (seller) side: how an API owner receives their earnings. Agents do not interact with it — it is provider-account configuration in the provider portal.",
1242
- how_it_works: [
1243
- "USDC earnings settle to the API's payout wallet (apis.wallet_address), which the portal links to a verified, Monerium-linked payout_wallets row.",
1244
- "Crypto -> EUR off-ramp mandate (optional): the provider signs an EIP-191 authorization and an on-chain mandate (ApioskOfframpExecutor on Base). Apiosk's offramp keeper then auto-converts their accumulated USDC to EURe via Monerium and redeems it to their IBAN over SEPA once the balance crosses their bundle threshold. Non-custodial — the keeper can never redirect funds or exceed the provider's on-chain per-run cap or cooldown.",
1245
- ],
1246
- },
1247
- },
1248
- wallets: {
1249
- topic: "wallets",
1250
- summary: "Local wallet tools let Claude or Codex create, select, reveal, and save wallet secrets without opening the dashboard.",
1251
- tools: [
1252
- "apiosk_get_started",
1253
- "apiosk_wallet_create",
1254
- "apiosk_configure",
1255
- "apiosk_wallet_list",
1256
- "apiosk_wallet_select",
1257
- "apiosk_wallet_reveal_secret",
1258
- "apiosk_wallet_save_secret",
1259
- ],
1260
- notes: [
1261
- "The active wallet is mirrored to ~/.apiosk/wallet.json and ~/.apiosk/wallet.txt",
1262
- "Use apiosk_wallet_reveal_secret only when the user explicitly asks to see the private key",
1263
- "Use apiosk_wallet_save_secret to write a backup file without showing the secret in chat",
1264
- ],
1265
- },
1266
- publish: {
1267
- topic: "publish",
1268
- summary: "Apiosk publish tools sign gateway management requests directly with the active local wallet or APIOSK_PRIVATE_KEY.",
1269
- tools: [
1270
- "apiosk_publish_api",
1271
- "apiosk_list_my_apis",
1272
- "apiosk_update_api",
1273
- "apiosk_delete_api",
1274
- ],
1275
- requirements: [
1276
- "A signing wallet must be available from a selected local wallet or APIOSK_PRIVATE_KEY",
1277
- "endpoint_url must use HTTPS",
1278
- "slug must use lowercase letters, numbers, and hyphens",
1279
- ],
1280
- identity_note:
1281
- "These tools manage listings via WALLET SIGNATURE — the community/MCP publish channel, owned by a synthetic platform account. That is a different identity from the provider portal, where providers manage APIs under a Supabase-auth account (owner_id) after Monerium KYB, and where listings auto-verify on create. A listing published here and one created in the portal are managed through different identities and do not currently round-trip between the two surfaces.",
1282
- },
1283
- configure: {
1284
- topic: "configure",
1285
- summary: "Use apiosk_configure to open a structured control panel for the active wallet, including funding QR codes, provider options, wallet actions, publish actions, and local data paths.",
1286
- examples: [
1287
- "{ \"section\": \"funding\" }",
1288
- "{ \"wallet_id\": \"...\", \"section\": \"security\" }",
1289
- "{ \"section\": \"funding\", \"funding_provider\": \"onramper\" }",
1290
- ],
1291
- },
1292
- };
1293
-
1294
- const fallback =
1295
- topics[selectedTopic] ||
1296
- {
1297
- topic: "overview",
1298
- error: `Unknown help topic: ${selectedTopic}`,
1299
- available_topics: Object.keys(topics),
1300
- };
1301
-
1302
- return {
1303
- ...fallback,
1304
- available_topics: Object.keys(topics),
1305
- };
1306
- }
1307
-
1308
- function hasExecuteEnvelope(argumentsObject) {
1309
- return Boolean(
1310
- argumentsObject &&
1311
- typeof argumentsObject === "object" &&
1312
- ("operation" in argumentsObject ||
1313
- "input" in argumentsObject ||
1314
- "query" in argumentsObject ||
1315
- "path_params" in argumentsObject)
1316
- );
1317
- }
1318
-
1319
- function validateSlug(slug) {
1320
- return /^[a-z0-9-]+$/.test(String(slug || ""));
1321
- }
1322
-
1323
- function buildWalletToolArguments(wallet, extra = {}) {
1324
- const base = { ...extra };
1325
- if (wallet?.source === "local_store" && wallet?.id) {
1326
- base.wallet_id = wallet.id;
1327
- }
1328
- return base;
1329
- }
1330
-
1331
- function normalizePrivateKeyHex(value) {
1332
- const trimmed = trimString(value);
1333
- if (!trimmed) return "";
1334
-
1335
- const normalized = trimmed.startsWith("0x") ? trimmed : `0x${trimmed}`;
1336
- if (!/^0x[0-9a-fA-F]{64}$/.test(normalized)) {
1337
- throw new Error("APIOSK_PRIVATE_KEY must be a 32-byte hex private key.");
1338
- }
1339
-
1340
- return normalized;
1341
- }
1342
-
1343
- function resolveEnvPrivateWallet(env = process.env) {
1344
- const privateKey = normalizePrivateKeyHex(env.APIOSK_PRIVATE_KEY);
1345
- if (!privateKey) return null;
1346
-
1347
- const account = privateKeyToAccount(privateKey);
1348
- const derivedAddress = account.address.toLowerCase();
1349
- const configuredAddress = trimString(env.APIOSK_WALLET_ADDRESS).toLowerCase();
1350
-
1351
- if (configuredAddress && configuredAddress !== derivedAddress) {
1352
- throw new Error("APIOSK_WALLET_ADDRESS does not match APIOSK_PRIVATE_KEY.");
1353
- }
1354
-
1355
- return {
1356
- address: derivedAddress,
1357
- private_key: privateKey,
1358
- label: "Environment wallet",
1359
- source: "env_private_key",
1360
- };
1361
- }
1362
-
1363
31
  export function createApioskMcpRuntime(options = {}) {
1364
32
  const env = options.env || process.env;
1365
- const fixedClient = options.client || null;
1366
- const clientFactory = options.clientFactory || null;
1367
- const providedDashboardManager = options.walletManager || null;
1368
- const hostedAuthEnabled = options.hostedAuthEnabled === true;
1369
- const localWalletStore =
1370
- options.localWalletStore ||
1371
- (options.enableLocalWallets === false ? null : createLocalWalletStore(env));
1372
- const cache = {
1373
- catalog: null,
1374
- expiresAt: 0,
1375
- dynamicTools: null,
1376
- toolIndex: new Map(),
1377
- toolNamesBySlug: new Map(),
1378
- };
1379
-
1380
- async function getActiveExecutionWallet() {
1381
- try {
1382
- const envWallet = resolveEnvPrivateWallet(env);
1383
- if (envWallet) return envWallet;
1384
- } catch (error) {
1385
- throw error;
1386
- }
1387
-
1388
- if (!localWalletStore) return null;
1389
- return localWalletStore.resolveSigningWallet();
1390
- }
1391
-
1392
- async function getSavedConfig() {
1393
- return readLocalApioskConfig(env);
1394
- }
1395
-
1396
- // Summarize whether the agent can settle a paid call right now, and how, so
1397
- // discovery responses and apiosk_payment_guide can give tailored buyer
1398
- // instructions without a second round trip.
1399
- async function resolvePaymentCapability(authInfo = null) {
1400
- const savedConfig = await getSavedConfig().catch(() => null);
1401
- let activeWallet = null;
1402
- try {
1403
- activeWallet = await getActiveExecutionWallet();
1404
- } catch {
1405
- activeWallet = null;
1406
- }
1407
-
1408
- const requestConnectToken = trimString(authInfo?.extra?.apiosk_connect_token);
1409
- const envConnectToken = trimString(env.APIOSK_CONNECT_TOKEN || savedConfig?.connect_token);
1410
- const envAuthorization = trimString(
1411
- env.APIOSK_CONNECT_AUTHORIZATION || savedConfig?.connect_authorization
1412
- );
1413
- const envWalletAddress = trimString(env.APIOSK_WALLET_ADDRESS).toLowerCase();
1414
- const savedWalletAddress = trimString(savedConfig?.agent_wallet_address).toLowerCase();
1415
-
1416
- let method = "none";
1417
- if (activeWallet?.source === "env_private_key") method = "env_private_key";
1418
- else if (activeWallet?.source === "local_store") method = "local_wallet";
1419
- else if (requestConnectToken) method = "request_connect_token";
1420
- else if (envConnectToken || envAuthorization) method = "connect_token";
1421
- else if (envWalletAddress || savedWalletAddress || activeWallet?.address) method = "wallet_address";
1422
-
1423
- const mode = hostedAuthEnabled ? "hosted" : localWalletStore ? "local" : "remote";
1424
-
1425
- return {
1426
- method,
1427
- ready: ["env_private_key", "local_wallet", "connect_token", "request_connect_token"].includes(
1428
- method
1429
- ),
1430
- wallet_address: activeWallet?.address || envWalletAddress || savedWalletAddress || null,
1431
- has_connect_token: Boolean(requestConnectToken || envConnectToken),
1432
- local_wallets_enabled: Boolean(localWalletStore),
1433
- mode,
1434
- };
1435
- }
1436
-
1437
- function hasConfiguredDashboardAccess(authInfo = null) {
1438
- if (hasRequestScopedDashboardAccess(authInfo)) {
1439
- return true;
1440
- }
1441
-
1442
- if (providedDashboardManager?.isConfigured) {
1443
- return Boolean(providedDashboardManager.isConfigured());
1444
- }
1445
-
1446
- return Boolean(resolveDashboardUserToken(env));
1447
- }
1448
-
1449
- async function requestDashboard(path, init = {}, options = {}, authInfo = null) {
1450
- const requireAuth = options.requireAuth !== false;
1451
- const requestScopedToken = resolveRequestDashboardUserToken(authInfo);
1452
-
1453
- if (providedDashboardManager && !requestScopedToken) {
1454
- return providedDashboardManager.request(path, init);
1455
- }
1456
-
1457
- const savedConfig = await getSavedConfig();
1458
- const dashboardUrl = resolveControlPlaneUrl(env, savedConfig);
1459
- const token = requestScopedToken || resolveDashboardUserToken(env, savedConfig);
1460
-
1461
- if (requireAuth && !token) {
1462
- throw new Error(
1463
- hostedAuthEnabled ?
1464
- "This tool needs an Apiosk dashboard session. In ChatGPT or another remote MCP client, authorize the Apiosk app when prompted and then retry the tool call." :
1465
- "This tool needs an Apiosk dashboard session. Ask whether the user wants an account, then call apiosk_create_account or apiosk_sign_in. If they already have an account, sign in first or set APIOSK_DASHBOARD_JWT / APIOSK_USER_JWT."
1466
- );
1467
- }
1468
-
1469
- const headers = new Headers(init.headers || {});
1470
- headers.set("accept", "application/json");
1471
-
1472
- if (token) {
1473
- headers.set("x-apiosk-user-jwt", token);
1474
- headers.set("authorization", `Bearer ${token}`);
1475
- }
1476
-
1477
- let body = init.body;
1478
- if (body !== undefined && body !== null && typeof body !== "string") {
1479
- headers.set("content-type", "application/json");
1480
- body = JSON.stringify(body);
1481
- }
1482
-
1483
- const response = await fetch(`${dashboardUrl}${path}`, {
1484
- ...init,
1485
- body,
1486
- cache: "no-store",
1487
- headers,
1488
- });
1489
-
1490
- const text = await response.text();
1491
- let payload = null;
1492
- try {
1493
- payload = text ? JSON.parse(text) : null;
1494
- } catch {
1495
- payload = text || null;
1496
- }
1497
-
1498
- if (!response.ok) {
1499
- throw new Error(pickMessage(payload, `Dashboard request failed with HTTP ${response.status}`));
1500
- }
1501
-
1502
- return payload;
1503
- }
1504
-
1505
- async function getClient(authInfo = null) {
1506
- if (fixedClient) {
1507
- return fixedClient;
1508
- }
1509
-
1510
- const savedConfig = await getSavedConfig();
1511
- const activeWallet = await getActiveExecutionWallet();
1512
- const requestScopedHeaders = buildRequestScopedDashboardHeaders(authInfo);
1513
- const envWalletAddress = trimString(env.APIOSK_WALLET_ADDRESS).toLowerCase();
1514
- const savedWalletAddress = trimString(savedConfig?.agent_wallet_address).toLowerCase();
1515
- const privateKey = activeWallet?.private_key || normalizePrivateKeyHex(env.APIOSK_PRIVATE_KEY) || undefined;
1516
- const walletAddress = privateKey
1517
- ? activeWallet?.address
1518
- : envWalletAddress || activeWallet?.address || savedWalletAddress;
1519
-
1520
- // Hosted MCP path: a headless caller authenticated by presenting an
1521
- // Apiosk connect token. oauth.mjs stashes it on req.auth.extra so we
1522
- // can thread it back through to the gateway as X-Apiosk-Connect-Token,
1523
- // which lets the gateway settle the call via the buyer's managed
1524
- // wallet (no APIOSK_PRIVATE_KEY needed). This is the request-scoped
1525
- // override; it beats the per-process env/saved-config token.
1526
- const requestConnectToken = trimString(authInfo?.extra?.apiosk_connect_token);
1527
- const envConnectToken = trimString(env.APIOSK_CONNECT_TOKEN || savedConfig?.connect_token);
1528
- const effectiveConnectToken = requestConnectToken || envConnectToken || undefined;
1529
- const requestAuthorization = requestConnectToken ? `Bearer ${requestConnectToken}` : undefined;
1530
- const envAuthorization = trimString(env.APIOSK_CONNECT_AUTHORIZATION || savedConfig?.connect_authorization);
1531
- const effectiveAuthorization = requestAuthorization || envAuthorization || undefined;
1532
-
1533
- const clientOptions = {
1534
- baseUrl: resolveGatewayBaseUrl(env, savedConfig),
1535
- connectToken: effectiveConnectToken,
1536
- connectHeaderName:
1537
- trimString(env.APIOSK_CONNECT_HEADER_NAME || savedConfig?.connect_header_name) || undefined,
1538
- authorization: effectiveAuthorization,
1539
- walletAddress: walletAddress || undefined,
1540
- xPayment: trimString(env.APIOSK_X_PAYMENT) || undefined,
1541
- privateKey,
1542
- headers: requestScopedHeaders || undefined,
1543
- };
1544
-
1545
- if (clientFactory) {
1546
- return await clientFactory(clientOptions);
1547
- }
1548
-
1549
- return new ApioskClient(clientOptions);
1550
- }
1551
-
1552
- function getStaticTools(authInfo = null) {
1553
- if (hostedAuthEnabled) {
1554
- return [...HOSTED_REMOTE_TOOLS];
1555
- }
1556
-
1557
- const tools = [...DISCOVERY_TOOLS];
1558
-
1559
- if (localWalletStore) {
1560
- tools.push(...LOCAL_WALLET_TOOLS, ...LOCAL_ACCOUNT_AND_CREDITS_TOOLS, ...DASHBOARD_WALLET_TOOLS, ...PUBLISH_TOOLS);
1561
- } else if (hasConfiguredDashboardAccess(authInfo)) {
1562
- tools.push(...DASHBOARD_WALLET_TOOLS);
1563
- }
1564
-
1565
- return tools;
1566
- }
1567
-
1568
- async function getCatalog(force = false, authInfo = null) {
1569
- if (!force && cache.catalog && Date.now() < cache.expiresAt) {
1570
- return cache.catalog;
1571
- }
1572
-
1573
- const client = await getClient(authInfo);
1574
- const response = await client.listApis({ limit: 500, offset: 0 });
1575
- cache.catalog = response.apis || [];
1576
- cache.expiresAt = Date.now() + CACHE_TTL_MS;
1577
- cache.dynamicTools = null;
1578
- cache.toolIndex = new Map();
1579
- cache.toolNamesBySlug = new Map();
1580
- return cache.catalog;
1581
- }
1582
-
1583
- async function getTools(force = false, authInfo = null) {
1584
- if (!force && cache.dynamicTools && Date.now() < cache.expiresAt) {
1585
- return [...getStaticTools(authInfo), ...cache.dynamicTools];
1586
- }
1587
-
1588
- const catalog = await getCatalog(force, authInfo);
1589
- const { tools, toolIndex } = buildDynamicTools(catalog, ALL_STATIC_TOOLS);
1590
-
1591
- cache.dynamicTools = tools;
1592
- cache.toolIndex = toolIndex;
1593
- cache.toolNamesBySlug = new Map(
1594
- Array.from(toolIndex.values()).map((entry) => [entry.api.slug, entry.toolName])
1595
- );
1596
-
1597
- return [...getStaticTools(authInfo), ...tools];
1598
- }
1599
-
1600
- async function resolveConfigurationWallet(walletId = null) {
1601
- const targetWalletId = trimString(walletId);
1602
-
1603
- if (targetWalletId && localWalletStore) {
1604
- const wallet = await localWalletStore.getWalletRecord(targetWalletId);
1605
- return {
1606
- ...wallet,
1607
- source: "local_store",
1608
- };
1609
- }
1610
-
1611
- if (localWalletStore) {
1612
- const activeWallet = await localWalletStore.resolveActiveWallet();
1613
- if (activeWallet) {
1614
- return {
1615
- ...activeWallet,
1616
- source: "local_store",
1617
- };
1618
- }
1619
- }
1620
-
1621
- const envWallet = resolveEnvPrivateWallet(env);
1622
- if (envWallet) {
1623
- return {
1624
- id: null,
1625
- label: envWallet.label || "Environment wallet",
1626
- address: envWallet.address,
1627
- active: true,
1628
- source: envWallet.source,
1629
- };
1630
- }
1631
-
1632
- return null;
1633
- }
1634
-
1635
- function buildApioskOptionsMenu(wallet, funding) {
1636
- const walletArgs = buildWalletToolArguments(wallet);
1637
- const fundingWalletArgs = buildWalletToolArguments(wallet, { section: "funding" });
1638
-
1639
- return {
1640
- title: "Apiosk Control Menu",
1641
- description: wallet
1642
- ? "Use the sections below to fund the wallet, explore APIs, pay for calls, publish listings, and manage local Apiosk data."
1643
- : "Create or select a wallet first, then use the sections below to fund it and manage Apiosk data.",
1644
- sections: [
1645
- {
1646
- id: "wallet",
1647
- label: "Wallet",
1648
- options: [
1649
- {
1650
- label: "Get started",
1651
- tool: "apiosk_get_started",
1652
- arguments: buildWalletToolArguments(wallet, {}),
1653
- },
1654
- {
1655
- label: "List wallets",
1656
- tool: "apiosk_wallet_list",
1657
- arguments: {},
1658
- },
1659
- {
1660
- label: "Create wallet",
1661
- tool: "apiosk_wallet_create",
1662
- arguments: {},
1663
- },
1664
- {
1665
- label: "Select active wallet",
1666
- tool: "apiosk_wallet_select",
1667
- arguments: walletArgs,
1668
- available: Boolean(wallet?.source === "local_store"),
1669
- },
1670
- ],
1671
- },
1672
- {
1673
- id: "funding",
1674
- label: "Fund Wallet",
1675
- options: [
1676
- {
1677
- label: "Show funding QR and Base deposit instructions",
1678
- tool: "apiosk_configure",
1679
- arguments: fundingWalletArgs,
1680
- available: Boolean(wallet),
1681
- },
1682
- {
1683
- label: "Request Onramper checkout",
1684
- tool: "apiosk_configure",
1685
- arguments: buildWalletToolArguments(wallet, {
1686
- section: "funding",
1687
- funding_provider: "onramper",
1688
- }),
1689
- available: Boolean(funding?.providers?.find((provider) => provider.id === "onramper")?.available),
1690
- },
1691
- {
1692
- label: "Request Transak checkout",
1693
- tool: "apiosk_configure",
1694
- arguments: buildWalletToolArguments(wallet, {
1695
- section: "funding",
1696
- funding_provider: "transak",
1697
- }),
1698
- available: Boolean(funding?.providers?.find((provider) => provider.id === "transak")?.available),
1699
- },
1700
- ],
1701
- },
1702
- {
1703
- id: "payments",
1704
- label: "Explore And Pay",
1705
- options: [
1706
- {
1707
- label: "Browse listing groups",
1708
- tool: "apiosk_explore",
1709
- arguments: {},
1710
- },
1711
- {
1712
- label: "Search APIs",
1713
- tool: "apiosk_search",
1714
- arguments: {},
1715
- },
1716
- {
1717
- label: "Execute an API",
1718
- tool: "apiosk_execute",
1719
- arguments: { slug: "<api-slug>" },
1720
- },
1721
- ],
1722
- },
1723
- {
1724
- id: "publish",
1725
- label: "Publish And Manage",
1726
- options: [
1727
- {
1728
- label: "Publish a new API",
1729
- tool: "apiosk_publish_api",
1730
- arguments: buildWalletToolArguments(wallet, {
1731
- name: "<name>",
1732
- slug: "<slug>",
1733
- endpoint_url: "https://example.com",
1734
- price_usd: 0.01,
1735
- description: "<description>",
1736
- }),
1737
- available: Boolean(wallet),
1738
- },
1739
- {
1740
- label: "List my APIs",
1741
- tool: "apiosk_list_my_apis",
1742
- arguments: walletArgs,
1743
- available: Boolean(wallet),
1744
- },
1745
- {
1746
- label: "Update an API",
1747
- tool: "apiosk_update_api",
1748
- arguments: buildWalletToolArguments(wallet, { slug: "<slug>" }),
1749
- available: Boolean(wallet),
1750
- },
1751
- ],
1752
- },
1753
- {
1754
- id: "security",
1755
- label: "Security",
1756
- options: [
1757
- {
1758
- label: "Reveal secret key",
1759
- tool: "apiosk_wallet_reveal_secret",
1760
- arguments: walletArgs,
1761
- available: Boolean(wallet?.source === "local_store" && wallet?.id),
1762
- },
1763
- {
1764
- label: "Save secret key to file",
1765
- tool: "apiosk_wallet_save_secret",
1766
- arguments: walletArgs,
1767
- available: Boolean(wallet?.source === "local_store" && wallet?.id),
1768
- },
1769
- ],
1770
- },
1771
- {
1772
- id: "data",
1773
- label: "Local Data",
1774
- options: [
1775
- {
1776
- label: "Show local data paths and wallet status",
1777
- tool: "apiosk_configure",
1778
- arguments: buildWalletToolArguments(wallet, { section: "data" }),
1779
- },
1780
- ],
1781
- },
1782
- ],
1783
- };
1784
- }
1785
-
1786
- async function buildConfigurePayload(argumentsObject = {}) {
1787
- const wallet = await resolveConfigurationWallet(argumentsObject.wallet_id);
1788
- const funding = wallet
1789
- ? await buildFundingOptions({
1790
- wallet,
1791
- env,
1792
- fundingProvider: argumentsObject.funding_provider,
1793
- includeQrDataUrl: argumentsObject.include_qr_data_url === true,
1794
- })
1795
- : null;
1796
-
1797
- const dataPaths = localWalletStore
1798
- ? {
1799
- store_file: localWalletStore.paths.storeFile,
1800
- wallet_json_file: localWalletStore.paths.activeWalletJsonFile,
1801
- wallet_txt_file: localWalletStore.paths.activeWalletTextFile,
1802
- secret_export_dir: localWalletStore.paths.secretExportDir,
1803
- }
1804
- : null;
1805
-
1806
- const section = trimString(argumentsObject.section || "overview").toLowerCase() || "overview";
1807
- const menu = buildApioskOptionsMenu(wallet, funding);
1808
- const sections = {
1809
- overview: {
1810
- wallet,
1811
- funding_preview: funding?.receive_on_base || null,
1812
- next_steps: wallet
1813
- ? [
1814
- "Fund the wallet on Base with ETH or USDC.",
1815
- "Use apiosk_search or apiosk_explore to find an API.",
1816
- "Use apiosk_publish_api when you want to list your own API.",
1817
- ]
1818
- : [
1819
- "Create or select a wallet first.",
1820
- "Then reopen apiosk_configure with section='funding' to fund it.",
1821
- ],
1822
- },
1823
- funding,
1824
- wallet: {
1825
- wallet,
1826
- local_wallet_tools_enabled: Boolean(localWalletStore),
1827
- dashboard_wallet_tools_enabled: hasConfiguredDashboardAccess(),
1828
- },
1829
- payments: {
1830
- wallet,
1831
- active_payment_source: wallet ? `${wallet.label || wallet.address} (${wallet.source})` : null,
1832
- recommendation: wallet
1833
- ? "This wallet will be used for autonomous paid execution unless APIOSK_PRIVATE_KEY overrides it."
1834
- : "No active wallet is available yet.",
1835
- },
1836
- publish: {
1837
- wallet,
1838
- requirements: [
1839
- "A signing wallet must be active.",
1840
- "endpoint_url must use HTTPS.",
1841
- "slug must use lowercase letters, numbers, and hyphens.",
1842
- ],
1843
- },
1844
- data: {
1845
- wallet,
1846
- data_paths: dataPaths,
1847
- },
1848
- security: {
1849
- wallet,
1850
- rules: [
1851
- "Only reveal or export the private key when the user explicitly asks.",
1852
- "The active local wallet is mirrored to ~/.apiosk/wallet.json and ~/.apiosk/wallet.txt.",
1853
- "Exported secret-key files should be treated as sensitive credentials.",
1854
- ],
1855
- },
1856
- };
1857
-
1858
- return {
1859
- selected_section: Object.prototype.hasOwnProperty.call(sections, section) ? section : "overview",
1860
- wallet,
1861
- funding,
1862
- options_menu: menu,
1863
- sections,
1864
- section_payload:
1865
- sections[Object.prototype.hasOwnProperty.call(sections, section) ? section : "overview"],
1866
- };
1867
- }
1868
-
1869
- async function resolveDynamicTool(name, authInfo = null) {
1870
- await getTools(false, authInfo);
1871
- if (cache.toolIndex.has(name)) {
1872
- return cache.toolIndex.get(name);
1873
- }
1874
-
1875
- await getTools(true, authInfo);
1876
- return cache.toolIndex.get(name) || null;
1877
- }
1878
-
1879
- function isDynamicToolProtectedApi(api) {
1880
- const price = Number(api?.price_usd ?? api?.listing_metadata?.cost_per_call ?? 0);
1881
- const annotations = api?.listing_metadata?.mcp_tool?.annotations || {};
1882
-
1883
- if (Number.isFinite(price) && price > 0) {
1884
- return true;
1885
- }
1886
-
1887
- if (annotations.destructiveHint === true) {
1888
- return true;
1889
- }
1890
-
1891
- if (annotations.readOnlyHint === false) {
1892
- return true;
1893
- }
1894
-
1895
- return false;
1896
- }
1897
-
1898
- async function isToolProtected(name, authInfo = null) {
1899
- if (PUBLIC_STATIC_TOOL_NAMES.has(name)) {
1900
- return false;
1901
- }
1902
-
1903
- if (REMOTE_PROTECTED_STATIC_TOOL_NAMES.has(name)) {
1904
- return true;
1905
- }
1906
-
1907
- const dynamicTool = await resolveDynamicTool(name, authInfo);
1908
- if (!dynamicTool) {
1909
- return false;
1910
- }
1911
-
1912
- return isDynamicToolProtectedApi(dynamicTool.api);
1913
- }
1914
-
1915
- async function resolvePublishingWallet(walletId = null) {
1916
- if (trimString(walletId)) {
1917
- if (!localWalletStore) {
1918
- throw new Error("Local wallet selection is not enabled in this server mode.");
1919
- }
1920
-
1921
- const wallet = await localWalletStore.resolveSigningWallet(trimString(walletId));
1922
- if (!wallet) {
1923
- throw new Error("No local wallet found for the provided wallet_id.");
1924
- }
1925
- return wallet;
1926
- }
1927
-
1928
- const envWallet = resolveEnvPrivateWallet(env);
1929
- if (envWallet) return envWallet;
1930
-
1931
- if (localWalletStore) {
1932
- const activeWallet = await localWalletStore.resolveSigningWallet();
1933
- if (activeWallet) return activeWallet;
1934
- }
1935
-
1936
- throw new Error(
1937
- "No signing wallet available. Create/select a local wallet or set APIOSK_PRIVATE_KEY."
1938
- );
1939
- }
1940
-
1941
- async function handleExplore(argumentsObject = {}, authInfo = null) {
1942
- const client = await getClient(authInfo);
1943
- const capability = await resolvePaymentCapability(authInfo);
1944
- const paymentHint = buildDiscoveryPaymentHint({
1945
- capability,
1946
- mode: capability.mode,
1947
- localWalletsEnabled: capability.local_wallets_enabled,
1948
- });
1949
-
1950
- if (!argumentsObject.listing_type) {
1951
- const response = await client.requestJson("/types", { method: "GET" });
1952
- return content({
1953
- ...response,
1954
- payment: paymentHint,
1955
- next_steps:
1956
- hostedAuthEnabled ?
1957
- "Pick a listing_type and call apiosk_explore again. Then use apiosk_metadata for a slug you want to inspect or apiosk_execute to run it. Call apiosk_payment_guide to learn how to pay." :
1958
- "Pick a listing_type and call apiosk_explore again, or use apiosk_search for full-text discovery. Call apiosk_payment_guide to learn how to pay.",
1959
- });
1960
- }
1961
-
1962
- const listingType = String(argumentsObject.listing_type).trim().toLowerCase();
1963
- const response = await client.requestJson(`/types/${encodeURIComponent(listingType)}/v1`, {
1964
- method: "GET",
1965
- query: {
1966
- search: argumentsObject.search,
1967
- sort: argumentsObject.sort,
1968
- order: argumentsObject.order,
1969
- limit: argumentsObject.limit || DEFAULT_LIMIT,
1970
- offset: argumentsObject.offset || 0,
1971
- },
1972
- });
1973
-
1974
- return content({
1975
- ...response,
1976
- payment: paymentHint,
1977
- next_steps:
1978
- hostedAuthEnabled ?
1979
- "Use apiosk_metadata for listing detail and apiosk_execute when you are ready to run a slug. Call apiosk_payment_guide to learn how to pay." :
1980
- "Use apiosk_get_api for detail or call apiosk_search if you want category filtering across the whole catalog. Call apiosk_payment_guide to learn how to pay.",
1981
- });
1982
- }
1983
-
1984
- async function handleSearch(argumentsObject = {}, authInfo = null) {
1985
- const client = await getClient(authInfo);
1986
- const response = await client.listApis({
1987
- search: argumentsObject.search,
1988
- category: argumentsObject.category,
1989
- sort: argumentsObject.sort,
1990
- order: argumentsObject.order,
1991
- limit: argumentsObject.limit || DEFAULT_LIMIT,
1992
- offset: argumentsObject.offset || 0,
1993
- });
1994
-
1995
- const catalog = response.apis || [];
1996
- await getTools(false, authInfo);
1997
- const capability = await resolvePaymentCapability(authInfo);
1998
-
1999
- return content({
2000
- apis: catalog.map((api) => buildCatalogEntry(api, cache.toolNamesBySlug.get(api.slug) || null)),
2001
- meta: response.meta,
2002
- payment: buildDiscoveryPaymentHint({
2003
- capability,
2004
- mode: capability.mode,
2005
- localWalletsEnabled: capability.local_wallets_enabled,
2006
- }),
2007
- for_providers:
2008
- "Listing your own API? Call apiosk_payment_guide with role='provider' (or apiosk_publish_api) to publish it for other agents.",
2009
- next_steps:
2010
- "Call apiosk_get_api for full metadata plus a per-listing payment block, use the API-specific tool directly when tool_name is present, or call apiosk_payment_guide to learn exactly how to settle a paid call.",
2011
- });
2012
- }
2013
-
2014
- async function handleGetApi(argumentsObject = {}, authInfo = null) {
2015
- if (!argumentsObject.slug) {
2016
- return errorContent("Missing required field: slug");
2017
- }
2018
-
2019
- const client = await getClient(authInfo);
2020
- const [detail, metadata, capability] = await Promise.all([
2021
- client.getApi(argumentsObject.slug),
2022
- client.getMetadata(argumentsObject.slug).catch(() => null),
2023
- resolvePaymentCapability(authInfo),
2024
- ]);
2025
-
2026
- return content({
2027
- detail,
2028
- metadata,
2029
- payment: buildPaymentGuidance({
2030
- api: {
2031
- slug: argumentsObject.slug,
2032
- price_usd: detail?.price_usd ?? metadata?.cost_per_call ?? null,
2033
- listing_metadata: metadata || detail?.listing_metadata || null,
2034
- },
2035
- capability,
2036
- mode: capability.mode,
2037
- localWalletsEnabled: capability.local_wallets_enabled,
2038
- }),
2039
- });
2040
- }
2041
-
2042
- async function handleHealth(argumentsObject = {}, authInfo = null) {
2043
- const savedConfig = await getSavedConfig();
2044
- let gateway = null;
2045
-
2046
- try {
2047
- const client = await getClient(authInfo);
2048
- gateway =
2049
- typeof client.requestJson === "function" ?
2050
- await client.requestJson("/health", { method: "GET" }) :
2051
- { status: "unknown" };
2052
- } catch (error) {
2053
- gateway = {
2054
- status: "degraded",
2055
- error: error instanceof Error ? error.message : String(error),
2056
- };
2057
- }
2058
-
2059
- if (typeof gateway === "string") {
2060
- gateway = {
2061
- status: gateway.toLowerCase() === "ok" ? "ok" : gateway,
2062
- raw: gateway,
2063
- };
2064
- } else if (gateway && typeof gateway === "object" && !gateway.status && gateway.ok === true) {
2065
- gateway = {
2066
- ...gateway,
2067
- status: "ok",
2068
- };
2069
- }
2070
-
2071
- return content({
2072
- status: gateway?.status || "ok",
2073
- mcp: {
2074
- mode: hostedAuthEnabled ? "hosted" : localWalletStore ? "local" : "remote",
2075
- tools: hostedAuthEnabled ? HOSTED_REMOTE_TOOLS.map((tool) => tool.name) : undefined,
2076
- },
2077
- gateway,
2078
- gateway_base_url: resolveGatewayBaseUrl(env, savedConfig),
2079
- });
2080
- }
2081
-
2082
- async function handleExecute(argumentsObject = {}, authInfo = null) {
2083
- if (!argumentsObject.slug) {
2084
- return errorContent("Missing required field: slug");
2085
- }
2086
-
2087
- const client = await getClient(authInfo);
2088
- const result = await client.execute(argumentsObject.slug, argumentsObject.input, {
2089
- operation: argumentsObject.operation,
2090
- query: argumentsObject.query,
2091
- pathParams: argumentsObject.path_params,
2092
- headers: {
2093
- accept: "application/json",
2094
- },
2095
- });
2096
-
2097
- return content(result);
2098
- }
2099
-
2100
- async function handleDynamicExecute(tool, argumentsObject = {}, authInfo = null) {
2101
- const client = await getClient(authInfo);
2102
- const result = hasExecuteEnvelope(argumentsObject)
2103
- ? await client.execute(tool.api.slug, argumentsObject.input, {
2104
- operation: argumentsObject.operation,
2105
- query: argumentsObject.query,
2106
- pathParams: argumentsObject.path_params,
2107
- headers: {
2108
- accept: "application/json",
2109
- },
2110
- })
2111
- : await client.execute(tool.api.slug, argumentsObject, {
2112
- headers: {
2113
- accept: "application/json",
2114
- },
2115
- });
2116
- const summary = summarizeDynamicToolResult(tool, result);
2117
- if (summary) {
2118
- return {
2119
- content: [
2120
- {
2121
- type: "text",
2122
- text: summary,
2123
- },
2124
- ],
2125
- structuredContent: result,
2126
- };
2127
- }
2128
- return content(result);
2129
- }
2130
-
2131
- async function handleHelp(argumentsObject = {}) {
2132
- return content(
2133
- buildHelpPayload(argumentsObject.topic, {
2134
- localWalletsEnabled: Boolean(localWalletStore),
2135
- hostedAuthEnabled,
2136
- })
2137
- );
2138
- }
2139
-
2140
- async function handlePaymentGuide(argumentsObject = {}, authInfo = null) {
2141
- const capability = await resolvePaymentCapability(authInfo);
2142
- const slug = trimString(argumentsObject.slug);
2143
-
2144
- let api = null;
2145
- if (slug) {
2146
- try {
2147
- const client = await getClient(authInfo);
2148
- const [detail, metadata] = await Promise.all([
2149
- client.getApi(slug).catch(() => null),
2150
- client.getMetadata(slug).catch(() => null),
2151
- ]);
2152
- api = {
2153
- slug,
2154
- price_usd: detail?.price_usd ?? metadata?.cost_per_call ?? null,
2155
- listing_metadata: metadata || detail?.listing_metadata || null,
2156
- };
2157
- } catch {
2158
- api = { slug };
2159
- }
2160
- }
2161
-
2162
- return content(
2163
- buildPaymentGuide({
2164
- role: argumentsObject.role,
2165
- api,
2166
- capability,
2167
- mode: capability.mode,
2168
- localWalletsEnabled: capability.local_wallets_enabled,
2169
- })
2170
- );
2171
- }
2172
-
2173
- function ensureLocalDashboardSessionTools(toolName) {
2174
- if (!localWalletStore) {
2175
- return errorContent(
2176
- `${toolName} is only available in the local stdio package because it stores dashboard session state on the local machine.`
2177
- );
2178
- }
2179
-
2180
- return null;
2181
- }
2182
-
2183
- function ensureDashboardSessionTool(toolName, authInfo = null) {
2184
- if (localWalletStore || hasConfiguredDashboardAccess(authInfo) || hostedAuthEnabled) {
2185
- return null;
2186
- }
2187
-
2188
- return errorContent(
2189
- "This tool needs an Apiosk dashboard session. In remote MCP clients, authorize the Apiosk app first. In the local stdio package, call apiosk_create_account or apiosk_sign_in."
2190
- );
2191
- }
2192
-
2193
- async function handleCreateAccount(argumentsObject = {}) {
2194
- const localOnlyError = ensureLocalDashboardSessionTools("apiosk_create_account");
2195
- if (localOnlyError) return localOnlyError;
2196
-
2197
- const email = trimString(argumentsObject.email);
2198
- const password = trimString(argumentsObject.password);
2199
-
2200
- if (!email) return errorContent("Missing required field: email");
2201
- if (!password) return errorContent("Missing required field: password");
2202
-
2203
- const payload = await requestDashboard(
2204
- "/api/auth/mcp-sign-up",
2205
- {
2206
- method: "POST",
2207
- body: { email, password },
2208
- },
2209
- { requireAuth: false }
2210
- );
2211
- const record = asObject(payload) || {};
2212
-
2213
- let savedSession = false;
2214
- if (typeof record.session_token === "string" && record.session_token.trim() && argumentsObject.save_session !== false) {
2215
- await saveLocalApioskDashboardSession(
2216
- {
2217
- session_token: record.session_token,
2218
- expires_at: record.expires_at,
2219
- email,
2220
- control_plane_url: resolveControlPlaneUrl(env),
2221
- dashboard_url: trimString(env.APIOSK_DASHBOARD_URL || "https://dashboard.apiosk.com"),
2222
- },
2223
- env
2224
- );
2225
- savedSession = true;
2226
- }
2227
-
2228
- return content({
2229
- ...record,
2230
- saved_session: savedSession,
2231
- local_config_paths: createApioskLocalConfigPaths(env),
2232
- next_steps:
2233
- record.email_confirmation_required ?
2234
- [
2235
- "Tell the user to confirm their email from the Supabase/Apiosk email they just received.",
2236
- "After confirmation, call apiosk_sign_in.",
2237
- ] :
2238
- [
2239
- "Explore the Apiosk catalog with apiosk_search or apiosk_explore.",
2240
- ],
2241
- });
2242
- }
2243
-
2244
- async function handleSignIn(argumentsObject = {}) {
2245
- const localOnlyError = ensureLocalDashboardSessionTools("apiosk_sign_in");
2246
- if (localOnlyError) return localOnlyError;
2247
-
2248
- const email = trimString(argumentsObject.email);
2249
- const password = trimString(argumentsObject.password);
2250
-
2251
- if (!email) return errorContent("Missing required field: email");
2252
- if (!password) return errorContent("Missing required field: password");
2253
-
2254
- const payload = await requestDashboard(
2255
- "/api/auth/mcp-sign-in",
2256
- {
2257
- method: "POST",
2258
- body: { email, password },
2259
- },
2260
- { requireAuth: false }
2261
- );
2262
- const record = asObject(payload) || {};
2263
-
2264
- let savedSession = false;
2265
- if (typeof record.session_token === "string" && record.session_token.trim() && argumentsObject.save_session !== false) {
2266
- await saveLocalApioskDashboardSession(
2267
- {
2268
- session_token: record.session_token,
2269
- expires_at: record.expires_at,
2270
- email,
2271
- control_plane_url: resolveControlPlaneUrl(env),
2272
- dashboard_url: trimString(env.APIOSK_DASHBOARD_URL || "https://dashboard.apiosk.com"),
2273
- },
2274
- env
2275
- );
2276
- savedSession = true;
2277
- }
2278
-
2279
- return content({
2280
- ...record,
2281
- saved_session: savedSession,
2282
- local_config_paths: createApioskLocalConfigPaths(env),
2283
- next_steps: [
2284
- "Explore the Apiosk catalog with apiosk_search or apiosk_explore.",
2285
- ],
2286
- });
2287
- }
2288
-
2289
- async function handleWalletList(authInfo = null) {
2290
- return content(await requestDashboard("/api/agent-wallets", {}, {}, authInfo));
2291
- }
2292
-
2293
- async function handleWalletCreate(argumentsObject = {}, authInfo = null) {
2294
- return content(
2295
- await requestDashboard(
2296
- "/api/agent-wallets",
2297
- {
2298
- method: "POST",
2299
- body: argumentsObject,
2300
- },
2301
- {},
2302
- authInfo
2303
- )
2304
- );
2305
- }
2306
-
2307
- async function handleWalletUpdate(argumentsObject = {}, authInfo = null) {
2308
- if (!argumentsObject.wallet_id) {
2309
- return errorContent("Missing required field: wallet_id");
2310
- }
2311
-
2312
- const { wallet_id, ...updates } = argumentsObject;
2313
- return content(
2314
- await requestDashboard(
2315
- `/api/agent-wallets/${wallet_id}`,
2316
- {
2317
- method: "PATCH",
2318
- body: updates,
2319
- },
2320
- {},
2321
- authInfo
2322
- )
2323
- );
2324
- }
2325
-
2326
- async function handleWalletDelete(argumentsObject = {}, authInfo = null) {
2327
- if (!argumentsObject.wallet_id) {
2328
- return errorContent("Missing required field: wallet_id");
2329
- }
2330
-
2331
- return content(
2332
- await requestDashboard(
2333
- `/api/agent-wallets/${argumentsObject.wallet_id}`,
2334
- {
2335
- method: "DELETE",
2336
- },
2337
- {},
2338
- authInfo
2339
- )
2340
- );
2341
- }
2342
-
2343
- async function handleWalletActivity(argumentsObject = {}, authInfo = null) {
2344
- if (!argumentsObject.wallet_id) {
2345
- return errorContent("Missing required field: wallet_id");
2346
- }
2347
-
2348
- const params = new URLSearchParams();
2349
- if (argumentsObject.page !== undefined) params.set("page", String(argumentsObject.page));
2350
- if (argumentsObject.limit !== undefined) params.set("limit", String(argumentsObject.limit));
2351
- const query = params.toString();
2352
-
2353
- return content(
2354
- await requestDashboard(
2355
- `/api/agent-wallets/${argumentsObject.wallet_id}/transactions${query ? `?${query}` : ""}`,
2356
- {},
2357
- {},
2358
- authInfo
2359
- )
2360
- );
2361
- }
2362
-
2363
- async function handleWalletConnectString(argumentsObject = {}, authInfo = null) {
2364
- if (!argumentsObject.wallet_id) {
2365
- return errorContent("Missing required field: wallet_id");
2366
- }
2367
-
2368
- const { wallet_id, ...body } = argumentsObject;
2369
- return content(
2370
- await requestDashboard(
2371
- `/api/agent-wallets/${wallet_id}/connect-string`,
2372
- {
2373
- method: "POST",
2374
- body,
2375
- },
2376
- {},
2377
- authInfo
2378
- )
2379
- );
2380
- }
2381
-
2382
- async function handleWalletApiKeys(argumentsObject = {}, authInfo = null) {
2383
- if (!argumentsObject.wallet_id) {
2384
- return errorContent("Missing required field: wallet_id");
2385
- }
2386
-
2387
- return content(
2388
- await requestDashboard(
2389
- `/api/agent-wallets/${argumentsObject.wallet_id}/api-keys`,
2390
- {},
2391
- {},
2392
- authInfo
2393
- )
2394
- );
2395
- }
2396
-
2397
- async function handleWalletApiKeyCreate(argumentsObject = {}, authInfo = null) {
2398
- if (!argumentsObject.wallet_id) {
2399
- return errorContent("Missing required field: wallet_id");
2400
- }
2401
-
2402
- const { wallet_id, ...body } = argumentsObject;
2403
- return content(
2404
- await requestDashboard(
2405
- `/api/agent-wallets/${wallet_id}/api-keys`,
2406
- {
2407
- method: "POST",
2408
- body,
2409
- },
2410
- {},
2411
- authInfo
2412
- )
2413
- );
2414
- }
2415
-
2416
- async function handleWalletApiKeyUpdate(argumentsObject = {}, authInfo = null) {
2417
- if (!argumentsObject.wallet_id) {
2418
- return errorContent("Missing required field: wallet_id");
2419
- }
2420
- if (!argumentsObject.key_id) {
2421
- return errorContent("Missing required field: key_id");
2422
- }
2423
-
2424
- const { wallet_id, key_id, ...body } = argumentsObject;
2425
- return content(
2426
- await requestDashboard(
2427
- `/api/agent-wallets/${wallet_id}/api-keys/${key_id}`,
2428
- {
2429
- method: "PATCH",
2430
- body,
2431
- },
2432
- {},
2433
- authInfo
2434
- )
2435
- );
2436
- }
2437
-
2438
- async function handleWalletApiKeyDelete(argumentsObject = {}, authInfo = null) {
2439
- if (!argumentsObject.wallet_id) {
2440
- return errorContent("Missing required field: wallet_id");
2441
- }
2442
- if (!argumentsObject.key_id) {
2443
- return errorContent("Missing required field: key_id");
2444
- }
2445
-
2446
- return content(
2447
- await requestDashboard(
2448
- `/api/agent-wallets/${argumentsObject.wallet_id}/api-keys/${argumentsObject.key_id}`,
2449
- { method: "DELETE" },
2450
- {},
2451
- authInfo
2452
- )
2453
- );
2454
- }
2455
-
2456
- async function handleLocalWalletList() {
2457
- if (!localWalletStore) {
2458
- return errorContent("Local wallet tools are not enabled in this server mode.");
2459
- }
2460
-
2461
- return content(await localWalletStore.listWallets());
2462
- }
2463
-
2464
- async function handleLocalWalletCreate(argumentsObject = {}) {
2465
- if (!localWalletStore) {
2466
- return errorContent("Local wallet tools are not enabled in this server mode.");
2467
- }
2468
-
2469
- const created = await localWalletStore.createWallet(argumentsObject);
2470
-
2471
- if (argumentsObject.return_secret === true) {
2472
- const secret = await localWalletStore.revealSecret(created.wallet.id);
2473
- created.private_key = secret.private_key;
2474
- created.warning = secret.warning;
2475
- }
2476
-
2477
- if (argumentsObject.save_secret === true) {
2478
- created.secret_export = await localWalletStore.saveSecret(created.wallet.id, {
2479
- path: argumentsObject.save_to,
2480
- format: argumentsObject.save_format,
2481
- });
2482
- }
2483
-
2484
- // Force include_qr_data_url so we can append an inline image content
2485
- // block below — clients that render images (Claude Desktop, MCP
2486
- // Inspector) will then show the funding QR right next to the
2487
- // newly-created wallet without a follow-up tool call.
2488
- created.configure = await buildConfigurePayload({
2489
- wallet_id: created.wallet.id,
2490
- section: "overview",
2491
- include_qr_data_url: true,
2492
- });
2493
-
2494
- const result = content(created);
2495
- const dataUrl = created.configure?.funding?.receive_on_base?.qr_code_data_url;
2496
- if (typeof dataUrl === "string" && dataUrl.startsWith("data:image/png;base64,")) {
2497
- result.content.push({
2498
- type: "image",
2499
- data: dataUrl.slice("data:image/png;base64,".length),
2500
- mimeType: "image/png",
2501
- });
2502
- }
2503
- return result;
2504
- }
2505
-
2506
- async function handleConfigure(argumentsObject = {}) {
2507
- return content(await buildConfigurePayload(argumentsObject));
2508
- }
2509
-
2510
- async function handleShowWalletFunding(argumentsObject = {}) {
2511
- const wallet = await resolveConfigurationWallet(argumentsObject.wallet_id);
2512
- if (!wallet?.address) {
2513
- return errorContent(
2514
- "No managed wallet is available. Use apiosk_create_wallet (or apiosk_get_started) to create one first.",
2515
- );
2516
- }
2517
-
2518
- const funding = await buildFundingOptions({
2519
- wallet,
2520
- env,
2521
- fundingProvider: "manual",
2522
- includeQrDataUrl: true,
2523
- });
2524
- if (!funding?.receive_on_base) {
2525
- return errorContent("Could not build funding instructions for this wallet.");
2526
- }
2527
-
2528
- const receive = funding.receive_on_base;
2529
- const baseUsdcContract = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
2530
- const lines = [
2531
- "Send USDC to your Apiosk wallet:",
2532
- "",
2533
- `Address: ${receive.address}`,
2534
- "Network: Base mainnet (chain id 8453)",
2535
- `Token: USDC (${baseUsdcContract})`,
2536
- "",
2537
- "WARNING: only Base mainnet USDC. Sending from Ethereum, Polygon, Solana,",
2538
- "or any other network will permanently lose the funds — this address is",
2539
- "Base only.",
2540
- "",
2541
- `Block explorer: ${receive.explorer_url}`,
2542
- ];
2543
- if (receive.qr_code_terminal) {
2544
- lines.push("", receive.qr_code_terminal);
2545
- } else if (receive.qr_image_url) {
2546
- lines.push("", `QR image: ${receive.qr_image_url}`);
2547
- }
2548
-
2549
- const messageContent = [{ type: "text", text: lines.join("\n") }];
2550
-
2551
- // Render the QR inline via MCP image content when the buyer's client
2552
- // supports images (Claude Desktop, the MCP Inspector, etc.). Falls back
2553
- // gracefully on terminals without image support — the text block above
2554
- // still has the address and the ANSI QR.
2555
- const dataUrl = receive.qr_code_data_url;
2556
- if (typeof dataUrl === "string" && dataUrl.startsWith("data:image/png;base64,")) {
2557
- messageContent.push({
2558
- type: "image",
2559
- data: dataUrl.slice("data:image/png;base64,".length),
2560
- mimeType: "image/png",
2561
- });
2562
- }
2563
-
2564
- return {
2565
- content: messageContent,
2566
- structuredContent: {
2567
- wallet_id: wallet.id ?? null,
2568
- wallet_label: wallet.label ?? null,
2569
- address: receive.address,
2570
- network: "base",
2571
- chain_id: 8453,
2572
- token_symbol: "USDC",
2573
- token_contract: baseUsdcContract,
2574
- explorer_url: receive.explorer_url,
2575
- qr_payload: receive.qr_payload,
2576
- qr_image_url: receive.qr_image_url,
2577
- transfer_uri: receive.transfer_uri,
2578
- },
2579
- };
2580
- }
2581
-
2582
- async function handleGetStarted(argumentsObject = {}) {
2583
- if (!localWalletStore) {
2584
- return errorContent(
2585
- "apiosk_get_started is only available in the local stdio package because it saves wallet and connect-token state on the local machine."
2586
- );
2587
- }
2588
-
2589
- let savedConfig = await getSavedConfig();
2590
- let savedConnectConfig = summarizeSavedConnectConfig(savedConfig, env);
2591
- let createdWallet = null;
2592
- let selectedWallet = null;
2593
-
2594
- if (trimString(argumentsObject.connect_string)) {
2595
- const parsed = parseConnectString(argumentsObject.connect_string);
2596
- await saveLocalApioskConfig(
2597
- {
2598
- gateway_url: parsed.gateway_url,
2599
- chain_id: parsed.chain_id,
2600
- wallet_address: parsed.wallet_address,
2601
- connect_token: parsed.connect_token,
2602
- connect_authorization: parsed.connect_authorization,
2603
- connect_header_name: parsed.connect_header_name,
2604
- daily_limit_usdc: parsed.daily_limit_usdc,
2605
- per_request_limit_usdc: parsed.per_request_limit_usdc,
2606
- },
2607
- env
2608
- );
2609
- savedConfig = await getSavedConfig();
2610
- savedConnectConfig = summarizeSavedConnectConfig(savedConfig, env);
2611
- } else if (
2612
- trimString(argumentsObject.connect_token) ||
2613
- trimString(argumentsObject.connect_authorization)
2614
- ) {
2615
- await saveLocalApioskConfig(
2616
- {
2617
- gateway_url: resolveGatewayBaseUrl(env, savedConfig),
2618
- chain_id: savedConfig?.chain_id || 8453,
2619
- wallet_address:
2620
- trimString(argumentsObject.wallet_address) ||
2621
- trimString(savedConfig?.agent_wallet_address),
2622
- connect_token:
2623
- trimString(argumentsObject.connect_token) || trimString(savedConfig?.connect_token),
2624
- connect_authorization:
2625
- trimString(argumentsObject.connect_authorization) ||
2626
- trimString(savedConfig?.connect_authorization),
2627
- connect_header_name:
2628
- trimString(argumentsObject.connect_header_name) ||
2629
- trimString(savedConfig?.connect_header_name) ||
2630
- "X-Apiosk-Connect-Token",
2631
- daily_limit_usdc: savedConfig?.daily_limit_usdc,
2632
- per_request_limit_usdc: savedConfig?.per_request_limit_usdc,
2633
- },
2634
- env
2635
- );
2636
- savedConfig = await getSavedConfig();
2637
- savedConnectConfig = summarizeSavedConnectConfig(savedConfig, env);
2638
- }
2639
-
2640
- if (trimString(argumentsObject.wallet_id)) {
2641
- const selected = await localWalletStore.selectWallet(trimString(argumentsObject.wallet_id));
2642
- selectedWallet = selected.wallet;
2643
- }
2644
-
2645
- let activeWallet = await resolveConfigurationWallet(argumentsObject.wallet_id);
2646
- let authState = buildAuthState({ env, savedConfig, activeWallet });
2647
-
2648
- if (authState.mode === "none" && argumentsObject.create_wallet !== false) {
2649
- const created = await localWalletStore.createWallet({
2650
- label: trimString(argumentsObject.wallet_label) || "Apiosk wallet",
2651
- set_active: true,
2652
- });
2653
- createdWallet = created.wallet;
2654
- activeWallet = await resolveConfigurationWallet(created.wallet.id);
2655
- authState = buildAuthState({ env, savedConfig, activeWallet });
2656
- }
2657
-
2658
- if (authState.mode === "none") {
2659
- return errorContent({
2660
- error:
2661
- "No Apiosk payment or access method is configured yet. Provide a connect string or allow apiosk_get_started to create a local wallet.",
2662
- local_config_paths: createApioskLocalConfigPaths(env),
2663
- next_steps: [
2664
- "Call apiosk_get_started with connect_string from the dashboard, or set create_wallet=true.",
2665
- "If you already manage auth outside the MCP package, set APIOSK_PRIVATE_KEY or APIOSK_CONNECT_TOKEN in the MCP environment.",
2666
- ],
2667
- });
2668
- }
2669
-
2670
- const client = await getClient();
2671
- const discoveryResponse = await client.listApis({ limit: 5, offset: 0 });
2672
- await getTools(true);
2673
-
2674
- const discoveredApis = (discoveryResponse.apis || []).map((api) =>
2675
- buildCatalogEntry(api, cache.toolNamesBySlug.get(api.slug) || null)
2676
- );
2677
- const testSlug =
2678
- trimString(argumentsObject.test_slug) ||
2679
- discoveredApis.find((api) => api.slug)?.slug ||
2680
- null;
2681
- const testInput =
2682
- Object.prototype.hasOwnProperty.call(argumentsObject, "test_input") ?
2683
- argumentsObject.test_input :
2684
- {};
2685
-
2686
- let metadata = null;
2687
- if (testSlug) {
2688
- metadata = await client.getMetadata(testSlug).catch((error) => ({
2689
- error: error instanceof Error ? error.message : String(error),
2690
- }));
2691
- }
2692
-
2693
- let status = "configured";
2694
- let summary =
2695
- "Apiosk is configured locally. Discovery worked, but the test execute call still needs a specific slug or input payload.";
2696
- let executeResult = null;
2697
- let executeError = null;
2698
- let configure = null;
2699
-
2700
- if (testSlug) {
2701
- try {
2702
- executeResult = await client.execute(testSlug, testInput, {
2703
- operation: argumentsObject.test_operation,
2704
- });
2705
- status = "ready";
2706
- summary = `Apiosk is configured and the test call to ${testSlug} succeeded.`;
2707
- } catch (error) {
2708
- if (error instanceof ApioskPaymentRequiredError) {
2709
- status = "needs_funding";
2710
- summary =
2711
- "Apiosk is configured, but the first paid execute call still needs wallet funding or a managed wallet with spend available.";
2712
- executeError = {
2713
- error: error.message,
2714
- payment_required: error.paymentRequired,
2715
- };
2716
- if (activeWallet?.source === "local_store" && activeWallet?.id) {
2717
- configure = await buildConfigurePayload({
2718
- wallet_id: activeWallet.id,
2719
- section: "funding",
2720
- include_qr_data_url: argumentsObject.include_qr_data_url,
2721
- });
2722
- }
2723
- } else {
2724
- executeError = {
2725
- error: error instanceof Error ? error.message : String(error),
2726
- };
2727
- }
2728
- }
2729
- }
2730
-
2731
- if (!configure && activeWallet?.source === "local_store" && activeWallet?.id) {
2732
- configure = await buildConfigurePayload({
2733
- wallet_id: activeWallet.id,
2734
- section: "overview",
2735
- include_qr_data_url: argumentsObject.include_qr_data_url,
2736
- });
2737
- }
2738
-
2739
- const nextSteps = [];
2740
- if (status === "ready") {
2741
- nextSteps.push(
2742
- "Call the API-specific dynamic tool from discovery, or use apiosk_execute with a different slug.",
2743
- "Use apiosk_publish_api when you want to list your own API behind the gateway."
2744
- );
2745
- } else if (status === "needs_funding") {
2746
- nextSteps.push(
2747
- "Fund the selected local wallet, then rerun apiosk_get_started or retry the API-specific tool.",
2748
- "If you prefer managed access, import a dashboard connect string into apiosk_get_started instead of using a local wallet."
2749
- );
2750
- } else {
2751
- nextSteps.push(
2752
- "Provide test_slug and test_input to apiosk_get_started if you want a more representative first execute call.",
2753
- "Use apiosk_get_api on one of the discovered slugs to inspect its metadata and input shape before retrying."
2754
- );
2755
- }
2756
-
2757
- return content({
2758
- status,
2759
- summary,
2760
- auth: {
2761
- ...authState,
2762
- saved_connect_config: savedConnectConfig,
2763
- local_config_paths: createApioskLocalConfigPaths(env),
2764
- },
2765
- setup: {
2766
- created_wallet: createdWallet,
2767
- selected_wallet: selectedWallet,
2768
- active_wallet: activeWallet,
2769
- },
2770
- discovery: {
2771
- apis: discoveredApis,
2772
- meta: discoveryResponse.meta || null,
2773
- },
2774
- test: {
2775
- slug: testSlug,
2776
- operation: trimString(argumentsObject.test_operation) || null,
2777
- input: testInput,
2778
- metadata,
2779
- result: executeResult,
2780
- error: executeError,
2781
- },
2782
- configure,
2783
- next_steps: nextSteps,
2784
- });
2785
- }
2786
-
2787
- async function handleLocalWalletSelect(argumentsObject = {}) {
2788
- if (!localWalletStore) {
2789
- return errorContent("Local wallet tools are not enabled in this server mode.");
2790
- }
2791
- if (!argumentsObject.wallet_id) {
2792
- return errorContent("Missing required field: wallet_id");
2793
- }
2794
-
2795
- const selected = await localWalletStore.selectWallet(argumentsObject.wallet_id);
2796
- selected.configure = await buildConfigurePayload({
2797
- wallet_id: argumentsObject.wallet_id,
2798
- section: "overview",
2799
- include_qr_data_url: argumentsObject.include_qr_data_url,
2800
- });
2801
- return content(selected);
2802
- }
2803
-
2804
- async function handleLocalWalletUpdate(argumentsObject = {}) {
2805
- if (!localWalletStore) {
2806
- return errorContent("Local wallet tools are not enabled in this server mode.");
2807
- }
2808
- if (!argumentsObject.wallet_id) {
2809
- return errorContent("Missing required field: wallet_id");
2810
- }
2811
-
2812
- return content(
2813
- await localWalletStore.updateWallet(argumentsObject.wallet_id, {
2814
- label: argumentsObject.label,
2815
- set_active: argumentsObject.set_active,
2816
- })
2817
- );
2818
- }
2819
-
2820
- async function handleLocalWalletDelete(argumentsObject = {}) {
2821
- if (!localWalletStore) {
2822
- return errorContent("Local wallet tools are not enabled in this server mode.");
2823
- }
2824
- if (!argumentsObject.wallet_id) {
2825
- return errorContent("Missing required field: wallet_id");
2826
- }
2827
-
2828
- return content(await localWalletStore.deleteWallet(argumentsObject.wallet_id));
2829
- }
2830
-
2831
- async function handleLocalWalletReveal(argumentsObject = {}) {
2832
- if (!localWalletStore) {
2833
- return errorContent("Local wallet tools are not enabled in this server mode.");
2834
- }
2835
- if (!argumentsObject.wallet_id) {
2836
- return errorContent("Missing required field: wallet_id");
2837
- }
2838
-
2839
- return content(await localWalletStore.revealSecret(argumentsObject.wallet_id));
2840
- }
2841
-
2842
- async function handleLocalWalletSave(argumentsObject = {}) {
2843
- if (!localWalletStore) {
2844
- return errorContent("Local wallet tools are not enabled in this server mode.");
2845
- }
2846
- if (!argumentsObject.wallet_id) {
2847
- return errorContent("Missing required field: wallet_id");
2848
- }
2849
-
2850
- return content(
2851
- await localWalletStore.saveSecret(argumentsObject.wallet_id, {
2852
- path: argumentsObject.path,
2853
- format: argumentsObject.format,
2854
- })
2855
- );
2856
- }
2857
-
2858
- async function handlePublishApi(argumentsObject = {}) {
2859
- const wallet = await resolvePublishingWallet(argumentsObject.wallet_id);
2860
- const slug = trimString(argumentsObject.slug);
2861
- const endpointUrl = trimString(argumentsObject.endpoint_url);
2862
- const priceUsd = Number(argumentsObject.price_usd);
2863
- const name = trimString(argumentsObject.name);
2864
- const description = trimString(argumentsObject.description);
2865
-
2866
- if (!name || !slug || !endpointUrl || !description || !Number.isFinite(priceUsd)) {
2867
- return errorContent(
2868
- "Missing required publish fields. Expected name, slug, endpoint_url, price_usd, and description."
2869
- );
2870
- }
2871
-
2872
- if (!validateSlug(slug)) {
2873
- return errorContent("Slug must use lowercase letters, numbers, and hyphens only.");
2874
- }
2875
-
2876
- if (!endpointUrl.startsWith("https://")) {
2877
- return errorContent("endpoint_url must start with https://");
2878
- }
2879
-
2880
- const category = resolveCategory(argumentsObject.category, argumentsObject.listing_group);
2881
- const payload = {
2882
- name,
2883
- slug,
2884
- endpoint_url: endpointUrl,
2885
- price_usd: priceUsd,
2886
- description,
2887
- owner_wallet: wallet.address,
2888
- category,
2889
- listing_metadata: buildListingMetadata({
2890
- name,
2891
- slug,
2892
- description,
2893
- endpoint_url: endpointUrl,
2894
- price_usd: priceUsd,
2895
- category,
2896
- listing_group: argumentsObject.listing_group,
2897
- listing_metadata: argumentsObject.listing_metadata,
2898
- }),
2899
- };
2900
-
2901
- const response = await requestGatewayManagement({
2902
- baseUrl: resolveGatewayBaseUrl(env),
2903
- path: "/v1/apis/register",
2904
- method: "POST",
2905
- body: payload,
2906
- action: "register_api",
2907
- resource: `register:${slug}`,
2908
- wallet,
2909
- });
2910
-
2911
- cache.expiresAt = 0;
2912
-
2913
- return content({
2914
- ...response,
2915
- wallet: {
2916
- address: wallet.address,
2917
- source: wallet.source,
2918
- },
2919
- });
2920
- }
2921
-
2922
- async function handleListMyApis(argumentsObject = {}) {
2923
- const wallet = await resolvePublishingWallet(argumentsObject.wallet_id);
2924
- const response = await requestGatewayManagement({
2925
- baseUrl: resolveGatewayBaseUrl(env),
2926
- path: `/v1/apis/mine?wallet=${encodeURIComponent(wallet.address)}`,
2927
- method: "GET",
2928
- action: "my_apis",
2929
- resource: `mine:${wallet.address}`,
2930
- wallet,
2931
- });
2932
-
2933
- return content({
2934
- wallet: {
2935
- address: wallet.address,
2936
- source: wallet.source,
2937
- },
2938
- ...response,
2939
- });
2940
- }
2941
-
2942
- async function handleUpdateApi(argumentsObject = {}) {
2943
- const wallet = await resolvePublishingWallet(argumentsObject.wallet_id);
2944
- const slug = trimString(argumentsObject.slug);
2945
-
2946
- if (!slug) {
2947
- return errorContent("Missing required field: slug");
2948
- }
2949
-
2950
- const body = {
2951
- owner_wallet: wallet.address,
2952
- };
2953
-
2954
- if (argumentsObject.endpoint_url !== undefined) {
2955
- const endpointUrl = trimString(argumentsObject.endpoint_url);
2956
- if (!endpointUrl.startsWith("https://")) {
2957
- return errorContent("endpoint_url must start with https://");
2958
- }
2959
- body.endpoint_url = endpointUrl;
2960
- }
2961
-
2962
- if (argumentsObject.price_usd !== undefined) {
2963
- const priceUsd = Number(argumentsObject.price_usd);
2964
- if (!Number.isFinite(priceUsd)) {
2965
- return errorContent("price_usd must be a valid number");
2966
- }
2967
- body.price_usd = priceUsd;
2968
- }
2969
-
2970
- if (argumentsObject.description !== undefined) {
2971
- body.description = trimString(argumentsObject.description);
2972
- }
2973
-
2974
- if (argumentsObject.active !== undefined) {
2975
- body.active = Boolean(argumentsObject.active);
2976
- }
2977
-
2978
- if (argumentsObject.listing_metadata !== undefined) {
2979
- body.listing_metadata = argumentsObject.listing_metadata;
2980
- }
2981
-
2982
- const response = await requestGatewayManagement({
2983
- baseUrl: resolveGatewayBaseUrl(env),
2984
- path: `/v1/apis/${encodeURIComponent(slug)}`,
2985
- method: "POST",
2986
- body,
2987
- action: "update_api",
2988
- resource: `update:${slug}`,
2989
- wallet,
2990
- });
2991
-
2992
- cache.expiresAt = 0;
2993
-
2994
- return content({
2995
- slug,
2996
- wallet: {
2997
- address: wallet.address,
2998
- source: wallet.source,
2999
- },
3000
- ...response,
3001
- });
3002
- }
3003
-
3004
- async function handleDeleteApi(argumentsObject = {}) {
3005
- const wallet = await resolvePublishingWallet(argumentsObject.wallet_id);
3006
- const slug = trimString(argumentsObject.slug);
3007
-
3008
- if (!slug) {
3009
- return errorContent("Missing required field: slug");
3010
- }
3011
-
3012
- const response = await requestGatewayManagement({
3013
- baseUrl: resolveGatewayBaseUrl(env),
3014
- path: `/v1/apis/${encodeURIComponent(slug)}?wallet=${encodeURIComponent(wallet.address)}`,
3015
- method: "DELETE",
3016
- action: "delete_api",
3017
- resource: `delete:${slug}`,
3018
- wallet,
3019
- });
3020
-
3021
- cache.expiresAt = 0;
3022
-
3023
- return content({
3024
- slug,
3025
- wallet: {
3026
- address: wallet.address,
3027
- source: wallet.source,
3028
- },
3029
- ...response,
3030
- });
3031
- }
3032
-
3033
- async function callTool(name, argumentsObject = {}, authInfo = null) {
3034
- try {
3035
- if (name === "apiosk_help") return await handleHelp(argumentsObject);
3036
- if (name === "apiosk_payment_guide") return await handlePaymentGuide(argumentsObject, authInfo);
3037
- if (name === "apiosk_explore") return await handleExplore(argumentsObject, authInfo);
3038
- if (name === "apiosk_search") return await handleSearch(argumentsObject, authInfo);
3039
- if (name === "apiosk_get_api" || name === "apiosk_metadata") {
3040
- return await handleGetApi(argumentsObject, authInfo);
3041
- }
3042
- if (name === "apiosk_execute") return await handleExecute(argumentsObject, authInfo);
3043
- if (name === "apiosk_health") return await handleHealth(argumentsObject, authInfo);
3044
-
3045
- if (name === "apiosk_wallet_list") return await handleLocalWalletList();
3046
- if (name === "apiosk_wallet_create") return await handleLocalWalletCreate(argumentsObject);
3047
- if (name === "apiosk_get_started") return await handleGetStarted(argumentsObject);
3048
- if (name === "apiosk_configure") return await handleConfigure(argumentsObject);
3049
- if (name === "apiosk_show_wallet_funding")
3050
- return await handleShowWalletFunding(argumentsObject);
3051
- if (name === "apiosk_wallet_select") return await handleLocalWalletSelect(argumentsObject);
3052
- if (name === "apiosk_wallet_update") return await handleLocalWalletUpdate(argumentsObject);
3053
- if (name === "apiosk_wallet_delete") return await handleLocalWalletDelete(argumentsObject);
3054
- if (name === "apiosk_wallet_reveal_secret") return await handleLocalWalletReveal(argumentsObject);
3055
- if (name === "apiosk_wallet_save_secret") return await handleLocalWalletSave(argumentsObject);
3056
-
3057
- if (name === "apiosk_publish_api") return await handlePublishApi(argumentsObject);
3058
- if (name === "apiosk_list_my_apis") return await handleListMyApis(argumentsObject);
3059
- if (name === "apiosk_update_api") return await handleUpdateApi(argumentsObject);
3060
- if (name === "apiosk_delete_api") return await handleDeleteApi(argumentsObject);
3061
-
3062
- if (name === "apiosk_create_account") return await handleCreateAccount(argumentsObject);
3063
- if (name === "apiosk_sign_in") return await handleSignIn(argumentsObject);
3064
- if (name === "apiosk_list_wallets") return await handleWalletList(authInfo);
3065
- if (name === "apiosk_create_wallet") return await handleWalletCreate(argumentsObject, authInfo);
3066
- if (name === "apiosk_update_wallet") return await handleWalletUpdate(argumentsObject, authInfo);
3067
- if (name === "apiosk_delete_wallet") return await handleWalletDelete(argumentsObject, authInfo);
3068
- if (name === "apiosk_get_wallet_activity") return await handleWalletActivity(argumentsObject, authInfo);
3069
- if (name === "apiosk_create_wallet_connect_string") return await handleWalletConnectString(argumentsObject, authInfo);
3070
- if (name === "apiosk_list_wallet_api_keys") return await handleWalletApiKeys(argumentsObject, authInfo);
3071
- if (name === "apiosk_create_wallet_api_key") return await handleWalletApiKeyCreate(argumentsObject, authInfo);
3072
- if (name === "apiosk_update_wallet_api_key") return await handleWalletApiKeyUpdate(argumentsObject, authInfo);
3073
- if (name === "apiosk_delete_wallet_api_key") return await handleWalletApiKeyDelete(argumentsObject, authInfo);
3074
-
3075
- const tool = await resolveDynamicTool(name, authInfo);
3076
- if (!tool) {
3077
- return errorContent(`Unknown Apiosk tool: ${name}`);
3078
- }
3079
-
3080
- return await handleDynamicExecute(tool, argumentsObject, authInfo);
3081
- } catch (error) {
3082
- if (error instanceof ApioskPaymentRequiredError) {
3083
- return errorContent({
3084
- error: error.message,
3085
- hint:
3086
- "Run apiosk_get_started in the local stdio package, or configure APIOSK_PRIVATE_KEY, to enable automatic x402 settlement.",
3087
- payment_required: error.paymentRequired,
3088
- });
3089
- }
3090
-
3091
- return errorContent({
3092
- error: error instanceof Error ? error.message : String(error),
3093
- });
3094
- }
3095
- }
3096
-
3097
- return {
3098
- listTools: (authInfo = null) => getTools(false, authInfo),
3099
- isToolProtected,
3100
- callTool,
3101
- };
33
+ // A copy, never a mutation: the caller's env (often process.env) is not ours.
34
+ return createV2Runtime({ ...options, env: { ...env, APIOSK_GATEWAY_V2_URL: resolveGatewayV2Url(env) } });
3102
35
  }