@apiosk/mcp 1.7.0 → 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.
- package/README.md +83 -612
- package/assets/brand/apiosk-a-20260921.png +0 -0
- package/assets/brand/apple-touch-icon.png +0 -0
- package/assets/brand/favicon-dark.ico +0 -0
- package/assets/brand/favicon-light.ico +0 -0
- package/assets/brand/favicon.ico +0 -0
- package/assets/brand/icon-192.png +0 -0
- package/assets/brand/icon-512.png +0 -0
- package/assets/brand/icon-maskable-512.png +0 -0
- package/assets/brand/inter-OFL.txt +93 -0
- package/assets/brand/inter-latin-400-normal.woff2 +0 -0
- package/assets/brand/inter-latin-500-normal.woff2 +0 -0
- package/assets/brand/inter-latin-600-normal.woff2 +0 -0
- package/assets/brand/mark-20260905-transparent.svg +1 -0
- package/assets/brand/mark-20260918.svg +1 -0
- package/assets/brand/mark-black-20260918.svg +1 -0
- package/assets/brand/mark-dark-20260905-transparent.png +0 -0
- package/assets/brand/mark-dark-20260918.png +0 -0
- package/assets/brand/mark-dark-96.png +0 -0
- package/assets/brand/mark-light-20260905-transparent.png +0 -0
- package/assets/brand/mark-light-20260918.png +0 -0
- package/assets/brand/mark-light-96.png +0 -0
- package/assets/brand/wordmark-black-320.png +0 -0
- package/assets/brand/wordmark-white-320.png +0 -0
- package/docs/branding.md +11 -0
- package/docs/marketplace-submission-2026-04-08.md +4 -0
- package/docs/marketplace-submission-2026-08-20.md +157 -0
- package/docs/openai-plugin-submission-2026-09-05.md +143 -0
- package/dxt.json +26 -22
- package/index.mjs +3 -1
- package/logo-optimized-light.png +0 -0
- package/package.json +14 -14
- package/plugin/apiosk/.codex-plugin/plugin.json +45 -0
- package/plugin/apiosk/.mcp.json +8 -0
- package/plugin/apiosk/assets/icon-dark.png +0 -0
- package/plugin/apiosk/assets/icon.png +0 -0
- package/plugin/apiosk/assets/icon.svg +1 -0
- package/plugin/apiosk/assets/logo.png +0 -0
- package/plugin/apiosk/skills/apiosk/SKILL.md +33 -0
- package/plugin/apiosk/skills/apiosk/agents/openai.yaml +12 -0
- package/server.json +58 -24
- package/server.mjs +136 -151
- package/src/approval-feedback.mjs +21 -0
- package/src/brand-routes.mjs +28 -0
- package/src/create-server.mjs +160 -46
- package/src/display-money.mjs +20 -0
- package/src/display-text.mjs +67 -0
- package/src/gateway-client.mjs +42 -0
- package/src/gateway-v2-ask.mjs +50 -0
- package/src/gateway-v2-card-account.mjs +23 -0
- package/src/gateway-v2-card-actions.mjs +67 -0
- package/src/gateway-v2-card-answer-text.mjs +130 -0
- package/src/gateway-v2-card-answer.mjs +68 -0
- package/src/gateway-v2-card-blocks.mjs +166 -0
- package/src/gateway-v2-card-body.mjs +259 -0
- package/src/gateway-v2-card-budget.mjs +21 -0
- package/src/gateway-v2-card-cbs.mjs +56 -0
- package/src/gateway-v2-card-choices.mjs +4 -0
- package/src/gateway-v2-card-clarification.mjs +25 -0
- package/src/gateway-v2-card-compact.mjs +34 -0
- package/src/gateway-v2-card-events.mjs +41 -0
- package/src/gateway-v2-card-presentation.mjs +125 -0
- package/src/gateway-v2-card-research.mjs +63 -0
- package/src/gateway-v2-card-result.mjs +32 -0
- package/src/gateway-v2-card-search.mjs +39 -0
- package/src/gateway-v2-card-sources.mjs +32 -0
- package/src/gateway-v2-card-style.mjs +76 -0
- package/src/gateway-v2-card-verdict.mjs +66 -0
- package/src/gateway-v2-card.mjs +91 -0
- package/src/gateway-v2-contracts.json +66 -0
- package/src/gateway-v2-instructions.md +113 -0
- package/src/gateway-v2-recovery.mjs +18 -0
- package/src/gateway-v2-report-links.mjs +14 -0
- package/src/gateway-v2-workflows.mjs +17 -0
- package/src/gateway-v2.mjs +179 -0
- package/src/oauth.mjs +402 -1949
- package/src/observability.mjs +19 -3
- package/src/result-presentation.mjs +9 -0
- package/src/runtime.mjs +24 -3670
- package/src/source-groups.mjs +91 -0
- package/src/source-value-format.mjs +25 -0
- package/src/tool-result.mjs +20 -0
- package/src/ui-bridge.mjs +185 -0
- package/src/well-known-routes.mjs +132 -0
- package/src/assets/wallet-accounts.mjs +0 -20513
- package/src/assets/walletconnect-provider.mjs +0 -6319
- package/src/discovery.mjs +0 -929
- package/src/external-fetch.mjs +0 -203
- package/src/funding-options.mjs +0 -255
- package/src/gateway-management.mjs +0 -107
- package/src/hosted-payment.mjs +0 -552
- package/src/hosted-wallets.mjs +0 -530
- package/src/listing-metadata.mjs +0 -287
- package/src/local-config.mjs +0 -256
- package/src/payment-guidance.mjs +0 -298
- package/src/publisher.mjs +0 -1288
- package/src/result-canvas.mjs +0 -16
- package/src/source-registry.mjs +0 -215
- package/src/wallet-store.mjs +0 -476
- package/src/x402-inspect.mjs +0 -361
package/src/runtime.mjs
CHANGED
|
@@ -1,3681 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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 {
|
|
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";
|
|
20
|
-
import { logToolCall } from "./observability.mjs";
|
|
21
|
-
import {
|
|
22
|
-
hostedCreateWalletToken,
|
|
23
|
-
hostedCreateWalletUnavailable,
|
|
24
|
-
hostedDeleteWallet,
|
|
25
|
-
hostedDeleteWalletToken,
|
|
26
|
-
hostedListWallets,
|
|
27
|
-
hostedListWalletTokens,
|
|
28
|
-
hostedUpdateWallet,
|
|
29
|
-
hostedUpdateWalletToken,
|
|
30
|
-
hostedWalletActivity,
|
|
31
|
-
} from "./hosted-wallets.mjs";
|
|
32
|
-
import {
|
|
33
|
-
PUBLISHER_TOOLS,
|
|
34
|
-
handlePublisherTool,
|
|
35
|
-
isPublisherTool,
|
|
36
|
-
} from "./publisher.mjs";
|
|
37
|
-
import { DISCOVER_TOOL, runDiscover } from "./discovery.mjs";
|
|
38
|
-
import { searchKnownSources } from "./source-registry.mjs";
|
|
39
|
-
import { INSPECT_TOOL, runInspect } from "./x402-inspect.mjs";
|
|
40
|
-
import { FETCH_PAID_TOOL, runFetchPaid } from "./external-fetch.mjs";
|
|
14
|
+
import { createV2Runtime } from "./gateway-v2.mjs";
|
|
41
15
|
|
|
42
|
-
const
|
|
43
|
-
const CACHE_TTL_MS = 60_000;
|
|
44
|
-
const DEFAULT_GATEWAY_BASE_URL = "https://gateway.apiosk.com";
|
|
45
|
-
const HOSTED_OAUTH_SCOPE = "mcp:tools";
|
|
16
|
+
export const DEFAULT_GATEWAY_V2_URL = "https://gateway.apiosk.com";
|
|
46
17
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// Apiosk tool results are always returned as a JSON object in structuredContent
|
|
51
|
-
// (see content()), so an open object schema both documents that contract and
|
|
52
|
-
// validates every success payload without risking false rejections on the
|
|
53
|
-
// varied per-tool result shapes. Individual tools can still ship a tighter
|
|
54
|
-
// schema (e.g. dynamic listings via listing_metadata.mcp_tool.outputSchema),
|
|
55
|
-
// which is preserved as-is.
|
|
56
|
-
const DEFAULT_TOOL_OUTPUT_SCHEMA = {
|
|
57
|
-
type: "object",
|
|
58
|
-
additionalProperties: true,
|
|
59
|
-
description:
|
|
60
|
-
"Structured JSON result of the tool call. Mirrors the human-readable text content; the exact fields depend on the tool (an `error` field is present when the call fails).",
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// Behaviour hints (MCP tool annotations). Clients like ChatGPT default an
|
|
64
|
-
// un-annotated tool to the most cautious badges — "destructive" and
|
|
65
|
-
// "open world" — so every write tool that ships no annotations reads as
|
|
66
|
-
// dangerous. These presets make the badges accurate: wallet/account/keystore
|
|
67
|
-
// management is a closed domain (the user's own Apiosk account), publishing
|
|
68
|
-
// wires up an external upstream (open world), and deletes are destructive.
|
|
69
|
-
const READ_ONLY_ANNOTATIONS = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
70
|
-
const CREATE_ANNOTATIONS = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
|
|
71
|
-
const UPDATE_ANNOTATIONS = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
72
|
-
const DELETE_ANNOTATIONS = { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false };
|
|
73
|
-
const SETUP_ANNOTATIONS = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true };
|
|
74
|
-
const PUBLISH_ANNOTATIONS = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true };
|
|
75
|
-
const PUBLISH_UPDATE_ANNOTATIONS = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true };
|
|
76
|
-
const PUBLISH_DELETE_ANNOTATIONS = { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true };
|
|
77
|
-
// Dynamic per-listing tools call arbitrary paid third-party APIs and spend
|
|
78
|
-
// USDC, so an unspecified listing gets the same hints as apiosk_execute.
|
|
79
|
-
const DYNAMIC_EXECUTE_ANNOTATIONS = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true };
|
|
80
|
-
|
|
81
|
-
// Static tools whose literals don't already declare `annotations`. Discovery
|
|
82
|
-
// and publisher (sk_live_) tools annotate themselves inline and are absent here.
|
|
83
|
-
const TOOL_ANNOTATIONS = {
|
|
84
|
-
// Managed (dashboard) wallet + account tools — closed Apiosk-account domain.
|
|
85
|
-
apiosk_list_wallets: READ_ONLY_ANNOTATIONS,
|
|
86
|
-
apiosk_get_wallet_activity: READ_ONLY_ANNOTATIONS,
|
|
87
|
-
apiosk_list_wallet_api_keys: READ_ONLY_ANNOTATIONS,
|
|
88
|
-
apiosk_show_wallet_funding: READ_ONLY_ANNOTATIONS,
|
|
89
|
-
apiosk_create_wallet: CREATE_ANNOTATIONS,
|
|
90
|
-
apiosk_create_wallet_connect_string: CREATE_ANNOTATIONS,
|
|
91
|
-
apiosk_create_wallet_api_key: CREATE_ANNOTATIONS,
|
|
92
|
-
apiosk_create_account: CREATE_ANNOTATIONS,
|
|
93
|
-
apiosk_update_wallet: UPDATE_ANNOTATIONS,
|
|
94
|
-
apiosk_update_wallet_api_key: UPDATE_ANNOTATIONS,
|
|
95
|
-
apiosk_sign_in: UPDATE_ANNOTATIONS,
|
|
96
|
-
apiosk_delete_wallet: DELETE_ANNOTATIONS,
|
|
97
|
-
apiosk_delete_wallet_api_key: DELETE_ANNOTATIONS,
|
|
98
|
-
// Local (stdio) wallet keystore tools.
|
|
99
|
-
apiosk_wallet_list: READ_ONLY_ANNOTATIONS,
|
|
100
|
-
apiosk_wallet_reveal_secret: READ_ONLY_ANNOTATIONS,
|
|
101
|
-
apiosk_configure: READ_ONLY_ANNOTATIONS,
|
|
102
|
-
apiosk_wallet_create: CREATE_ANNOTATIONS,
|
|
103
|
-
apiosk_wallet_select: UPDATE_ANNOTATIONS,
|
|
104
|
-
apiosk_wallet_update: UPDATE_ANNOTATIONS,
|
|
105
|
-
apiosk_wallet_save_secret: UPDATE_ANNOTATIONS,
|
|
106
|
-
apiosk_wallet_delete: DELETE_ANNOTATIONS,
|
|
107
|
-
apiosk_get_started: SETUP_ANNOTATIONS,
|
|
108
|
-
// Wallet-signed publishing — registers an external upstream (open world).
|
|
109
|
-
apiosk_list_my_apis: READ_ONLY_ANNOTATIONS,
|
|
110
|
-
apiosk_publish_api: PUBLISH_ANNOTATIONS,
|
|
111
|
-
apiosk_update_api: PUBLISH_UPDATE_ANNOTATIONS,
|
|
112
|
-
apiosk_delete_api: PUBLISH_DELETE_ANNOTATIONS,
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
// Applied at the single list-emission boundary (runtime.listTools): fills in a
|
|
116
|
-
// default output schema and behaviour annotations for any tool that doesn't
|
|
117
|
-
// already declare its own, so clients stop flagging "output schema recommended"
|
|
118
|
-
// and render accurate read-only/destructive/open-world badges.
|
|
119
|
-
function normalizeToolForClient(tool, { hosted = false, protectedTool = false } = {}) {
|
|
120
|
-
if (!tool || typeof tool !== "object") return tool;
|
|
121
|
-
let next = tool;
|
|
122
|
-
if (!next.outputSchema) {
|
|
123
|
-
next = { ...next, outputSchema: DEFAULT_TOOL_OUTPUT_SCHEMA };
|
|
124
|
-
}
|
|
125
|
-
if (!next.annotations && TOOL_ANNOTATIONS[next.name]) {
|
|
126
|
-
next = { ...next, annotations: TOOL_ANNOTATIONS[next.name] };
|
|
127
|
-
}
|
|
128
|
-
if (hosted && !next.securitySchemes) {
|
|
129
|
-
const securitySchemes = protectedTool
|
|
130
|
-
? [{ type: "oauth2", scopes: [HOSTED_OAUTH_SCOPE] }]
|
|
131
|
-
: [{ type: "noauth" }];
|
|
132
|
-
next = {
|
|
133
|
-
...next,
|
|
134
|
-
securitySchemes,
|
|
135
|
-
_meta: {
|
|
136
|
-
...(next._meta || {}),
|
|
137
|
-
// Back-compat mirror required by older ChatGPT connector clients.
|
|
138
|
-
securitySchemes,
|
|
139
|
-
},
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
return next;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const DASHBOARD_WALLET_TOOLS = [
|
|
146
|
-
{
|
|
147
|
-
name: "apiosk_list_wallets",
|
|
148
|
-
description: "List the signed-in user's managed Apiosk wallets. Requires an Apiosk dashboard session from local env auth or hosted MCP authorization.",
|
|
149
|
-
inputSchema: {
|
|
150
|
-
type: "object",
|
|
151
|
-
additionalProperties: false,
|
|
152
|
-
properties: {},
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
name: "apiosk_create_wallet",
|
|
157
|
-
description: "Create or import a managed Apiosk wallet for the signed-in user.",
|
|
158
|
-
inputSchema: {
|
|
159
|
-
type: "object",
|
|
160
|
-
required: ["label"],
|
|
161
|
-
properties: {
|
|
162
|
-
label: { type: "string" },
|
|
163
|
-
mode: {
|
|
164
|
-
type: "string",
|
|
165
|
-
enum: ["create", "import_private_key", "import_phrase"],
|
|
166
|
-
},
|
|
167
|
-
secret: {
|
|
168
|
-
type: "string",
|
|
169
|
-
description: "Private key or recovery phrase when importing.",
|
|
170
|
-
},
|
|
171
|
-
daily_limit_usdc: { type: "number" },
|
|
172
|
-
per_tx_limit_usdc: { type: "number" },
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
name: "apiosk_update_wallet",
|
|
178
|
-
description: "Update wallet label, status, display metadata, or spending limits.",
|
|
179
|
-
inputSchema: {
|
|
180
|
-
type: "object",
|
|
181
|
-
required: ["wallet_id"],
|
|
182
|
-
properties: {
|
|
183
|
-
wallet_id: { type: "string" },
|
|
184
|
-
label: { type: "string" },
|
|
185
|
-
status: {
|
|
186
|
-
type: "string",
|
|
187
|
-
enum: ["active", "paused", "revoked"],
|
|
188
|
-
},
|
|
189
|
-
daily_limit_usdc: { type: "number" },
|
|
190
|
-
per_tx_limit_usdc: { type: "number" },
|
|
191
|
-
color: { type: "string" },
|
|
192
|
-
icon: { type: "string" },
|
|
193
|
-
},
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
name: "apiosk_delete_wallet",
|
|
198
|
-
description: "Delete one of the signed-in user's wallets.",
|
|
199
|
-
inputSchema: {
|
|
200
|
-
type: "object",
|
|
201
|
-
required: ["wallet_id"],
|
|
202
|
-
properties: {
|
|
203
|
-
wallet_id: { type: "string" },
|
|
204
|
-
},
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
name: "apiosk_get_wallet_activity",
|
|
209
|
-
description: "Fetch recent transactions and activity for one managed wallet.",
|
|
210
|
-
inputSchema: {
|
|
211
|
-
type: "object",
|
|
212
|
-
required: ["wallet_id"],
|
|
213
|
-
properties: {
|
|
214
|
-
wallet_id: { type: "string" },
|
|
215
|
-
page: { type: "number" },
|
|
216
|
-
limit: { type: "number" },
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
name: "apiosk_create_wallet_connect_string",
|
|
222
|
-
description: "Rotate or create a connect token for a managed wallet and return the new connect string.",
|
|
223
|
-
inputSchema: {
|
|
224
|
-
type: "object",
|
|
225
|
-
required: ["wallet_id"],
|
|
226
|
-
properties: {
|
|
227
|
-
wallet_id: { type: "string" },
|
|
228
|
-
token_name: { type: "string" },
|
|
229
|
-
revoke_existing: { type: "boolean" },
|
|
230
|
-
private_key: {
|
|
231
|
-
type: "string",
|
|
232
|
-
description: "Optional legacy recovery key when requested by the backend.",
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
},
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
name: "apiosk_list_wallet_api_keys",
|
|
239
|
-
description: "List API keys / connect tokens for a managed wallet.",
|
|
240
|
-
inputSchema: {
|
|
241
|
-
type: "object",
|
|
242
|
-
required: ["wallet_id"],
|
|
243
|
-
properties: {
|
|
244
|
-
wallet_id: { type: "string" },
|
|
245
|
-
},
|
|
246
|
-
},
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
name: "apiosk_create_wallet_api_key",
|
|
250
|
-
description: "Create a new API key / connect token for a managed wallet.",
|
|
251
|
-
inputSchema: {
|
|
252
|
-
type: "object",
|
|
253
|
-
required: ["wallet_id"],
|
|
254
|
-
properties: {
|
|
255
|
-
wallet_id: { type: "string" },
|
|
256
|
-
name: { type: "string" },
|
|
257
|
-
expiration_days: { type: "number" },
|
|
258
|
-
revoke_existing: { type: "boolean" },
|
|
259
|
-
private_key: {
|
|
260
|
-
type: "string",
|
|
261
|
-
description: "Optional legacy recovery key when requested by the backend.",
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
},
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
name: "apiosk_update_wallet_api_key",
|
|
268
|
-
description: "Rename, revoke, or extend an existing managed wallet API key.",
|
|
269
|
-
inputSchema: {
|
|
270
|
-
type: "object",
|
|
271
|
-
required: ["wallet_id", "key_id"],
|
|
272
|
-
properties: {
|
|
273
|
-
wallet_id: { type: "string" },
|
|
274
|
-
key_id: { type: "string" },
|
|
275
|
-
name: { type: "string" },
|
|
276
|
-
expiration_days: { type: "number" },
|
|
277
|
-
revoke: { type: "boolean" },
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
name: "apiosk_delete_wallet_api_key",
|
|
283
|
-
description: "Delete an existing managed wallet API key permanently.",
|
|
284
|
-
inputSchema: {
|
|
285
|
-
type: "object",
|
|
286
|
-
required: ["wallet_id", "key_id"],
|
|
287
|
-
properties: {
|
|
288
|
-
wallet_id: { type: "string" },
|
|
289
|
-
key_id: { type: "string" },
|
|
290
|
-
},
|
|
291
|
-
},
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
name: "apiosk_show_wallet_funding",
|
|
295
|
-
description:
|
|
296
|
-
"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.",
|
|
297
|
-
inputSchema: {
|
|
298
|
-
type: "object",
|
|
299
|
-
properties: {
|
|
300
|
-
wallet_id: {
|
|
301
|
-
type: "string",
|
|
302
|
-
description:
|
|
303
|
-
"Optional. Wallet to show funding info for. Defaults to the user's first managed wallet.",
|
|
304
|
-
},
|
|
305
|
-
},
|
|
306
|
-
},
|
|
307
|
-
},
|
|
308
|
-
];
|
|
309
|
-
|
|
310
|
-
const LOCAL_ACCOUNT_AND_CREDITS_TOOLS = [
|
|
311
|
-
{
|
|
312
|
-
name: "apiosk_create_account",
|
|
313
|
-
description:
|
|
314
|
-
"Create an Apiosk dashboard account for credits-based payments. Local stdio only; saves the returned dashboard session token locally when available.",
|
|
315
|
-
inputSchema: {
|
|
316
|
-
type: "object",
|
|
317
|
-
required: ["email", "password"],
|
|
318
|
-
properties: {
|
|
319
|
-
email: { type: "string" },
|
|
320
|
-
password: { type: "string" },
|
|
321
|
-
save_session: {
|
|
322
|
-
type: "boolean",
|
|
323
|
-
description: "Defaults to true when a session token is returned immediately.",
|
|
324
|
-
},
|
|
325
|
-
},
|
|
326
|
-
},
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
name: "apiosk_sign_in",
|
|
330
|
-
description:
|
|
331
|
-
"Sign into an existing Apiosk dashboard account and save the session token locally for credits and managed-wallet tools.",
|
|
332
|
-
inputSchema: {
|
|
333
|
-
type: "object",
|
|
334
|
-
required: ["email", "password"],
|
|
335
|
-
properties: {
|
|
336
|
-
email: { type: "string" },
|
|
337
|
-
password: { type: "string" },
|
|
338
|
-
save_session: {
|
|
339
|
-
type: "boolean",
|
|
340
|
-
description: "Defaults to true.",
|
|
341
|
-
},
|
|
342
|
-
},
|
|
343
|
-
},
|
|
344
|
-
},
|
|
345
|
-
];
|
|
346
|
-
|
|
347
|
-
const REMOTE_CREDITS_TOOLS = [];
|
|
348
|
-
|
|
349
|
-
const LOCAL_WALLET_TOOLS = [
|
|
350
|
-
{
|
|
351
|
-
name: "apiosk_wallet_list",
|
|
352
|
-
description: "List locally stored Apiosk wallets and show which one is active for autonomous pay and publish.",
|
|
353
|
-
inputSchema: {
|
|
354
|
-
type: "object",
|
|
355
|
-
additionalProperties: false,
|
|
356
|
-
properties: {},
|
|
357
|
-
},
|
|
358
|
-
},
|
|
359
|
-
{
|
|
360
|
-
name: "apiosk_wallet_create",
|
|
361
|
-
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.",
|
|
362
|
-
inputSchema: {
|
|
363
|
-
type: "object",
|
|
364
|
-
required: ["label"],
|
|
365
|
-
properties: {
|
|
366
|
-
label: { type: "string" },
|
|
367
|
-
mode: {
|
|
368
|
-
type: "string",
|
|
369
|
-
enum: ["create", "import_private_key", "import_phrase"],
|
|
370
|
-
description: "Defaults to create.",
|
|
371
|
-
},
|
|
372
|
-
secret: {
|
|
373
|
-
type: "string",
|
|
374
|
-
description: "Private key or recovery phrase when importing.",
|
|
375
|
-
},
|
|
376
|
-
set_active: {
|
|
377
|
-
type: "boolean",
|
|
378
|
-
description: "Defaults to true.",
|
|
379
|
-
},
|
|
380
|
-
return_secret: {
|
|
381
|
-
type: "boolean",
|
|
382
|
-
description: "Only enable this when the user explicitly asks to see the private key.",
|
|
383
|
-
},
|
|
384
|
-
save_secret: {
|
|
385
|
-
type: "boolean",
|
|
386
|
-
description: "When true, also export the secret key to a local file.",
|
|
387
|
-
},
|
|
388
|
-
save_to: {
|
|
389
|
-
type: "string",
|
|
390
|
-
description: "Optional output path for the exported secret key.",
|
|
391
|
-
},
|
|
392
|
-
save_format: {
|
|
393
|
-
type: "string",
|
|
394
|
-
enum: ["json", "txt"],
|
|
395
|
-
},
|
|
396
|
-
include_qr_data_url: {
|
|
397
|
-
type: "boolean",
|
|
398
|
-
description: "Include a QR data URL alongside the terminal QR when the client can render images.",
|
|
399
|
-
},
|
|
400
|
-
},
|
|
401
|
-
},
|
|
402
|
-
},
|
|
403
|
-
{
|
|
404
|
-
name: "apiosk_get_started",
|
|
405
|
-
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.",
|
|
406
|
-
inputSchema: {
|
|
407
|
-
type: "object",
|
|
408
|
-
properties: {
|
|
409
|
-
connect_string: {
|
|
410
|
-
type: "string",
|
|
411
|
-
description: "Optional dashboard connect string to save locally for managed access.",
|
|
412
|
-
},
|
|
413
|
-
connect_token: {
|
|
414
|
-
type: "string",
|
|
415
|
-
description: "Optional connect token to save locally when you do not have the full connect string.",
|
|
416
|
-
},
|
|
417
|
-
connect_authorization: {
|
|
418
|
-
type: "string",
|
|
419
|
-
description: "Optional Authorization header value to save alongside a managed connect token.",
|
|
420
|
-
},
|
|
421
|
-
connect_header_name: {
|
|
422
|
-
type: "string",
|
|
423
|
-
description: "Optional connect header override. Defaults to X-Apiosk-Connect-Token.",
|
|
424
|
-
},
|
|
425
|
-
wallet_address: {
|
|
426
|
-
type: "string",
|
|
427
|
-
description: "Optional wallet address to save with a managed connect token.",
|
|
428
|
-
},
|
|
429
|
-
wallet_id: {
|
|
430
|
-
type: "string",
|
|
431
|
-
description: "Optional existing local wallet id to select before running the test call.",
|
|
432
|
-
},
|
|
433
|
-
wallet_label: {
|
|
434
|
-
type: "string",
|
|
435
|
-
description: "Label to use if a new local wallet must be created. Defaults to Apiosk wallet.",
|
|
436
|
-
},
|
|
437
|
-
create_wallet: {
|
|
438
|
-
type: "boolean",
|
|
439
|
-
description: "Defaults to true when no managed connect token is configured.",
|
|
440
|
-
},
|
|
441
|
-
test_slug: {
|
|
442
|
-
type: "string",
|
|
443
|
-
description: "Optional API slug for the test call. Defaults to the first discovered API.",
|
|
444
|
-
},
|
|
445
|
-
test_operation: {
|
|
446
|
-
type: "string",
|
|
447
|
-
description: "Optional explicit operation for the test call.",
|
|
448
|
-
},
|
|
449
|
-
test_input: {
|
|
450
|
-
description: "Optional JSON input for the test call. Defaults to an empty object.",
|
|
451
|
-
},
|
|
452
|
-
include_qr_data_url: {
|
|
453
|
-
type: "boolean",
|
|
454
|
-
description: "Include QR image data when funding instructions are returned.",
|
|
455
|
-
},
|
|
456
|
-
},
|
|
457
|
-
},
|
|
458
|
-
},
|
|
459
|
-
{
|
|
460
|
-
name: "apiosk_configure",
|
|
461
|
-
description: "Show a structured Apiosk control menu for a wallet, including funding instructions, QR code output when available, and wallet/pay/publish/data actions.",
|
|
462
|
-
inputSchema: {
|
|
463
|
-
type: "object",
|
|
464
|
-
properties: {
|
|
465
|
-
wallet_id: { type: "string" },
|
|
466
|
-
section: {
|
|
467
|
-
type: "string",
|
|
468
|
-
enum: ["overview", "funding", "wallet", "payments", "publish", "data", "security"],
|
|
469
|
-
},
|
|
470
|
-
funding_provider: {
|
|
471
|
-
type: "string",
|
|
472
|
-
enum: ["manual", "transak", "onramper"],
|
|
473
|
-
},
|
|
474
|
-
include_qr_data_url: {
|
|
475
|
-
type: "boolean",
|
|
476
|
-
description: "Include a QR data URL alongside the terminal QR when supported by the client.",
|
|
477
|
-
},
|
|
478
|
-
},
|
|
479
|
-
},
|
|
480
|
-
},
|
|
481
|
-
{
|
|
482
|
-
name: "apiosk_wallet_select",
|
|
483
|
-
description: "Select the active local wallet used by apiosk_execute, dynamic API tools, and publish tools.",
|
|
484
|
-
inputSchema: {
|
|
485
|
-
type: "object",
|
|
486
|
-
required: ["wallet_id"],
|
|
487
|
-
properties: {
|
|
488
|
-
wallet_id: { type: "string" },
|
|
489
|
-
include_qr_data_url: {
|
|
490
|
-
type: "boolean",
|
|
491
|
-
description: "Include a QR data URL alongside the terminal QR when supported by the client.",
|
|
492
|
-
},
|
|
493
|
-
},
|
|
494
|
-
},
|
|
495
|
-
},
|
|
496
|
-
{
|
|
497
|
-
name: "apiosk_wallet_update",
|
|
498
|
-
description: "Rename a local wallet or mark it active.",
|
|
499
|
-
inputSchema: {
|
|
500
|
-
type: "object",
|
|
501
|
-
required: ["wallet_id"],
|
|
502
|
-
properties: {
|
|
503
|
-
wallet_id: { type: "string" },
|
|
504
|
-
label: { type: "string" },
|
|
505
|
-
set_active: { type: "boolean" },
|
|
506
|
-
},
|
|
507
|
-
},
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
name: "apiosk_wallet_delete",
|
|
511
|
-
description: "Delete a local wallet from the MCP keystore.",
|
|
512
|
-
inputSchema: {
|
|
513
|
-
type: "object",
|
|
514
|
-
required: ["wallet_id"],
|
|
515
|
-
properties: {
|
|
516
|
-
wallet_id: { type: "string" },
|
|
517
|
-
},
|
|
518
|
-
},
|
|
519
|
-
},
|
|
520
|
-
{
|
|
521
|
-
name: "apiosk_wallet_reveal_secret",
|
|
522
|
-
description: "Reveal a local wallet's private key. Only call this when the user explicitly asks to see the secret key.",
|
|
523
|
-
inputSchema: {
|
|
524
|
-
type: "object",
|
|
525
|
-
required: ["wallet_id"],
|
|
526
|
-
properties: {
|
|
527
|
-
wallet_id: { type: "string" },
|
|
528
|
-
},
|
|
529
|
-
},
|
|
530
|
-
},
|
|
531
|
-
{
|
|
532
|
-
name: "apiosk_wallet_save_secret",
|
|
533
|
-
description: "Save a local wallet's private key to a file without going through the dashboard.",
|
|
534
|
-
inputSchema: {
|
|
535
|
-
type: "object",
|
|
536
|
-
required: ["wallet_id"],
|
|
537
|
-
properties: {
|
|
538
|
-
wallet_id: { type: "string" },
|
|
539
|
-
path: {
|
|
540
|
-
type: "string",
|
|
541
|
-
description: "Optional output path. Defaults to a file under ~/.apiosk/exports.",
|
|
542
|
-
},
|
|
543
|
-
format: {
|
|
544
|
-
type: "string",
|
|
545
|
-
enum: ["json", "txt"],
|
|
546
|
-
},
|
|
547
|
-
},
|
|
548
|
-
},
|
|
549
|
-
},
|
|
550
|
-
];
|
|
551
|
-
|
|
552
|
-
const PUBLISH_TOOLS = [
|
|
553
|
-
{
|
|
554
|
-
name: "apiosk_publish_api",
|
|
555
|
-
description: "Publish a new API on Apiosk using the active local wallet or APIOSK_PRIVATE_KEY. No dashboard required.",
|
|
556
|
-
inputSchema: {
|
|
557
|
-
type: "object",
|
|
558
|
-
required: ["name", "slug", "endpoint_url", "price_usd", "description"],
|
|
559
|
-
properties: {
|
|
560
|
-
wallet_id: { type: "string" },
|
|
561
|
-
name: { type: "string" },
|
|
562
|
-
slug: { type: "string" },
|
|
563
|
-
endpoint_url: { type: "string" },
|
|
564
|
-
price_usd: { type: "number" },
|
|
565
|
-
description: { type: "string" },
|
|
566
|
-
category: { type: "string" },
|
|
567
|
-
listing_group: {
|
|
568
|
-
type: "string",
|
|
569
|
-
enum: ["api", "datasets", "compute"],
|
|
570
|
-
},
|
|
571
|
-
listing_metadata: {
|
|
572
|
-
type: "object",
|
|
573
|
-
additionalProperties: true,
|
|
574
|
-
description: "Optional MCP-native metadata override. Defaults are generated automatically.",
|
|
575
|
-
},
|
|
576
|
-
},
|
|
577
|
-
},
|
|
578
|
-
},
|
|
579
|
-
{
|
|
580
|
-
name: "apiosk_list_my_apis",
|
|
581
|
-
description: "List the APIs owned by the active local wallet or APIOSK_PRIVATE_KEY.",
|
|
582
|
-
inputSchema: {
|
|
583
|
-
type: "object",
|
|
584
|
-
properties: {
|
|
585
|
-
wallet_id: { type: "string" },
|
|
586
|
-
},
|
|
587
|
-
},
|
|
588
|
-
},
|
|
589
|
-
{
|
|
590
|
-
name: "apiosk_update_api",
|
|
591
|
-
description: "Update a published Apiosk API with signed wallet auth.",
|
|
592
|
-
inputSchema: {
|
|
593
|
-
type: "object",
|
|
594
|
-
required: ["slug"],
|
|
595
|
-
properties: {
|
|
596
|
-
wallet_id: { type: "string" },
|
|
597
|
-
slug: { type: "string" },
|
|
598
|
-
endpoint_url: { type: "string" },
|
|
599
|
-
price_usd: { type: "number" },
|
|
600
|
-
description: { type: "string" },
|
|
601
|
-
active: { type: "boolean" },
|
|
602
|
-
listing_metadata: {
|
|
603
|
-
type: "object",
|
|
604
|
-
additionalProperties: true,
|
|
605
|
-
},
|
|
606
|
-
},
|
|
607
|
-
},
|
|
608
|
-
},
|
|
609
|
-
{
|
|
610
|
-
name: "apiosk_delete_api",
|
|
611
|
-
description: "Delete or deactivate one of your published Apiosk APIs with signed wallet auth.",
|
|
612
|
-
inputSchema: {
|
|
613
|
-
type: "object",
|
|
614
|
-
required: ["slug"],
|
|
615
|
-
properties: {
|
|
616
|
-
wallet_id: { type: "string" },
|
|
617
|
-
slug: { type: "string" },
|
|
618
|
-
},
|
|
619
|
-
},
|
|
620
|
-
},
|
|
621
|
-
];
|
|
622
|
-
|
|
623
|
-
const HELP_TOOL = {
|
|
624
|
-
name: "apiosk_help",
|
|
625
|
-
description: "Explain what Apiosk MCP is, how to connect it, how auth and USDC/x402 payments work, and the recommended workflow for discovery, wallets, and publishing.",
|
|
626
|
-
annotations: {
|
|
627
|
-
readOnlyHint: true,
|
|
628
|
-
openWorldHint: false,
|
|
629
|
-
destructiveHint: false,
|
|
630
|
-
},
|
|
631
|
-
inputSchema: {
|
|
632
|
-
type: "object",
|
|
633
|
-
properties: {
|
|
634
|
-
topic: {
|
|
635
|
-
type: "string",
|
|
636
|
-
enum: ["overview", "setup", "auth", "workflow", "discovery", "payments", "rails", "wallets", "publish", "configure"],
|
|
637
|
-
description: "Optional help topic. Defaults to overview. Use 'discovery' to learn which live sources apiosk_discover searches (Apiosk catalog + Coinbase Bazaar + well-known); use 'rails' for how USDC/x402 settlement works.",
|
|
638
|
-
},
|
|
639
|
-
},
|
|
640
|
-
},
|
|
641
|
-
};
|
|
642
|
-
|
|
643
|
-
const EXPLORE_TOOL = {
|
|
644
|
-
name: "apiosk_explore",
|
|
645
|
-
description: "Browse Apiosk listing groups and explore one group at a time before narrowing with search.",
|
|
646
|
-
annotations: {
|
|
647
|
-
readOnlyHint: true,
|
|
648
|
-
openWorldHint: false,
|
|
649
|
-
destructiveHint: false,
|
|
650
|
-
},
|
|
651
|
-
inputSchema: {
|
|
652
|
-
type: "object",
|
|
653
|
-
properties: {
|
|
654
|
-
listing_type: {
|
|
655
|
-
type: "string",
|
|
656
|
-
enum: ["api", "dataset", "service", "connector", "skill", "product"],
|
|
657
|
-
},
|
|
658
|
-
search: {
|
|
659
|
-
type: "string",
|
|
660
|
-
description: "Optional free-text search when listing_type is set.",
|
|
661
|
-
},
|
|
662
|
-
sort: {
|
|
663
|
-
type: "string",
|
|
664
|
-
enum: ["name", "price", "newest"],
|
|
665
|
-
},
|
|
666
|
-
order: {
|
|
667
|
-
type: "string",
|
|
668
|
-
enum: ["asc", "desc"],
|
|
669
|
-
},
|
|
670
|
-
limit: {
|
|
671
|
-
type: "number",
|
|
672
|
-
},
|
|
673
|
-
offset: {
|
|
674
|
-
type: "number",
|
|
675
|
-
},
|
|
676
|
-
},
|
|
677
|
-
},
|
|
678
|
-
};
|
|
679
|
-
|
|
680
|
-
const SEARCH_TOOL = {
|
|
681
|
-
name: "apiosk_search",
|
|
682
|
-
description: "Search and browse the Apiosk catalog by capability, price, or category. For browsing/filtering the catalog. When the goal is to fulfil a user request with real paid data ('get me the live X'), prefer apiosk_discover, which decomposes the need and ranks the best endpoints across sources.",
|
|
683
|
-
annotations: {
|
|
684
|
-
readOnlyHint: true,
|
|
685
|
-
openWorldHint: false,
|
|
686
|
-
destructiveHint: false,
|
|
687
|
-
},
|
|
688
|
-
inputSchema: {
|
|
689
|
-
type: "object",
|
|
690
|
-
properties: {
|
|
691
|
-
search: {
|
|
692
|
-
type: "string",
|
|
693
|
-
description: "Free-text search over API names and descriptions.",
|
|
694
|
-
},
|
|
695
|
-
category: {
|
|
696
|
-
type: "string",
|
|
697
|
-
description: "Optional category filter.",
|
|
698
|
-
},
|
|
699
|
-
sort: {
|
|
700
|
-
type: "string",
|
|
701
|
-
enum: ["name", "price", "newest"],
|
|
702
|
-
description: "Sort order for results.",
|
|
703
|
-
},
|
|
704
|
-
order: {
|
|
705
|
-
type: "string",
|
|
706
|
-
enum: ["asc", "desc"],
|
|
707
|
-
description: "Sort direction.",
|
|
708
|
-
},
|
|
709
|
-
limit: {
|
|
710
|
-
type: "number",
|
|
711
|
-
description: "Maximum number of APIs to return.",
|
|
712
|
-
},
|
|
713
|
-
offset: {
|
|
714
|
-
type: "number",
|
|
715
|
-
description: "Pagination offset.",
|
|
716
|
-
},
|
|
717
|
-
},
|
|
718
|
-
},
|
|
719
|
-
};
|
|
720
|
-
|
|
721
|
-
const METADATA_TOOL_INPUT_SCHEMA = {
|
|
722
|
-
type: "object",
|
|
723
|
-
required: ["slug"],
|
|
724
|
-
properties: {
|
|
725
|
-
slug: {
|
|
726
|
-
type: "string",
|
|
727
|
-
description: "Apiosk API slug, for example 'agent-json-diff'.",
|
|
728
|
-
},
|
|
729
|
-
},
|
|
730
|
-
};
|
|
731
|
-
|
|
732
|
-
const GET_API_TOOL = {
|
|
733
|
-
name: "apiosk_get_api",
|
|
734
|
-
description: "Fetch full listing detail and agent metadata for a specific Apiosk API slug.",
|
|
735
|
-
annotations: {
|
|
736
|
-
readOnlyHint: true,
|
|
737
|
-
openWorldHint: false,
|
|
738
|
-
destructiveHint: false,
|
|
739
|
-
},
|
|
740
|
-
inputSchema: METADATA_TOOL_INPUT_SCHEMA,
|
|
741
|
-
};
|
|
742
|
-
|
|
743
|
-
const METADATA_TOOL = {
|
|
744
|
-
name: "apiosk_metadata",
|
|
745
|
-
description: "Fetch full listing detail and agent metadata for a specific Apiosk API slug.",
|
|
746
|
-
annotations: {
|
|
747
|
-
readOnlyHint: true,
|
|
748
|
-
openWorldHint: false,
|
|
749
|
-
destructiveHint: false,
|
|
750
|
-
},
|
|
751
|
-
inputSchema: METADATA_TOOL_INPUT_SCHEMA,
|
|
752
|
-
};
|
|
753
|
-
|
|
754
|
-
const EXECUTE_TOOL = {
|
|
755
|
-
name: "apiosk_execute",
|
|
756
|
-
description: "Execute any Apiosk API by slug through the uniform /execute contract.",
|
|
757
|
-
annotations: {
|
|
758
|
-
readOnlyHint: false,
|
|
759
|
-
openWorldHint: true,
|
|
760
|
-
destructiveHint: true,
|
|
761
|
-
},
|
|
762
|
-
_meta: {
|
|
763
|
-
"openai/outputTemplate": "ui://apiosk/result-canvas.html",
|
|
764
|
-
"openai/toolInvocation/invoking": "Fetching and paying for data…",
|
|
765
|
-
"openai/toolInvocation/invoked": "Paid data received",
|
|
766
|
-
ui: { resourceUri: "ui://apiosk/result-canvas.html" },
|
|
767
|
-
},
|
|
768
|
-
inputSchema: {
|
|
769
|
-
type: "object",
|
|
770
|
-
required: ["slug"],
|
|
771
|
-
properties: {
|
|
772
|
-
slug: {
|
|
773
|
-
type: "string",
|
|
774
|
-
description: "Apiosk API slug.",
|
|
775
|
-
},
|
|
776
|
-
operation: {
|
|
777
|
-
type: "string",
|
|
778
|
-
description: "Optional explicit operation id or path.",
|
|
779
|
-
},
|
|
780
|
-
input: {
|
|
781
|
-
type: "object",
|
|
782
|
-
additionalProperties: true,
|
|
783
|
-
description: "Raw JSON body for the default operation, or the envelope input field when operation is provided.",
|
|
784
|
-
},
|
|
785
|
-
query: {
|
|
786
|
-
type: "object",
|
|
787
|
-
additionalProperties: true,
|
|
788
|
-
description: "Optional query override when using the execute envelope.",
|
|
789
|
-
},
|
|
790
|
-
path_params: {
|
|
791
|
-
type: "object",
|
|
792
|
-
additionalProperties: true,
|
|
793
|
-
description: "Optional path parameter override when using the execute envelope.",
|
|
794
|
-
},
|
|
795
|
-
},
|
|
796
|
-
},
|
|
797
|
-
};
|
|
798
|
-
|
|
799
|
-
const HEALTH_TOOL = {
|
|
800
|
-
name: "apiosk_health",
|
|
801
|
-
description: "Report Apiosk MCP runtime status and the configured gateway base URL.",
|
|
802
|
-
annotations: {
|
|
803
|
-
readOnlyHint: true,
|
|
804
|
-
openWorldHint: false,
|
|
805
|
-
destructiveHint: false,
|
|
806
|
-
},
|
|
807
|
-
inputSchema: {
|
|
808
|
-
type: "object",
|
|
809
|
-
additionalProperties: false,
|
|
810
|
-
properties: {},
|
|
811
|
-
},
|
|
812
|
-
};
|
|
813
|
-
|
|
814
|
-
const PAYMENT_GUIDE_TOOL = {
|
|
815
|
-
name: "apiosk_payment_guide",
|
|
816
|
-
description:
|
|
817
|
-
"Explain how to pay through the Apiosk gateway. Returns a buyer guide (how an agent settles a paid API call over USDC/x402, 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.",
|
|
818
|
-
annotations: {
|
|
819
|
-
readOnlyHint: true,
|
|
820
|
-
openWorldHint: false,
|
|
821
|
-
destructiveHint: false,
|
|
822
|
-
},
|
|
823
|
-
inputSchema: {
|
|
824
|
-
type: "object",
|
|
825
|
-
properties: {
|
|
826
|
-
role: {
|
|
827
|
-
type: "string",
|
|
828
|
-
enum: ["buyer", "provider", "both"],
|
|
829
|
-
description: "Which side to explain. Defaults to both.",
|
|
830
|
-
},
|
|
831
|
-
slug: {
|
|
832
|
-
type: "string",
|
|
833
|
-
description: "Optional API slug to scope buyer guidance (price, payment steps) to one listing.",
|
|
834
|
-
},
|
|
835
|
-
},
|
|
836
|
-
},
|
|
837
|
-
};
|
|
838
|
-
|
|
839
|
-
const DISCOVERY_TOOLS = [
|
|
840
|
-
HELP_TOOL,
|
|
841
|
-
PAYMENT_GUIDE_TOOL,
|
|
842
|
-
EXPLORE_TOOL,
|
|
843
|
-
SEARCH_TOOL,
|
|
844
|
-
DISCOVER_TOOL,
|
|
845
|
-
INSPECT_TOOL,
|
|
846
|
-
GET_API_TOOL,
|
|
847
|
-
EXECUTE_TOOL,
|
|
848
|
-
];
|
|
849
|
-
// Discovery + payment guidance available to every remote buyer, even before
|
|
850
|
-
// they authorize (these tools are public / read-only). apiosk_discover and
|
|
851
|
-
// apiosk_inspect_x402 are read-only too — they find candidate x402 endpoints and
|
|
852
|
-
// read their 402 terms without spending, so buyers can plan a purchase pre-auth.
|
|
853
|
-
const HOSTED_DISCOVERY_TOOLS = [
|
|
854
|
-
HELP_TOOL,
|
|
855
|
-
PAYMENT_GUIDE_TOOL,
|
|
856
|
-
SEARCH_TOOL,
|
|
857
|
-
DISCOVER_TOOL,
|
|
858
|
-
INSPECT_TOOL,
|
|
859
|
-
EXPLORE_TOOL,
|
|
860
|
-
GET_API_TOOL,
|
|
861
|
-
METADATA_TOOL,
|
|
862
|
-
EXECUTE_TOOL,
|
|
863
|
-
HEALTH_TOOL,
|
|
864
|
-
];
|
|
865
|
-
|
|
866
|
-
// Managed buyer tools that work over request-scoped dashboard auth on the
|
|
867
|
-
// hosted endpoint: prepaid credits + full managed agent-wallet CRUD. These are
|
|
868
|
-
// protected (the OAuth layer requires authorization before they run).
|
|
869
|
-
// apiosk_show_wallet_funding is intentionally excluded, it resolves a local /
|
|
870
|
-
// env wallet, which does not exist on the hosted surface.
|
|
871
|
-
const HOSTED_MANAGED_TOOLS = [
|
|
872
|
-
...DASHBOARD_WALLET_TOOLS.filter((tool) => tool.name !== "apiosk_show_wallet_funding"),
|
|
873
|
-
];
|
|
874
|
-
|
|
875
|
-
// The hosted/remote surface is fully capable: discovery, payment guidance,
|
|
876
|
-
// generic + dynamic per-API execution, credits, and managed-wallet management.
|
|
877
|
-
// Wallet-signed publishing (apiosk_publish_api & co.) stays local/portal-only
|
|
878
|
-
// because it requires a client-side signing key the hosted server never
|
|
879
|
-
// holds; the x402 publisher tools work hosted because they authenticate with
|
|
880
|
-
// a provider token (sk_live_…) instead of a wallet.
|
|
881
|
-
// External-payment buyer tool: pay an x402 endpoint the gateway does NOT host,
|
|
882
|
-
// from the connected managed wallet, via the gateway payer proxy. Protected
|
|
883
|
-
// (spends USDC) and available in every mode that can present a connect token.
|
|
884
|
-
const EXTERNAL_PAY_TOOLS = [FETCH_PAID_TOOL];
|
|
885
|
-
|
|
886
|
-
const HOSTED_REMOTE_TOOLS = [
|
|
887
|
-
...HOSTED_DISCOVERY_TOOLS,
|
|
888
|
-
...HOSTED_MANAGED_TOOLS,
|
|
889
|
-
...EXTERNAL_PAY_TOOLS,
|
|
890
|
-
...PUBLISHER_TOOLS,
|
|
891
|
-
];
|
|
892
|
-
|
|
893
|
-
// Lean BUYER surface for the hosted connector. A buyer opening the connector in
|
|
894
|
-
// ChatGPT/Claude should see the agentic flow + one wallet view — not 28 tools.
|
|
895
|
-
// Ordered flow-first. The advanced wallet CRUD (create/update/delete wallet +
|
|
896
|
-
// api-keys), explore/metadata/health, and provider publishing are dropped from
|
|
897
|
-
// the default list. Nothing is lost: every hidden tool is still dispatchable by
|
|
898
|
-
// name (callTool + isToolProtected are unchanged) and the full set is one env
|
|
899
|
-
// flag away (APIOSK_MCP_FULL_TOOLS=true). apiosk_list_wallets stays so buyers can
|
|
900
|
-
// see their wallet address, spend limits, and funding.
|
|
901
|
-
const HOSTED_BUYER_TOOLS = [
|
|
902
|
-
HELP_TOOL,
|
|
903
|
-
DISCOVER_TOOL,
|
|
904
|
-
EXPLORE_TOOL,
|
|
905
|
-
INSPECT_TOOL,
|
|
906
|
-
EXECUTE_TOOL,
|
|
907
|
-
FETCH_PAID_TOOL,
|
|
908
|
-
GET_API_TOOL,
|
|
909
|
-
SEARCH_TOOL,
|
|
910
|
-
PAYMENT_GUIDE_TOOL,
|
|
911
|
-
DASHBOARD_WALLET_TOOLS.find((tool) => tool.name === "apiosk_list_wallets"),
|
|
912
|
-
].filter(Boolean);
|
|
913
|
-
|
|
914
|
-
// PROVIDER surface (caller authenticated with a sk_live_ provider key): a focused
|
|
915
|
-
// publishing toolkit. The managed-wallet CRUD needs a dashboard JWT a provider
|
|
916
|
-
// key does not carry, so those tools would fail anyway — they're excluded here.
|
|
917
|
-
const HOSTED_PROVIDER_TOOLS = [
|
|
918
|
-
HELP_TOOL,
|
|
919
|
-
PAYMENT_GUIDE_TOOL,
|
|
920
|
-
SEARCH_TOOL,
|
|
921
|
-
GET_API_TOOL,
|
|
922
|
-
...PUBLISHER_TOOLS,
|
|
923
|
-
];
|
|
924
|
-
|
|
925
|
-
const ALL_STATIC_TOOLS = [
|
|
926
|
-
...DISCOVERY_TOOLS,
|
|
927
|
-
...HOSTED_REMOTE_TOOLS,
|
|
928
|
-
...LOCAL_ACCOUNT_AND_CREDITS_TOOLS,
|
|
929
|
-
...LOCAL_WALLET_TOOLS,
|
|
930
|
-
...DASHBOARD_WALLET_TOOLS,
|
|
931
|
-
...PUBLISH_TOOLS,
|
|
932
|
-
...PUBLISHER_TOOLS,
|
|
933
|
-
];
|
|
934
|
-
|
|
935
|
-
// These tools read or mutate client-machine state (local files, local signing
|
|
936
|
-
// keys, or a local dashboard session). They must never execute in the hosted
|
|
937
|
-
// multi-tenant server, even if a stale MCP client cached an older tool list or
|
|
938
|
-
// the deployment accidentally carries APIOSK_ENABLE_LOCAL_WALLETS=true.
|
|
939
|
-
const LOCAL_ONLY_TOOL_NAMES = new Set([
|
|
940
|
-
...LOCAL_ACCOUNT_AND_CREDITS_TOOLS.map((tool) => tool.name),
|
|
941
|
-
...LOCAL_WALLET_TOOLS.map((tool) => tool.name),
|
|
942
|
-
...PUBLISH_TOOLS.map((tool) => tool.name),
|
|
943
|
-
]);
|
|
944
|
-
|
|
945
|
-
const PUBLIC_STATIC_TOOL_NAMES = new Set(
|
|
946
|
-
DISCOVERY_TOOLS.map((tool) => tool.name).filter((name) => name !== "apiosk_execute")
|
|
947
|
-
);
|
|
948
|
-
const REMOTE_PROTECTED_STATIC_TOOL_NAMES = new Set([
|
|
949
|
-
"apiosk_execute",
|
|
950
|
-
...EXTERNAL_PAY_TOOLS.map((tool) => tool.name),
|
|
951
|
-
...REMOTE_CREDITS_TOOLS.map((tool) => tool.name),
|
|
952
|
-
...DASHBOARD_WALLET_TOOLS.map((tool) => tool.name),
|
|
953
|
-
...PUBLISHER_TOOLS.map((tool) => tool.name),
|
|
954
|
-
]);
|
|
955
|
-
|
|
956
|
-
function trimString(value) {
|
|
957
|
-
return String(value || "").trim();
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
function sanitizeToolName(name, fallback) {
|
|
961
|
-
const candidate = String(name || fallback || "apiosk_tool")
|
|
962
|
-
.trim()
|
|
963
|
-
.replace(/[^A-Za-z0-9_-]+/g, "-")
|
|
964
|
-
.replace(/^-+|-+$/g, "");
|
|
965
|
-
|
|
966
|
-
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;
|
|
967
21
|
}
|
|
968
22
|
|
|
969
23
|
/**
|
|
970
|
-
*
|
|
971
|
-
* three-state listing lifecycle (live | pending | disabled). The portal writes
|
|
972
|
-
* apis.status (active|inactive) and the gateway auto-verifies owner-owned
|
|
973
|
-
* (KYB-gated) listings, so for a catalog reader: not active -> disabled,
|
|
974
|
-
* active but unverified -> pending review, active + verified -> live.
|
|
24
|
+
* Build the MCP runtime.
|
|
975
25
|
*
|
|
976
|
-
*
|
|
977
|
-
*
|
|
978
|
-
*
|
|
979
|
-
*
|
|
980
|
-
* 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
|
|
981
30
|
*/
|
|
982
|
-
function deriveListingStatus(api) {
|
|
983
|
-
if (api.active === false) return "disabled";
|
|
984
|
-
if (api.verified === false) return "pending";
|
|
985
|
-
return "live";
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
function buildCatalogEntry(api, toolName) {
|
|
989
|
-
return {
|
|
990
|
-
slug: api.slug,
|
|
991
|
-
name: api.name,
|
|
992
|
-
description: api.description,
|
|
993
|
-
category: api.category,
|
|
994
|
-
listing_type:
|
|
995
|
-
api.listing_type ||
|
|
996
|
-
api.listing_metadata?.provider?.listing_type ||
|
|
997
|
-
null,
|
|
998
|
-
price_usd: api.price_usd ?? api.listing_metadata?.cost_per_call ?? null,
|
|
999
|
-
docs_url: api.docs_url ?? api.listing_metadata?.provider?.docs_url ?? null,
|
|
1000
|
-
tool_name: toolName,
|
|
1001
|
-
default_operation: api.listing_metadata?.default_operation ?? null,
|
|
1002
|
-
tags: api.listing_metadata?.tags ?? [],
|
|
1003
|
-
mcp_native: api.listing_metadata?.mcp_native ?? false,
|
|
1004
|
-
// Listing lifecycle from the gateway /v1/apis flags so a catalog reader can
|
|
1005
|
-
// tell live from pending-verification from disabled, matching the portal.
|
|
1006
|
-
status: deriveListingStatus(api),
|
|
1007
|
-
active: api.active ?? true,
|
|
1008
|
-
verified: api.verified ?? false,
|
|
1009
|
-
};
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
function normalizeCatalogToken(value) {
|
|
1013
|
-
return String(value || "")
|
|
1014
|
-
.trim()
|
|
1015
|
-
.toLowerCase()
|
|
1016
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
1017
|
-
.replace(/^-+|-+$/g, "");
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
function isWeatherLikeSlug(slug) {
|
|
1021
|
-
return /weather|meteo|forecast/.test(normalizeCatalogToken(slug));
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
function buildDynamicTools(catalog, reservedTools) {
|
|
1025
|
-
const tools = [];
|
|
1026
|
-
const toolIndex = new Map();
|
|
1027
|
-
|
|
1028
|
-
// Per-API "dynamic" tools surface one MCP tool per gateway listing, which
|
|
1029
|
-
// DUPLICATES every paid API. Exposing them also lets the provider-portal MCP
|
|
1030
|
-
// import create separate /tools/<slug> gateway endpoints that undercut (or
|
|
1031
|
-
// give away for free) the real API listings. Agents must instead discover via
|
|
1032
|
-
// apiosk_search / apiosk_explore and call any API through apiosk_execute,
|
|
1033
|
-
// which settles the real per-call price. So these are OFF by default; set
|
|
1034
|
-
// APIOSK_MCP_DYNAMIC_TOOLS=true to opt back in.
|
|
1035
|
-
if (process.env.APIOSK_MCP_DYNAMIC_TOOLS !== "true") {
|
|
1036
|
-
return { tools, toolIndex };
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
const usedNames = new Set(reservedTools.map((tool) => tool.name));
|
|
1040
|
-
|
|
1041
|
-
for (const api of catalog) {
|
|
1042
|
-
if (api.active === false) continue;
|
|
1043
|
-
|
|
1044
|
-
const rawName = api.listing_metadata?.mcp_tool?.name || api.slug;
|
|
1045
|
-
let toolName = sanitizeToolName(rawName, api.slug);
|
|
1046
|
-
if (usedNames.has(toolName)) {
|
|
1047
|
-
toolName = sanitizeToolName(`${toolName}-${api.slug}`, api.slug);
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
usedNames.add(toolName);
|
|
1051
|
-
|
|
1052
|
-
const price = api.price_usd ?? api.listing_metadata?.cost_per_call;
|
|
1053
|
-
const dynamicDescriptionParts = [
|
|
1054
|
-
api.listing_metadata?.mcp_tool?.description || api.description || api.name,
|
|
1055
|
-
`Apiosk slug: ${api.slug}.`,
|
|
1056
|
-
];
|
|
1057
|
-
|
|
1058
|
-
if (price !== undefined && price !== null) {
|
|
1059
|
-
dynamicDescriptionParts.push(`Cost per call: $${price}.`);
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
if (api.listing_metadata?.default_operation) {
|
|
1063
|
-
dynamicDescriptionParts.push(`Default operation: ${api.listing_metadata.default_operation}.`);
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
dynamicDescriptionParts.push("This tool executes the API through Apiosk's uniform /execute contract.");
|
|
1067
|
-
|
|
1068
|
-
tools.push({
|
|
1069
|
-
name: toolName,
|
|
1070
|
-
description: dynamicDescriptionParts.join(" "),
|
|
1071
|
-
inputSchema: api.listing_metadata?.mcp_tool?.inputSchema || {
|
|
1072
|
-
type: "object",
|
|
1073
|
-
additionalProperties: true,
|
|
1074
|
-
},
|
|
1075
|
-
outputSchema:
|
|
1076
|
-
api.listing_metadata?.mcp_tool?.outputSchema || DEFAULT_TOOL_OUTPUT_SCHEMA,
|
|
1077
|
-
annotations:
|
|
1078
|
-
api.listing_metadata?.mcp_tool?.annotations || DYNAMIC_EXECUTE_ANNOTATIONS,
|
|
1079
|
-
_meta: {
|
|
1080
|
-
"openai/outputTemplate": "ui://apiosk/result-canvas.html",
|
|
1081
|
-
"openai/toolInvocation/invoking": `Fetching ${api.name || api.slug}…`,
|
|
1082
|
-
"openai/toolInvocation/invoked": `${api.name || api.slug} received`,
|
|
1083
|
-
ui: { resourceUri: "ui://apiosk/result-canvas.html" },
|
|
1084
|
-
},
|
|
1085
|
-
});
|
|
1086
|
-
|
|
1087
|
-
toolIndex.set(toolName, {
|
|
1088
|
-
api,
|
|
1089
|
-
toolName,
|
|
1090
|
-
});
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
return { tools, toolIndex };
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
function resolveDashboardUserToken(env = process.env, savedConfig = null) {
|
|
1097
|
-
return trimString(
|
|
1098
|
-
env.APIOSK_DASHBOARD_JWT ||
|
|
1099
|
-
env.APIOSK_USER_JWT ||
|
|
1100
|
-
env.APIOSK_AUTH_HEADER_VALUE ||
|
|
1101
|
-
env.APIOSK_SESSION_TOKEN ||
|
|
1102
|
-
savedConfig?.dashboard_session_token
|
|
1103
|
-
);
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
function resolveControlPlaneUrl(env = process.env, savedConfig = null) {
|
|
1107
|
-
return trimString(
|
|
1108
|
-
env.APIOSK_CONTROL_PLANE_URL || savedConfig?.control_plane_url || "https://mcp.apiosk.com"
|
|
1109
|
-
).replace(
|
|
1110
|
-
/\/+$/,
|
|
1111
|
-
""
|
|
1112
|
-
);
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
function resolveGatewayBaseUrl(env = process.env, savedConfig = null) {
|
|
1116
|
-
return trimString(env.APIOSK_GATEWAY || savedConfig?.gateway_url || DEFAULT_GATEWAY_BASE_URL).replace(
|
|
1117
|
-
/\/+$/,
|
|
1118
|
-
""
|
|
1119
|
-
);
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
function summarizeSavedConnectConfig(savedConfig = null, env = process.env) {
|
|
1123
|
-
if (!savedConfig) return null;
|
|
1124
|
-
|
|
1125
|
-
return {
|
|
1126
|
-
gateway_url: trimString(savedConfig.gateway_url || DEFAULT_GATEWAY_BASE_URL),
|
|
1127
|
-
agent_wallet_address: trimString(savedConfig.agent_wallet_address || ""),
|
|
1128
|
-
connect_header_name: trimString(savedConfig.connect_header_name || "X-Apiosk-Connect-Token"),
|
|
1129
|
-
connect_token_saved: Boolean(trimString(savedConfig.connect_token)),
|
|
1130
|
-
connect_authorization_saved:
|
|
1131
|
-
Boolean(trimString(savedConfig.connect_authorization || env.APIOSK_CONNECT_AUTHORIZATION)),
|
|
1132
|
-
};
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
function buildAuthState({ env = process.env, savedConfig = null, activeWallet = null } = {}) {
|
|
1136
|
-
const envConnectToken = trimString(env.APIOSK_CONNECT_TOKEN);
|
|
1137
|
-
const savedConnectToken = trimString(savedConfig?.connect_token);
|
|
1138
|
-
const envAuthorization = trimString(env.APIOSK_CONNECT_AUTHORIZATION);
|
|
1139
|
-
const savedAuthorization = trimString(savedConfig?.connect_authorization);
|
|
1140
|
-
const envWalletAddress = trimString(env.APIOSK_WALLET_ADDRESS).toLowerCase();
|
|
1141
|
-
const savedWalletAddress = trimString(savedConfig?.agent_wallet_address).toLowerCase();
|
|
1142
|
-
|
|
1143
|
-
let mode = "none";
|
|
1144
|
-
if (envConnectToken || savedConnectToken) {
|
|
1145
|
-
mode = "connect_token";
|
|
1146
|
-
} else if (envAuthorization || savedAuthorization) {
|
|
1147
|
-
mode = "authorization";
|
|
1148
|
-
} else if (trimString(env.APIOSK_PRIVATE_KEY)) {
|
|
1149
|
-
mode = "env_private_key";
|
|
1150
|
-
} else if (activeWallet?.source === "local_store") {
|
|
1151
|
-
mode = "local_wallet";
|
|
1152
|
-
} else if (envWalletAddress || savedWalletAddress || activeWallet?.address) {
|
|
1153
|
-
mode = "wallet_address";
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
return {
|
|
1157
|
-
mode,
|
|
1158
|
-
connect_token_configured: Boolean(envConnectToken || savedConnectToken),
|
|
1159
|
-
authorization_configured: Boolean(envAuthorization || savedAuthorization),
|
|
1160
|
-
wallet_address:
|
|
1161
|
-
activeWallet?.address ||
|
|
1162
|
-
envWalletAddress ||
|
|
1163
|
-
savedWalletAddress ||
|
|
1164
|
-
null,
|
|
1165
|
-
gateway_url: resolveGatewayBaseUrl(env, savedConfig),
|
|
1166
|
-
};
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
function resolveRequestDashboardUserToken(authInfo = null) {
|
|
1170
|
-
const extra = authInfo?.extra;
|
|
1171
|
-
if (!extra || typeof extra !== "object") return "";
|
|
1172
|
-
|
|
1173
|
-
return trimString(
|
|
1174
|
-
extra.dashboardSessionToken ||
|
|
1175
|
-
extra.dashboard_session_token ||
|
|
1176
|
-
extra.userToken ||
|
|
1177
|
-
extra.user_token ||
|
|
1178
|
-
""
|
|
1179
|
-
);
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
function hasRequestScopedDashboardAccess(authInfo = null) {
|
|
1183
|
-
return Boolean(resolveRequestDashboardUserToken(authInfo));
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
function buildRequestScopedDashboardHeaders(authInfo = null) {
|
|
1187
|
-
const token = resolveRequestDashboardUserToken(authInfo);
|
|
1188
|
-
if (!token) return null;
|
|
1189
|
-
|
|
1190
|
-
return {
|
|
1191
|
-
"x-apiosk-user-jwt": token,
|
|
1192
|
-
authorization: `Bearer ${token}`,
|
|
1193
|
-
};
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
function pickMessage(payload, fallback) {
|
|
1197
|
-
if (payload && typeof payload === "object") {
|
|
1198
|
-
if (typeof payload.message === "string" && payload.message.trim()) {
|
|
1199
|
-
return payload.message.trim();
|
|
1200
|
-
}
|
|
1201
|
-
if (typeof payload.error === "string" && payload.error.trim()) {
|
|
1202
|
-
return payload.error.trim();
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
return fallback;
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
function asObject(value) {
|
|
1210
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
function createDashboardWalletManagerFromEnv(env = process.env) {
|
|
1214
|
-
const dashboardUrl = resolveControlPlaneUrl(env);
|
|
1215
|
-
const token = resolveDashboardUserToken(env);
|
|
1216
|
-
|
|
1217
|
-
return {
|
|
1218
|
-
isConfigured() {
|
|
1219
|
-
return Boolean(token);
|
|
1220
|
-
},
|
|
1221
|
-
async request(path, init = {}) {
|
|
1222
|
-
if (!token) {
|
|
1223
|
-
throw new Error(
|
|
1224
|
-
"Wallet tools require APIOSK_DASHBOARD_JWT or APIOSK_USER_JWT in the local MCP environment."
|
|
1225
|
-
);
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
const headers = new Headers(init.headers || {});
|
|
1229
|
-
headers.set("accept", "application/json");
|
|
1230
|
-
headers.set("x-apiosk-user-jwt", token);
|
|
1231
|
-
headers.set("authorization", `Bearer ${token}`);
|
|
1232
|
-
|
|
1233
|
-
let body = init.body;
|
|
1234
|
-
if (body !== undefined && body !== null && typeof body !== "string") {
|
|
1235
|
-
headers.set("content-type", "application/json");
|
|
1236
|
-
body = JSON.stringify(body);
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
const response = await fetch(`${dashboardUrl}${path}`, {
|
|
1240
|
-
...init,
|
|
1241
|
-
body,
|
|
1242
|
-
cache: "no-store",
|
|
1243
|
-
headers,
|
|
1244
|
-
});
|
|
1245
|
-
|
|
1246
|
-
const text = await response.text();
|
|
1247
|
-
let payload = null;
|
|
1248
|
-
try {
|
|
1249
|
-
payload = text ? JSON.parse(text) : null;
|
|
1250
|
-
} catch {
|
|
1251
|
-
payload = text || null;
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
if (!response.ok) {
|
|
1255
|
-
throw new Error(
|
|
1256
|
-
pickMessage(payload, `Dashboard wallet request failed with HTTP ${response.status}`)
|
|
1257
|
-
);
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
return payload;
|
|
1261
|
-
},
|
|
1262
|
-
};
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
function content(value) {
|
|
1266
|
-
const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
1267
|
-
const result = {
|
|
1268
|
-
content: [
|
|
1269
|
-
{
|
|
1270
|
-
type: "text",
|
|
1271
|
-
text,
|
|
1272
|
-
},
|
|
1273
|
-
],
|
|
1274
|
-
};
|
|
1275
|
-
|
|
1276
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1277
|
-
result.structuredContent = value;
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
return result;
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
function errorContent(value) {
|
|
1284
|
-
return {
|
|
1285
|
-
content: [
|
|
1286
|
-
{
|
|
1287
|
-
type: "text",
|
|
1288
|
-
text: typeof value === "string" ? value : JSON.stringify(value, null, 2),
|
|
1289
|
-
},
|
|
1290
|
-
],
|
|
1291
|
-
isError: true,
|
|
1292
|
-
};
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
function summarizeDynamicToolResult(tool, result) {
|
|
1296
|
-
if (!result || typeof result !== "object") return null;
|
|
1297
|
-
if (result.status !== "success") return null;
|
|
1298
|
-
|
|
1299
|
-
const payload =
|
|
1300
|
-
result.result && typeof result.result === "object" && !Array.isArray(result.result)
|
|
1301
|
-
? result.result
|
|
1302
|
-
: null;
|
|
1303
|
-
if (!payload) return null;
|
|
1304
|
-
|
|
1305
|
-
const orderId = payload.order_id;
|
|
1306
|
-
const pizzaType = payload.pizza?.type || payload.pizza_type;
|
|
1307
|
-
const pizzaSize = payload.pizza?.size || payload.size;
|
|
1308
|
-
const address = payload.address;
|
|
1309
|
-
const receiptUrl = payload.receipt_url;
|
|
1310
|
-
|
|
1311
|
-
if (orderId && pizzaType && pizzaSize) {
|
|
1312
|
-
const parts = [
|
|
1313
|
-
`${tool.api.name || tool.api.slug} order confirmed.`,
|
|
1314
|
-
`${pizzaSize} ${pizzaType}.`,
|
|
1315
|
-
`Order ID ${orderId}.`,
|
|
1316
|
-
];
|
|
1317
|
-
if (address) parts.push(`Delivery: ${address}.`);
|
|
1318
|
-
if (receiptUrl) parts.push(`Receipt: ${receiptUrl}.`);
|
|
1319
|
-
if (typeof result.cost === "number" && Number.isFinite(result.cost)) {
|
|
1320
|
-
parts.push(`Cost: $${result.cost.toFixed(2)}.`);
|
|
1321
|
-
}
|
|
1322
|
-
return parts.join(" ");
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
return null;
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
function buildHelpPayload(topic = "overview", options = {}) {
|
|
1329
|
-
const selectedTopic = String(topic || "overview");
|
|
1330
|
-
const localWalletsEnabled = Boolean(options.localWalletsEnabled);
|
|
1331
|
-
const hostedAuthEnabled = Boolean(options.hostedAuthEnabled);
|
|
1332
|
-
|
|
1333
|
-
const topics = {
|
|
1334
|
-
overview: {
|
|
1335
|
-
topic: "overview",
|
|
1336
|
-
summary:
|
|
1337
|
-
"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.",
|
|
1338
|
-
what_you_get: [
|
|
1339
|
-
"Built-in discovery tools for explore, search, detail lookup, and generic execute",
|
|
1340
|
-
"Dynamic API-specific tools generated from Apiosk listing metadata",
|
|
1341
|
-
"Autonomous payment with APIOSK_PRIVATE_KEY or a selected local wallet",
|
|
1342
|
-
localWalletsEnabled
|
|
1343
|
-
? "Local wallet and publish tools that do not require the dashboard"
|
|
1344
|
-
: hostedAuthEnabled
|
|
1345
|
-
? "Managed wallet CRUD and prepaid-credits tools over your authorized session; publishing is local/portal-only"
|
|
1346
|
-
: "Wallet and publish tools are disabled in this server mode unless local wallets are enabled",
|
|
1347
|
-
],
|
|
1348
|
-
next_steps: [
|
|
1349
|
-
localWalletsEnabled
|
|
1350
|
-
? "Use apiosk_get_started first to save a connect string or create a wallet and run a test call"
|
|
1351
|
-
: "Start with apiosk_search or apiosk_explore to discover APIs",
|
|
1352
|
-
"Use apiosk_explore or apiosk_search to find an API",
|
|
1353
|
-
"Use apiosk_payment_guide to learn exactly how to pay (buyer) or publish (provider)",
|
|
1354
|
-
"Use apiosk_get_api to inspect detail and metadata",
|
|
1355
|
-
"Use the API-specific dynamic tool or apiosk_execute to call it",
|
|
1356
|
-
localWalletsEnabled
|
|
1357
|
-
? "Use apiosk_wallet_create to create a local wallet for autonomous pay and publish"
|
|
1358
|
-
: "Run the local stdio package to unlock local wallet and publish tools",
|
|
1359
|
-
],
|
|
1360
|
-
},
|
|
1361
|
-
setup: {
|
|
1362
|
-
topic: "setup",
|
|
1363
|
-
summary: "Connect Apiosk MCP over remote HTTP or local stdio.",
|
|
1364
|
-
claude_code: [
|
|
1365
|
-
"Remote HTTP: claude mcp add --transport http apiosk https://mcp.apiosk.com/mcp",
|
|
1366
|
-
"Verify: claude mcp get apiosk",
|
|
1367
|
-
],
|
|
1368
|
-
local_stdio: [
|
|
1369
|
-
"npx -y @apiosk/mcp",
|
|
1370
|
-
"Or add a stdio config that runs npx -y @apiosk/mcp",
|
|
1371
|
-
"Then call apiosk_get_started to save a managed connect string or create a local wallet and run a test call",
|
|
1372
|
-
"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",
|
|
1373
|
-
],
|
|
1374
|
-
endpoint: "https://mcp.apiosk.com/mcp",
|
|
1375
|
-
local_wallet_note:
|
|
1376
|
-
"Local wallet creation, secret export, and publish tools are intended for the local stdio package. The public HTTP deployment keeps them disabled by default.",
|
|
1377
|
-
},
|
|
1378
|
-
auth: {
|
|
1379
|
-
topic: "auth",
|
|
1380
|
-
summary: "Apiosk supports multiple auth paths depending on how you want the agent to pay or identify itself.",
|
|
1381
|
-
auth_modes: [
|
|
1382
|
-
"APIOSK_PRIVATE_KEY: automatic x402 settlement for paid endpoints and signed publish requests",
|
|
1383
|
-
"Local wallet tools: create/select a wallet inside the MCP package and use it automatically for pay and publish",
|
|
1384
|
-
"APIOSK_CONNECT_TOKEN: dashboard-managed access token",
|
|
1385
|
-
"APIOSK_CONNECT_AUTHORIZATION: custom Authorization header",
|
|
1386
|
-
"APIOSK_WALLET_ADDRESS: wallet-aware flows without a token",
|
|
1387
|
-
"APIOSK_X_PAYMENT: manually attach a prebuilt x402 proof",
|
|
1388
|
-
"APIOSK_DASHBOARD_JWT or APIOSK_USER_JWT: unlock dashboard-managed wallet CRUD tools",
|
|
1389
|
-
"Saved local dashboard session from apiosk_sign_in or apiosk_create_account: reuse credits and managed-wallet tools without exporting a JWT manually",
|
|
1390
|
-
],
|
|
1391
|
-
notes: [
|
|
1392
|
-
"Connect token is the simplest managed option",
|
|
1393
|
-
"Private key or a selected local wallet is the best fit for autonomous paid execution",
|
|
1394
|
-
"Dashboard JWT is optional and only needed for managed-wallet dashboard endpoints",
|
|
1395
|
-
],
|
|
1396
|
-
},
|
|
1397
|
-
workflow: {
|
|
1398
|
-
topic: "workflow",
|
|
1399
|
-
summary: "Recommended usage pattern for agents working with Apiosk.",
|
|
1400
|
-
steps: [
|
|
1401
|
-
localWalletsEnabled
|
|
1402
|
-
? "Call apiosk_get_started first when you want the MCP package to configure local auth and prove the setup with a test call"
|
|
1403
|
-
: "Call apiosk_search or apiosk_explore to find a capability",
|
|
1404
|
-
"Call apiosk_explore to browse listing groups or apiosk_search to find a capability",
|
|
1405
|
-
"Read tool_name from search results when a dynamic tool is available",
|
|
1406
|
-
"Call apiosk_get_api when you need detail, docs_url, or listing metadata",
|
|
1407
|
-
"Use the dynamic tool directly for the cleanest invocation shape",
|
|
1408
|
-
"Use apiosk_execute when you need to force a slug, operation, query, or path_params envelope",
|
|
1409
|
-
localWalletsEnabled
|
|
1410
|
-
? "Create or select a local wallet before making paid calls or publishing APIs"
|
|
1411
|
-
: "Use APIOSK_PRIVATE_KEY if you need autonomous payment on the public server mode",
|
|
1412
|
-
],
|
|
1413
|
-
},
|
|
1414
|
-
discovery: {
|
|
1415
|
-
topic: "discovery",
|
|
1416
|
-
summary:
|
|
1417
|
-
"apiosk_discover searches the whole x402 ecosystem in ONE call and returns ranked endpoints. This is the full list of sources it can explore. Call it whenever the user wants real/live/paid data.",
|
|
1418
|
-
searched_by_default: [
|
|
1419
|
-
"apiosk — the Apiosk catalog: first-party listings PLUS federated externals (imported from providers' /.well-known/x402, the APILayer & ApyHub ecosystems, direct provider integrations, and selected MCP skills). Paid via apiosk_execute.",
|
|
1420
|
-
"bazaar — the Coinbase x402 Bazaar (~25k resources), searched LIVE every call. The central shared index most marketplaces + MCPs publish into. Paid via apiosk_fetch_paid.",
|
|
1421
|
-
],
|
|
1422
|
-
opt_in_pass_sources_or_all: [
|
|
1423
|
-
"x402-list — x402-list.com public directory (free REST).",
|
|
1424
|
-
"x402-direct — x402.direct search engine with trust scores (free REST).",
|
|
1425
|
-
"agentic-market — Coinbase Agentic.Market directory (free REST).",
|
|
1426
|
-
"thirdweb — public thirdweb Payments x402 resource index (free REST).",
|
|
1427
|
-
"payai — public PayAI facilitator discovery mirror (free REST).",
|
|
1428
|
-
"x402engine — direct manifest with paid AI/media/code/web endpoints.",
|
|
1429
|
-
"anchor-x402 — direct manifest with paid primitives and LLM endpoints.",
|
|
1430
|
-
"x402scan — paid full-text resource search; returned as an inspect/fetch-paid pointer, never auto-paid.",
|
|
1431
|
-
"apify — paid x402 prepaid-token endpoint plus the public Actor catalog; never auto-paid.",
|
|
1432
|
-
"wellknown — probe a specific host's /.well-known/x402 (needs probe_hosts).",
|
|
1433
|
-
"Use sources:['all'] to fan out to all directly wired free REST sources at once. Paid sources remain explicit opt-ins.",
|
|
1434
|
-
],
|
|
1435
|
-
indexed_or_reference_only: [
|
|
1436
|
-
"x402list.fun (paid MCP), awesome-x402 (markdown), and facilitators Nevermined / @swader / x402.rs — reference, not directly searchable.",
|
|
1437
|
-
],
|
|
1438
|
-
important_note:
|
|
1439
|
-
"These are DISCOVERY sources (where the agent LOOKS for endpoints) — NOT the same as a catalog listing's `source` field (where one existing entry was imported from). The Bazaar is queried by default, so external endpoints surface without passing sources; pass sources:['all'] to also sweep the other public directories.",
|
|
1440
|
-
how_to:
|
|
1441
|
-
"apiosk_discover({ query, segments, sources }) → one ranked list; each result tagged with `source`, `trust_tier`, and `executable_via`.",
|
|
1442
|
-
},
|
|
1443
|
-
payments: {
|
|
1444
|
-
topic: "payments",
|
|
1445
|
-
summary: "Paid Apiosk APIs can return x402 payment requirements if the client is not configured to settle automatically.",
|
|
1446
|
-
behavior: [
|
|
1447
|
-
"With APIOSK_PRIVATE_KEY configured, the SDK can auto-settle supported x402 flows",
|
|
1448
|
-
"With a selected local wallet, the MCP package uses that wallet automatically for paid execution",
|
|
1449
|
-
"Without auto-pay, paid calls return structured payment_required data",
|
|
1450
|
-
"The MCP server surfaces payment errors with hints that explain how to configure settlement",
|
|
1451
|
-
],
|
|
1452
|
-
recommended_setup: [
|
|
1453
|
-
"Use apiosk_get_started in the local stdio package for the shortest path from install to first paid-capable call",
|
|
1454
|
-
"Use apiosk_wallet_create and apiosk_wallet_select in the local stdio package for the cleanest autonomous workflow",
|
|
1455
|
-
"Use APIOSK_PRIVATE_KEY when you want deterministic wallet selection from environment variables",
|
|
1456
|
-
"Use APIOSK_CONNECT_TOKEN when access is managed in the dashboard",
|
|
1457
|
-
"Use the buyer portal to top up a prepaid credits balance and let the agent keep spending from it",
|
|
1458
|
-
],
|
|
1459
|
-
see_also:
|
|
1460
|
-
"Call apiosk_help with topic='rails' to learn how the same connect token settles over USDC and prepaid credits, including the rail fallback order.",
|
|
1461
|
-
},
|
|
1462
|
-
rails: {
|
|
1463
|
-
topic: "rails",
|
|
1464
|
-
summary:
|
|
1465
|
-
"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.",
|
|
1466
|
-
settlement_rails: [
|
|
1467
|
-
"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.",
|
|
1468
|
-
"credits: prepaid balance topped up once by a human via the buyer portal, then spent down per call.",
|
|
1469
|
-
],
|
|
1470
|
-
rail_fallback_order: [
|
|
1471
|
-
"1. USDC / x402 wallet when the agent can produce a payment proof.",
|
|
1472
|
-
"2. Prepaid credits balance.",
|
|
1473
|
-
"A 402 is only returned when none of the buyer's enabled rails can cover the call.",
|
|
1474
|
-
],
|
|
1475
|
-
connect_string_note:
|
|
1476
|
-
"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.",
|
|
1477
|
-
provider_settlement: {
|
|
1478
|
-
what_it_is:
|
|
1479
|
-
"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.",
|
|
1480
|
-
how_it_works: [
|
|
1481
|
-
"USDC earnings settle to the API's payout wallet (apis.wallet_address), which the portal links to a verified, Monerium-linked payout_wallets row.",
|
|
1482
|
-
"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.",
|
|
1483
|
-
],
|
|
1484
|
-
},
|
|
1485
|
-
},
|
|
1486
|
-
wallets: {
|
|
1487
|
-
topic: "wallets",
|
|
1488
|
-
summary: "Local wallet tools let Claude or Codex create, select, reveal, and save wallet secrets without opening the dashboard.",
|
|
1489
|
-
tools: [
|
|
1490
|
-
"apiosk_get_started",
|
|
1491
|
-
"apiosk_wallet_create",
|
|
1492
|
-
"apiosk_configure",
|
|
1493
|
-
"apiosk_wallet_list",
|
|
1494
|
-
"apiosk_wallet_select",
|
|
1495
|
-
"apiosk_wallet_reveal_secret",
|
|
1496
|
-
"apiosk_wallet_save_secret",
|
|
1497
|
-
],
|
|
1498
|
-
notes: [
|
|
1499
|
-
"The active wallet is mirrored to ~/.apiosk/wallet.json and ~/.apiosk/wallet.txt",
|
|
1500
|
-
"Use apiosk_wallet_reveal_secret only when the user explicitly asks to see the private key",
|
|
1501
|
-
"Use apiosk_wallet_save_secret to write a backup file without showing the secret in chat",
|
|
1502
|
-
],
|
|
1503
|
-
},
|
|
1504
|
-
publish: {
|
|
1505
|
-
topic: "publish",
|
|
1506
|
-
summary: "Apiosk publish tools sign gateway management requests directly with the active local wallet or APIOSK_PRIVATE_KEY.",
|
|
1507
|
-
tools: [
|
|
1508
|
-
"apiosk_publish_api",
|
|
1509
|
-
"apiosk_list_my_apis",
|
|
1510
|
-
"apiosk_update_api",
|
|
1511
|
-
"apiosk_delete_api",
|
|
1512
|
-
],
|
|
1513
|
-
requirements: [
|
|
1514
|
-
"A signing wallet must be available from a selected local wallet or APIOSK_PRIVATE_KEY",
|
|
1515
|
-
"endpoint_url must use HTTPS",
|
|
1516
|
-
"slug must use lowercase letters, numbers, and hyphens",
|
|
1517
|
-
],
|
|
1518
|
-
identity_note:
|
|
1519
|
-
"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.",
|
|
1520
|
-
},
|
|
1521
|
-
configure: {
|
|
1522
|
-
topic: "configure",
|
|
1523
|
-
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.",
|
|
1524
|
-
examples: [
|
|
1525
|
-
"{ \"section\": \"funding\" }",
|
|
1526
|
-
"{ \"wallet_id\": \"...\", \"section\": \"security\" }",
|
|
1527
|
-
"{ \"section\": \"funding\", \"funding_provider\": \"onramper\" }",
|
|
1528
|
-
],
|
|
1529
|
-
},
|
|
1530
|
-
};
|
|
1531
|
-
|
|
1532
|
-
const fallback =
|
|
1533
|
-
topics[selectedTopic] ||
|
|
1534
|
-
{
|
|
1535
|
-
topic: "overview",
|
|
1536
|
-
error: `Unknown help topic: ${selectedTopic}`,
|
|
1537
|
-
available_topics: Object.keys(topics),
|
|
1538
|
-
};
|
|
1539
|
-
|
|
1540
|
-
return {
|
|
1541
|
-
...fallback,
|
|
1542
|
-
available_topics: Object.keys(topics),
|
|
1543
|
-
};
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
function hasExecuteEnvelope(argumentsObject) {
|
|
1547
|
-
return Boolean(
|
|
1548
|
-
argumentsObject &&
|
|
1549
|
-
typeof argumentsObject === "object" &&
|
|
1550
|
-
("operation" in argumentsObject ||
|
|
1551
|
-
"input" in argumentsObject ||
|
|
1552
|
-
"query" in argumentsObject ||
|
|
1553
|
-
"path_params" in argumentsObject)
|
|
1554
|
-
);
|
|
1555
|
-
}
|
|
1556
|
-
|
|
1557
|
-
function validateSlug(slug) {
|
|
1558
|
-
return /^[a-z0-9-]+$/.test(String(slug || ""));
|
|
1559
|
-
}
|
|
1560
|
-
|
|
1561
|
-
function buildWalletToolArguments(wallet, extra = {}) {
|
|
1562
|
-
const base = { ...extra };
|
|
1563
|
-
if (wallet?.source === "local_store" && wallet?.id) {
|
|
1564
|
-
base.wallet_id = wallet.id;
|
|
1565
|
-
}
|
|
1566
|
-
return base;
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
|
-
function normalizePrivateKeyHex(value) {
|
|
1570
|
-
const trimmed = trimString(value);
|
|
1571
|
-
if (!trimmed) return "";
|
|
1572
|
-
|
|
1573
|
-
const normalized = trimmed.startsWith("0x") ? trimmed : `0x${trimmed}`;
|
|
1574
|
-
if (!/^0x[0-9a-fA-F]{64}$/.test(normalized)) {
|
|
1575
|
-
throw new Error("APIOSK_PRIVATE_KEY must be a 32-byte hex private key.");
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
return normalized;
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
function resolveEnvPrivateWallet(env = process.env) {
|
|
1582
|
-
const privateKey = normalizePrivateKeyHex(env.APIOSK_PRIVATE_KEY);
|
|
1583
|
-
if (!privateKey) return null;
|
|
1584
|
-
|
|
1585
|
-
const account = privateKeyToAccount(privateKey);
|
|
1586
|
-
const derivedAddress = account.address.toLowerCase();
|
|
1587
|
-
const configuredAddress = trimString(env.APIOSK_WALLET_ADDRESS).toLowerCase();
|
|
1588
|
-
|
|
1589
|
-
if (configuredAddress && configuredAddress !== derivedAddress) {
|
|
1590
|
-
throw new Error("APIOSK_WALLET_ADDRESS does not match APIOSK_PRIVATE_KEY.");
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
return {
|
|
1594
|
-
address: derivedAddress,
|
|
1595
|
-
private_key: privateKey,
|
|
1596
|
-
label: "Environment wallet",
|
|
1597
|
-
source: "env_private_key",
|
|
1598
|
-
};
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
31
|
export function createApioskMcpRuntime(options = {}) {
|
|
1602
32
|
const env = options.env || process.env;
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
const providedDashboardManager = options.walletManager || null;
|
|
1606
|
-
const hostedAuthEnabled = options.hostedAuthEnabled === true;
|
|
1607
|
-
const localWalletStore =
|
|
1608
|
-
hostedAuthEnabled
|
|
1609
|
-
? null
|
|
1610
|
-
: options.localWalletStore ||
|
|
1611
|
-
(options.enableLocalWallets === false ? null : createLocalWalletStore(env));
|
|
1612
|
-
const cache = {
|
|
1613
|
-
catalog: null,
|
|
1614
|
-
expiresAt: 0,
|
|
1615
|
-
dynamicTools: null,
|
|
1616
|
-
toolIndex: new Map(),
|
|
1617
|
-
toolNamesBySlug: new Map(),
|
|
1618
|
-
};
|
|
1619
|
-
|
|
1620
|
-
async function getActiveExecutionWallet() {
|
|
1621
|
-
if (hostedAuthEnabled) return null;
|
|
1622
|
-
try {
|
|
1623
|
-
const envWallet = resolveEnvPrivateWallet(env);
|
|
1624
|
-
if (envWallet) return envWallet;
|
|
1625
|
-
} catch (error) {
|
|
1626
|
-
throw error;
|
|
1627
|
-
}
|
|
1628
|
-
|
|
1629
|
-
if (!localWalletStore) return null;
|
|
1630
|
-
return localWalletStore.resolveSigningWallet();
|
|
1631
|
-
}
|
|
1632
|
-
|
|
1633
|
-
async function getSavedConfig() {
|
|
1634
|
-
if (hostedAuthEnabled) return null;
|
|
1635
|
-
return readLocalApioskConfig(env);
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
// Summarize whether the agent can settle a paid call right now, and how, so
|
|
1639
|
-
// discovery responses and apiosk_payment_guide can give tailored buyer
|
|
1640
|
-
// instructions without a second round trip.
|
|
1641
|
-
async function resolvePaymentCapability(authInfo = null) {
|
|
1642
|
-
const savedConfig = await getSavedConfig().catch(() => null);
|
|
1643
|
-
let activeWallet = null;
|
|
1644
|
-
try {
|
|
1645
|
-
activeWallet = await getActiveExecutionWallet();
|
|
1646
|
-
} catch {
|
|
1647
|
-
activeWallet = null;
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
const requestConnectToken = trimString(authInfo?.extra?.apiosk_connect_token);
|
|
1651
|
-
const envConnectToken = trimString(env.APIOSK_CONNECT_TOKEN || savedConfig?.connect_token);
|
|
1652
|
-
const envAuthorization = trimString(
|
|
1653
|
-
env.APIOSK_CONNECT_AUTHORIZATION || savedConfig?.connect_authorization
|
|
1654
|
-
);
|
|
1655
|
-
const envWalletAddress = trimString(env.APIOSK_WALLET_ADDRESS).toLowerCase();
|
|
1656
|
-
const savedWalletAddress = trimString(savedConfig?.agent_wallet_address).toLowerCase();
|
|
1657
|
-
|
|
1658
|
-
let method = "none";
|
|
1659
|
-
if (activeWallet?.source === "env_private_key") method = "env_private_key";
|
|
1660
|
-
else if (activeWallet?.source === "local_store") method = "local_wallet";
|
|
1661
|
-
else if (requestConnectToken) method = "request_connect_token";
|
|
1662
|
-
else if (envConnectToken || envAuthorization) method = "connect_token";
|
|
1663
|
-
else if (envWalletAddress || savedWalletAddress || activeWallet?.address) method = "wallet_address";
|
|
1664
|
-
|
|
1665
|
-
const mode = hostedAuthEnabled ? "hosted" : localWalletStore ? "local" : "remote";
|
|
1666
|
-
|
|
1667
|
-
return {
|
|
1668
|
-
method,
|
|
1669
|
-
ready: ["env_private_key", "local_wallet", "connect_token", "request_connect_token"].includes(
|
|
1670
|
-
method
|
|
1671
|
-
),
|
|
1672
|
-
wallet_address: activeWallet?.address || envWalletAddress || savedWalletAddress || null,
|
|
1673
|
-
has_connect_token: Boolean(requestConnectToken || envConnectToken),
|
|
1674
|
-
local_wallets_enabled: Boolean(localWalletStore),
|
|
1675
|
-
mode,
|
|
1676
|
-
};
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
function hasConfiguredDashboardAccess(authInfo = null) {
|
|
1680
|
-
if (hasRequestScopedDashboardAccess(authInfo)) {
|
|
1681
|
-
return true;
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
if (providedDashboardManager?.isConfigured) {
|
|
1685
|
-
return Boolean(providedDashboardManager.isConfigured());
|
|
1686
|
-
}
|
|
1687
|
-
|
|
1688
|
-
return Boolean(resolveDashboardUserToken(env));
|
|
1689
|
-
}
|
|
1690
|
-
|
|
1691
|
-
async function requestDashboard(path, init = {}, options = {}, authInfo = null) {
|
|
1692
|
-
const requireAuth = options.requireAuth !== false;
|
|
1693
|
-
const requestScopedToken = resolveRequestDashboardUserToken(authInfo);
|
|
1694
|
-
|
|
1695
|
-
if (providedDashboardManager && !requestScopedToken) {
|
|
1696
|
-
return providedDashboardManager.request(path, init);
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
const savedConfig = await getSavedConfig();
|
|
1700
|
-
const dashboardUrl = resolveControlPlaneUrl(env, savedConfig);
|
|
1701
|
-
const token = requestScopedToken || resolveDashboardUserToken(env, savedConfig);
|
|
1702
|
-
|
|
1703
|
-
if (requireAuth && !token) {
|
|
1704
|
-
throw new Error(
|
|
1705
|
-
hostedAuthEnabled ?
|
|
1706
|
-
"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." :
|
|
1707
|
-
"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."
|
|
1708
|
-
);
|
|
1709
|
-
}
|
|
1710
|
-
|
|
1711
|
-
const headers = new Headers(init.headers || {});
|
|
1712
|
-
headers.set("accept", "application/json");
|
|
1713
|
-
|
|
1714
|
-
if (token) {
|
|
1715
|
-
headers.set("x-apiosk-user-jwt", token);
|
|
1716
|
-
headers.set("authorization", `Bearer ${token}`);
|
|
1717
|
-
}
|
|
1718
|
-
|
|
1719
|
-
let body = init.body;
|
|
1720
|
-
if (body !== undefined && body !== null && typeof body !== "string") {
|
|
1721
|
-
headers.set("content-type", "application/json");
|
|
1722
|
-
body = JSON.stringify(body);
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
|
-
const response = await fetch(`${dashboardUrl}${path}`, {
|
|
1726
|
-
...init,
|
|
1727
|
-
body,
|
|
1728
|
-
cache: "no-store",
|
|
1729
|
-
headers,
|
|
1730
|
-
});
|
|
1731
|
-
|
|
1732
|
-
const text = await response.text();
|
|
1733
|
-
let payload = null;
|
|
1734
|
-
try {
|
|
1735
|
-
payload = text ? JSON.parse(text) : null;
|
|
1736
|
-
} catch {
|
|
1737
|
-
payload = text || null;
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
if (!response.ok) {
|
|
1741
|
-
throw new Error(pickMessage(payload, `Dashboard request failed with HTTP ${response.status}`));
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
return payload;
|
|
1745
|
-
}
|
|
1746
|
-
|
|
1747
|
-
async function getClient(authInfo = null) {
|
|
1748
|
-
if (fixedClient) {
|
|
1749
|
-
return fixedClient;
|
|
1750
|
-
}
|
|
1751
|
-
|
|
1752
|
-
const savedConfig = await getSavedConfig();
|
|
1753
|
-
const activeWallet = await getActiveExecutionWallet();
|
|
1754
|
-
const requestScopedHeaders = buildRequestScopedDashboardHeaders(authInfo);
|
|
1755
|
-
const envWalletAddress = trimString(env.APIOSK_WALLET_ADDRESS).toLowerCase();
|
|
1756
|
-
const savedWalletAddress = trimString(savedConfig?.agent_wallet_address).toLowerCase();
|
|
1757
|
-
const privateKey = activeWallet?.private_key || normalizePrivateKeyHex(env.APIOSK_PRIVATE_KEY) || undefined;
|
|
1758
|
-
const walletAddress = privateKey
|
|
1759
|
-
? activeWallet?.address
|
|
1760
|
-
: envWalletAddress || activeWallet?.address || savedWalletAddress;
|
|
1761
|
-
|
|
1762
|
-
// Hosted MCP path: a headless caller authenticated by presenting an
|
|
1763
|
-
// Apiosk connect token. oauth.mjs stashes it on req.auth.extra so we
|
|
1764
|
-
// can thread it back through to the gateway as X-Apiosk-Connect-Token,
|
|
1765
|
-
// which lets the gateway settle the call via the buyer's managed
|
|
1766
|
-
// wallet (no APIOSK_PRIVATE_KEY needed). This is the request-scoped
|
|
1767
|
-
// override; it beats the per-process env/saved-config token.
|
|
1768
|
-
const requestConnectToken = trimString(authInfo?.extra?.apiosk_connect_token);
|
|
1769
|
-
const envConnectToken = trimString(env.APIOSK_CONNECT_TOKEN || savedConfig?.connect_token);
|
|
1770
|
-
const effectiveConnectToken = requestConnectToken || envConnectToken || undefined;
|
|
1771
|
-
const requestAuthorization = requestConnectToken ? `Bearer ${requestConnectToken}` : undefined;
|
|
1772
|
-
const envAuthorization = trimString(env.APIOSK_CONNECT_AUTHORIZATION || savedConfig?.connect_authorization);
|
|
1773
|
-
const effectiveAuthorization = requestAuthorization || envAuthorization || undefined;
|
|
1774
|
-
|
|
1775
|
-
const clientOptions = {
|
|
1776
|
-
baseUrl: resolveGatewayBaseUrl(env, savedConfig),
|
|
1777
|
-
connectToken: effectiveConnectToken,
|
|
1778
|
-
connectHeaderName:
|
|
1779
|
-
trimString(env.APIOSK_CONNECT_HEADER_NAME || savedConfig?.connect_header_name) || undefined,
|
|
1780
|
-
authorization: effectiveAuthorization,
|
|
1781
|
-
walletAddress: walletAddress || undefined,
|
|
1782
|
-
xPayment: trimString(env.APIOSK_X_PAYMENT) || undefined,
|
|
1783
|
-
privateKey,
|
|
1784
|
-
headers: requestScopedHeaders || undefined,
|
|
1785
|
-
};
|
|
1786
|
-
|
|
1787
|
-
if (clientFactory) {
|
|
1788
|
-
return await clientFactory(clientOptions);
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
|
-
return new ApioskClient(clientOptions);
|
|
1792
|
-
}
|
|
1793
|
-
|
|
1794
|
-
function getStaticTools(authInfo = null) {
|
|
1795
|
-
if (hostedAuthEnabled) {
|
|
1796
|
-
// Escape hatch: expose the full 28-tool surface when explicitly opted in.
|
|
1797
|
-
if (env.APIOSK_MCP_FULL_TOOLS === "true") {
|
|
1798
|
-
return [...HOSTED_REMOTE_TOOLS];
|
|
1799
|
-
}
|
|
1800
|
-
// Providers authenticate with a sk_live_ key → focused publishing surface.
|
|
1801
|
-
if (trimString(authInfo?.extra?.apiosk_provider_key)) {
|
|
1802
|
-
return [...HOSTED_PROVIDER_TOOLS];
|
|
1803
|
-
}
|
|
1804
|
-
// Buyers (OAuth / connect token / pre-auth) → the lean agentic surface.
|
|
1805
|
-
return [...HOSTED_BUYER_TOOLS];
|
|
1806
|
-
}
|
|
1807
|
-
|
|
1808
|
-
const tools = [...DISCOVERY_TOOLS];
|
|
1809
|
-
|
|
1810
|
-
if (localWalletStore) {
|
|
1811
|
-
tools.push(...LOCAL_WALLET_TOOLS, ...LOCAL_ACCOUNT_AND_CREDITS_TOOLS, ...DASHBOARD_WALLET_TOOLS, ...PUBLISH_TOOLS);
|
|
1812
|
-
} else if (hasConfiguredDashboardAccess(authInfo)) {
|
|
1813
|
-
tools.push(...DASHBOARD_WALLET_TOOLS);
|
|
1814
|
-
}
|
|
1815
|
-
|
|
1816
|
-
// Provider-token x402 publishing works in every mode: hosted callers pass
|
|
1817
|
-
// Authorization: Bearer sk_live_…, stdio callers set APIOSK_PROVIDER_TOKEN.
|
|
1818
|
-
tools.push(...PUBLISHER_TOOLS);
|
|
1819
|
-
|
|
1820
|
-
// External x402 pay works with any connect token (env APIOSK_CONNECT_TOKEN in
|
|
1821
|
-
// stdio, or a request-scoped token on the hosted surface).
|
|
1822
|
-
tools.push(...EXTERNAL_PAY_TOOLS);
|
|
1823
|
-
|
|
1824
|
-
return tools;
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1827
|
-
async function getCatalog(force = false, authInfo = null) {
|
|
1828
|
-
if (!force && cache.catalog && Date.now() < cache.expiresAt) {
|
|
1829
|
-
return cache.catalog;
|
|
1830
|
-
}
|
|
1831
|
-
|
|
1832
|
-
const client = await getClient(authInfo);
|
|
1833
|
-
const response = await client.listApis({ limit: 500, offset: 0 });
|
|
1834
|
-
cache.catalog = response.apis || [];
|
|
1835
|
-
cache.expiresAt = Date.now() + CACHE_TTL_MS;
|
|
1836
|
-
cache.dynamicTools = null;
|
|
1837
|
-
cache.toolIndex = new Map();
|
|
1838
|
-
cache.toolNamesBySlug = new Map();
|
|
1839
|
-
return cache.catalog;
|
|
1840
|
-
}
|
|
1841
|
-
|
|
1842
|
-
async function resolveExecutableApiSlug(slug, authInfo = null) {
|
|
1843
|
-
const requested = normalizeCatalogToken(slug);
|
|
1844
|
-
if (!requested) return slug;
|
|
1845
|
-
|
|
1846
|
-
const catalog = await getCatalog(false, authInfo);
|
|
1847
|
-
const exact = catalog.find((api) => normalizeCatalogToken(api.slug) === requested);
|
|
1848
|
-
if (exact) return exact.slug;
|
|
1849
|
-
|
|
1850
|
-
const aliases = new Map([
|
|
1851
|
-
["weather", "open-meteo"],
|
|
1852
|
-
["weather-api", "open-meteo"],
|
|
1853
|
-
["hugen-weather", "open-meteo"],
|
|
1854
|
-
["open-meteo-weather", "open-meteo"],
|
|
1855
|
-
]);
|
|
1856
|
-
const alias = aliases.get(requested);
|
|
1857
|
-
if (alias && catalog.some((api) => normalizeCatalogToken(api.slug) === alias)) {
|
|
1858
|
-
return alias;
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1861
|
-
if (isWeatherLikeSlug(requested)) {
|
|
1862
|
-
const weather = catalog.find((api) => {
|
|
1863
|
-
const haystack = [
|
|
1864
|
-
api.slug,
|
|
1865
|
-
api.name,
|
|
1866
|
-
api.description,
|
|
1867
|
-
api.category,
|
|
1868
|
-
api.raw_category,
|
|
1869
|
-
...(Array.isArray(api.listing_metadata?.tags) ? api.listing_metadata.tags : []),
|
|
1870
|
-
]
|
|
1871
|
-
.map(normalizeCatalogToken)
|
|
1872
|
-
.join(" ");
|
|
1873
|
-
|
|
1874
|
-
return (
|
|
1875
|
-
api.active !== false &&
|
|
1876
|
-
api.verified !== false &&
|
|
1877
|
-
/weather|meteo|forecast/.test(haystack)
|
|
1878
|
-
);
|
|
1879
|
-
});
|
|
1880
|
-
|
|
1881
|
-
if (weather?.slug) return weather.slug;
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
return slug;
|
|
1885
|
-
}
|
|
1886
|
-
|
|
1887
|
-
async function getTools(force = false, authInfo = null) {
|
|
1888
|
-
if (!force && cache.dynamicTools && Date.now() < cache.expiresAt) {
|
|
1889
|
-
return [...getStaticTools(authInfo), ...cache.dynamicTools];
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
|
-
const catalog = await getCatalog(force, authInfo);
|
|
1893
|
-
const { tools, toolIndex } = buildDynamicTools(catalog, ALL_STATIC_TOOLS);
|
|
1894
|
-
|
|
1895
|
-
cache.dynamicTools = tools;
|
|
1896
|
-
cache.toolIndex = toolIndex;
|
|
1897
|
-
cache.toolNamesBySlug = new Map(
|
|
1898
|
-
Array.from(toolIndex.values()).map((entry) => [entry.api.slug, entry.toolName])
|
|
1899
|
-
);
|
|
1900
|
-
|
|
1901
|
-
return [...getStaticTools(authInfo), ...tools];
|
|
1902
|
-
}
|
|
1903
|
-
|
|
1904
|
-
async function resolveConfigurationWallet(walletId = null) {
|
|
1905
|
-
const targetWalletId = trimString(walletId);
|
|
1906
|
-
|
|
1907
|
-
if (targetWalletId && localWalletStore) {
|
|
1908
|
-
const wallet = await localWalletStore.getWalletRecord(targetWalletId);
|
|
1909
|
-
return {
|
|
1910
|
-
...wallet,
|
|
1911
|
-
source: "local_store",
|
|
1912
|
-
};
|
|
1913
|
-
}
|
|
1914
|
-
|
|
1915
|
-
if (localWalletStore) {
|
|
1916
|
-
const activeWallet = await localWalletStore.resolveActiveWallet();
|
|
1917
|
-
if (activeWallet) {
|
|
1918
|
-
return {
|
|
1919
|
-
...activeWallet,
|
|
1920
|
-
source: "local_store",
|
|
1921
|
-
};
|
|
1922
|
-
}
|
|
1923
|
-
}
|
|
1924
|
-
|
|
1925
|
-
const envWallet = resolveEnvPrivateWallet(env);
|
|
1926
|
-
if (envWallet) {
|
|
1927
|
-
return {
|
|
1928
|
-
id: null,
|
|
1929
|
-
label: envWallet.label || "Environment wallet",
|
|
1930
|
-
address: envWallet.address,
|
|
1931
|
-
active: true,
|
|
1932
|
-
source: envWallet.source,
|
|
1933
|
-
};
|
|
1934
|
-
}
|
|
1935
|
-
|
|
1936
|
-
return null;
|
|
1937
|
-
}
|
|
1938
|
-
|
|
1939
|
-
function buildApioskOptionsMenu(wallet, funding) {
|
|
1940
|
-
const walletArgs = buildWalletToolArguments(wallet);
|
|
1941
|
-
const fundingWalletArgs = buildWalletToolArguments(wallet, { section: "funding" });
|
|
1942
|
-
|
|
1943
|
-
return {
|
|
1944
|
-
title: "Apiosk Control Menu",
|
|
1945
|
-
description: wallet
|
|
1946
|
-
? "Use the sections below to fund the wallet, explore APIs, pay for calls, publish listings, and manage local Apiosk data."
|
|
1947
|
-
: "Create or select a wallet first, then use the sections below to fund it and manage Apiosk data.",
|
|
1948
|
-
sections: [
|
|
1949
|
-
{
|
|
1950
|
-
id: "wallet",
|
|
1951
|
-
label: "Wallet",
|
|
1952
|
-
options: [
|
|
1953
|
-
{
|
|
1954
|
-
label: "Get started",
|
|
1955
|
-
tool: "apiosk_get_started",
|
|
1956
|
-
arguments: buildWalletToolArguments(wallet, {}),
|
|
1957
|
-
},
|
|
1958
|
-
{
|
|
1959
|
-
label: "List wallets",
|
|
1960
|
-
tool: "apiosk_wallet_list",
|
|
1961
|
-
arguments: {},
|
|
1962
|
-
},
|
|
1963
|
-
{
|
|
1964
|
-
label: "Create wallet",
|
|
1965
|
-
tool: "apiosk_wallet_create",
|
|
1966
|
-
arguments: {},
|
|
1967
|
-
},
|
|
1968
|
-
{
|
|
1969
|
-
label: "Select active wallet",
|
|
1970
|
-
tool: "apiosk_wallet_select",
|
|
1971
|
-
arguments: walletArgs,
|
|
1972
|
-
available: Boolean(wallet?.source === "local_store"),
|
|
1973
|
-
},
|
|
1974
|
-
],
|
|
1975
|
-
},
|
|
1976
|
-
{
|
|
1977
|
-
id: "funding",
|
|
1978
|
-
label: "Fund Wallet",
|
|
1979
|
-
options: [
|
|
1980
|
-
{
|
|
1981
|
-
label: "Show funding QR and Base deposit instructions",
|
|
1982
|
-
tool: "apiosk_configure",
|
|
1983
|
-
arguments: fundingWalletArgs,
|
|
1984
|
-
available: Boolean(wallet),
|
|
1985
|
-
},
|
|
1986
|
-
{
|
|
1987
|
-
label: "Request Onramper checkout",
|
|
1988
|
-
tool: "apiosk_configure",
|
|
1989
|
-
arguments: buildWalletToolArguments(wallet, {
|
|
1990
|
-
section: "funding",
|
|
1991
|
-
funding_provider: "onramper",
|
|
1992
|
-
}),
|
|
1993
|
-
available: Boolean(funding?.providers?.find((provider) => provider.id === "onramper")?.available),
|
|
1994
|
-
},
|
|
1995
|
-
{
|
|
1996
|
-
label: "Request Transak checkout",
|
|
1997
|
-
tool: "apiosk_configure",
|
|
1998
|
-
arguments: buildWalletToolArguments(wallet, {
|
|
1999
|
-
section: "funding",
|
|
2000
|
-
funding_provider: "transak",
|
|
2001
|
-
}),
|
|
2002
|
-
available: Boolean(funding?.providers?.find((provider) => provider.id === "transak")?.available),
|
|
2003
|
-
},
|
|
2004
|
-
],
|
|
2005
|
-
},
|
|
2006
|
-
{
|
|
2007
|
-
id: "payments",
|
|
2008
|
-
label: "Explore And Pay",
|
|
2009
|
-
options: [
|
|
2010
|
-
{
|
|
2011
|
-
label: "Browse listing groups",
|
|
2012
|
-
tool: "apiosk_explore",
|
|
2013
|
-
arguments: {},
|
|
2014
|
-
},
|
|
2015
|
-
{
|
|
2016
|
-
label: "Search APIs",
|
|
2017
|
-
tool: "apiosk_search",
|
|
2018
|
-
arguments: {},
|
|
2019
|
-
},
|
|
2020
|
-
{
|
|
2021
|
-
label: "Execute an API",
|
|
2022
|
-
tool: "apiosk_execute",
|
|
2023
|
-
arguments: { slug: "<api-slug>" },
|
|
2024
|
-
},
|
|
2025
|
-
],
|
|
2026
|
-
},
|
|
2027
|
-
{
|
|
2028
|
-
id: "publish",
|
|
2029
|
-
label: "Publish And Manage",
|
|
2030
|
-
options: [
|
|
2031
|
-
{
|
|
2032
|
-
label: "Publish a new API",
|
|
2033
|
-
tool: "apiosk_publish_api",
|
|
2034
|
-
arguments: buildWalletToolArguments(wallet, {
|
|
2035
|
-
name: "<name>",
|
|
2036
|
-
slug: "<slug>",
|
|
2037
|
-
endpoint_url: "https://example.com",
|
|
2038
|
-
price_usd: 0.01,
|
|
2039
|
-
description: "<description>",
|
|
2040
|
-
}),
|
|
2041
|
-
available: Boolean(wallet),
|
|
2042
|
-
},
|
|
2043
|
-
{
|
|
2044
|
-
label: "List my APIs",
|
|
2045
|
-
tool: "apiosk_list_my_apis",
|
|
2046
|
-
arguments: walletArgs,
|
|
2047
|
-
available: Boolean(wallet),
|
|
2048
|
-
},
|
|
2049
|
-
{
|
|
2050
|
-
label: "Update an API",
|
|
2051
|
-
tool: "apiosk_update_api",
|
|
2052
|
-
arguments: buildWalletToolArguments(wallet, { slug: "<slug>" }),
|
|
2053
|
-
available: Boolean(wallet),
|
|
2054
|
-
},
|
|
2055
|
-
],
|
|
2056
|
-
},
|
|
2057
|
-
{
|
|
2058
|
-
id: "security",
|
|
2059
|
-
label: "Security",
|
|
2060
|
-
options: [
|
|
2061
|
-
{
|
|
2062
|
-
label: "Reveal secret key",
|
|
2063
|
-
tool: "apiosk_wallet_reveal_secret",
|
|
2064
|
-
arguments: walletArgs,
|
|
2065
|
-
available: Boolean(wallet?.source === "local_store" && wallet?.id),
|
|
2066
|
-
},
|
|
2067
|
-
{
|
|
2068
|
-
label: "Save secret key to file",
|
|
2069
|
-
tool: "apiosk_wallet_save_secret",
|
|
2070
|
-
arguments: walletArgs,
|
|
2071
|
-
available: Boolean(wallet?.source === "local_store" && wallet?.id),
|
|
2072
|
-
},
|
|
2073
|
-
],
|
|
2074
|
-
},
|
|
2075
|
-
{
|
|
2076
|
-
id: "data",
|
|
2077
|
-
label: "Local Data",
|
|
2078
|
-
options: [
|
|
2079
|
-
{
|
|
2080
|
-
label: "Show local data paths and wallet status",
|
|
2081
|
-
tool: "apiosk_configure",
|
|
2082
|
-
arguments: buildWalletToolArguments(wallet, { section: "data" }),
|
|
2083
|
-
},
|
|
2084
|
-
],
|
|
2085
|
-
},
|
|
2086
|
-
],
|
|
2087
|
-
};
|
|
2088
|
-
}
|
|
2089
|
-
|
|
2090
|
-
async function buildConfigurePayload(argumentsObject = {}) {
|
|
2091
|
-
const wallet = await resolveConfigurationWallet(argumentsObject.wallet_id);
|
|
2092
|
-
const funding = wallet
|
|
2093
|
-
? await buildFundingOptions({
|
|
2094
|
-
wallet,
|
|
2095
|
-
env,
|
|
2096
|
-
fundingProvider: argumentsObject.funding_provider,
|
|
2097
|
-
includeQrDataUrl: argumentsObject.include_qr_data_url === true,
|
|
2098
|
-
})
|
|
2099
|
-
: null;
|
|
2100
|
-
|
|
2101
|
-
const dataPaths = localWalletStore
|
|
2102
|
-
? {
|
|
2103
|
-
store_file: localWalletStore.paths.storeFile,
|
|
2104
|
-
wallet_json_file: localWalletStore.paths.activeWalletJsonFile,
|
|
2105
|
-
wallet_txt_file: localWalletStore.paths.activeWalletTextFile,
|
|
2106
|
-
secret_export_dir: localWalletStore.paths.secretExportDir,
|
|
2107
|
-
}
|
|
2108
|
-
: null;
|
|
2109
|
-
|
|
2110
|
-
const section = trimString(argumentsObject.section || "overview").toLowerCase() || "overview";
|
|
2111
|
-
const menu = buildApioskOptionsMenu(wallet, funding);
|
|
2112
|
-
const sections = {
|
|
2113
|
-
overview: {
|
|
2114
|
-
wallet,
|
|
2115
|
-
funding_preview: funding?.receive_on_base || null,
|
|
2116
|
-
next_steps: wallet
|
|
2117
|
-
? [
|
|
2118
|
-
"Fund the wallet on Base with ETH or USDC.",
|
|
2119
|
-
"Use apiosk_search or apiosk_explore to find an API.",
|
|
2120
|
-
"Use apiosk_publish_api when you want to list your own API.",
|
|
2121
|
-
]
|
|
2122
|
-
: [
|
|
2123
|
-
"Create or select a wallet first.",
|
|
2124
|
-
"Then reopen apiosk_configure with section='funding' to fund it.",
|
|
2125
|
-
],
|
|
2126
|
-
},
|
|
2127
|
-
funding,
|
|
2128
|
-
wallet: {
|
|
2129
|
-
wallet,
|
|
2130
|
-
local_wallet_tools_enabled: Boolean(localWalletStore),
|
|
2131
|
-
dashboard_wallet_tools_enabled: hasConfiguredDashboardAccess(),
|
|
2132
|
-
},
|
|
2133
|
-
payments: {
|
|
2134
|
-
wallet,
|
|
2135
|
-
active_payment_source: wallet ? `${wallet.label || wallet.address} (${wallet.source})` : null,
|
|
2136
|
-
recommendation: wallet
|
|
2137
|
-
? "This wallet will be used for autonomous paid execution unless APIOSK_PRIVATE_KEY overrides it."
|
|
2138
|
-
: "No active wallet is available yet.",
|
|
2139
|
-
},
|
|
2140
|
-
publish: {
|
|
2141
|
-
wallet,
|
|
2142
|
-
requirements: [
|
|
2143
|
-
"A signing wallet must be active.",
|
|
2144
|
-
"endpoint_url must use HTTPS.",
|
|
2145
|
-
"slug must use lowercase letters, numbers, and hyphens.",
|
|
2146
|
-
],
|
|
2147
|
-
},
|
|
2148
|
-
data: {
|
|
2149
|
-
wallet,
|
|
2150
|
-
data_paths: dataPaths,
|
|
2151
|
-
},
|
|
2152
|
-
security: {
|
|
2153
|
-
wallet,
|
|
2154
|
-
rules: [
|
|
2155
|
-
"Only reveal or export the private key when the user explicitly asks.",
|
|
2156
|
-
"The active local wallet is mirrored to ~/.apiosk/wallet.json and ~/.apiosk/wallet.txt.",
|
|
2157
|
-
"Exported secret-key files should be treated as sensitive credentials.",
|
|
2158
|
-
],
|
|
2159
|
-
},
|
|
2160
|
-
};
|
|
2161
|
-
|
|
2162
|
-
return {
|
|
2163
|
-
selected_section: Object.prototype.hasOwnProperty.call(sections, section) ? section : "overview",
|
|
2164
|
-
wallet,
|
|
2165
|
-
funding,
|
|
2166
|
-
options_menu: menu,
|
|
2167
|
-
sections,
|
|
2168
|
-
section_payload:
|
|
2169
|
-
sections[Object.prototype.hasOwnProperty.call(sections, section) ? section : "overview"],
|
|
2170
|
-
};
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
async function resolveDynamicTool(name, authInfo = null) {
|
|
2174
|
-
await getTools(false, authInfo);
|
|
2175
|
-
if (cache.toolIndex.has(name)) {
|
|
2176
|
-
return cache.toolIndex.get(name);
|
|
2177
|
-
}
|
|
2178
|
-
|
|
2179
|
-
await getTools(true, authInfo);
|
|
2180
|
-
return cache.toolIndex.get(name) || null;
|
|
2181
|
-
}
|
|
2182
|
-
|
|
2183
|
-
function isDynamicToolProtectedApi(api) {
|
|
2184
|
-
const price = Number(api?.price_usd ?? api?.listing_metadata?.cost_per_call ?? 0);
|
|
2185
|
-
const annotations = api?.listing_metadata?.mcp_tool?.annotations || {};
|
|
2186
|
-
|
|
2187
|
-
if (Number.isFinite(price) && price > 0) {
|
|
2188
|
-
return true;
|
|
2189
|
-
}
|
|
2190
|
-
|
|
2191
|
-
if (annotations.destructiveHint === true) {
|
|
2192
|
-
return true;
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
if (annotations.readOnlyHint === false) {
|
|
2196
|
-
return true;
|
|
2197
|
-
}
|
|
2198
|
-
|
|
2199
|
-
return false;
|
|
2200
|
-
}
|
|
2201
|
-
|
|
2202
|
-
async function isToolProtected(name, authInfo = null) {
|
|
2203
|
-
if (PUBLIC_STATIC_TOOL_NAMES.has(name)) {
|
|
2204
|
-
return false;
|
|
2205
|
-
}
|
|
2206
|
-
|
|
2207
|
-
if (REMOTE_PROTECTED_STATIC_TOOL_NAMES.has(name)) {
|
|
2208
|
-
return true;
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
|
-
const dynamicTool = await resolveDynamicTool(name, authInfo);
|
|
2212
|
-
if (!dynamicTool) {
|
|
2213
|
-
return false;
|
|
2214
|
-
}
|
|
2215
|
-
|
|
2216
|
-
return isDynamicToolProtectedApi(dynamicTool.api);
|
|
2217
|
-
}
|
|
2218
|
-
|
|
2219
|
-
async function resolvePublishingWallet(walletId = null) {
|
|
2220
|
-
if (trimString(walletId)) {
|
|
2221
|
-
if (!localWalletStore) {
|
|
2222
|
-
throw new Error("Local wallet selection is not enabled in this server mode.");
|
|
2223
|
-
}
|
|
2224
|
-
|
|
2225
|
-
const wallet = await localWalletStore.resolveSigningWallet(trimString(walletId));
|
|
2226
|
-
if (!wallet) {
|
|
2227
|
-
throw new Error("No local wallet found for the provided wallet_id.");
|
|
2228
|
-
}
|
|
2229
|
-
return wallet;
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
|
-
const envWallet = resolveEnvPrivateWallet(env);
|
|
2233
|
-
if (envWallet) return envWallet;
|
|
2234
|
-
|
|
2235
|
-
if (localWalletStore) {
|
|
2236
|
-
const activeWallet = await localWalletStore.resolveSigningWallet();
|
|
2237
|
-
if (activeWallet) return activeWallet;
|
|
2238
|
-
}
|
|
2239
|
-
|
|
2240
|
-
throw new Error(
|
|
2241
|
-
"No signing wallet available. Create/select a local wallet or set APIOSK_PRIVATE_KEY."
|
|
2242
|
-
);
|
|
2243
|
-
}
|
|
2244
|
-
|
|
2245
|
-
async function handleExplore(argumentsObject = {}, authInfo = null) {
|
|
2246
|
-
const client = await getClient(authInfo);
|
|
2247
|
-
const capability = await resolvePaymentCapability(authInfo);
|
|
2248
|
-
const paymentHint = buildDiscoveryPaymentHint({
|
|
2249
|
-
capability,
|
|
2250
|
-
mode: capability.mode,
|
|
2251
|
-
localWalletsEnabled: capability.local_wallets_enabled,
|
|
2252
|
-
});
|
|
2253
|
-
|
|
2254
|
-
if (!argumentsObject.listing_type) {
|
|
2255
|
-
const response = await client.requestJson("/types", { method: "GET" });
|
|
2256
|
-
return content({
|
|
2257
|
-
...response,
|
|
2258
|
-
payment: paymentHint,
|
|
2259
|
-
next_steps:
|
|
2260
|
-
hostedAuthEnabled ?
|
|
2261
|
-
"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." :
|
|
2262
|
-
"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.",
|
|
2263
|
-
});
|
|
2264
|
-
}
|
|
2265
|
-
|
|
2266
|
-
const listingType = String(argumentsObject.listing_type).trim().toLowerCase();
|
|
2267
|
-
const response = await client.requestJson(`/types/${encodeURIComponent(listingType)}/v1`, {
|
|
2268
|
-
method: "GET",
|
|
2269
|
-
query: {
|
|
2270
|
-
search: argumentsObject.search,
|
|
2271
|
-
sort: argumentsObject.sort,
|
|
2272
|
-
order: argumentsObject.order,
|
|
2273
|
-
limit: argumentsObject.limit || DEFAULT_LIMIT,
|
|
2274
|
-
offset: argumentsObject.offset || 0,
|
|
2275
|
-
},
|
|
2276
|
-
});
|
|
2277
|
-
|
|
2278
|
-
return content({
|
|
2279
|
-
...response,
|
|
2280
|
-
payment: paymentHint,
|
|
2281
|
-
next_steps:
|
|
2282
|
-
hostedAuthEnabled ?
|
|
2283
|
-
"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." :
|
|
2284
|
-
"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.",
|
|
2285
|
-
});
|
|
2286
|
-
}
|
|
2287
|
-
|
|
2288
|
-
async function handleSearch(argumentsObject = {}, authInfo = null) {
|
|
2289
|
-
const client = await getClient(authInfo);
|
|
2290
|
-
const response = await client.listApis({
|
|
2291
|
-
search: argumentsObject.search,
|
|
2292
|
-
category: argumentsObject.category,
|
|
2293
|
-
sort: argumentsObject.sort,
|
|
2294
|
-
order: argumentsObject.order,
|
|
2295
|
-
limit: argumentsObject.limit || DEFAULT_LIMIT,
|
|
2296
|
-
offset: argumentsObject.offset || 0,
|
|
2297
|
-
});
|
|
2298
|
-
|
|
2299
|
-
const catalog = response.apis || [];
|
|
2300
|
-
const sourceMatches = searchKnownSources(argumentsObject.search, {
|
|
2301
|
-
limit: argumentsObject.limit || DEFAULT_LIMIT,
|
|
2302
|
-
});
|
|
2303
|
-
await getTools(false, authInfo);
|
|
2304
|
-
const capability = await resolvePaymentCapability(authInfo);
|
|
2305
|
-
|
|
2306
|
-
return content({
|
|
2307
|
-
apis: catalog.map((api) => buildCatalogEntry(api, cache.toolNamesBySlug.get(api.slug) || null)),
|
|
2308
|
-
sources: sourceMatches,
|
|
2309
|
-
meta: response.meta,
|
|
2310
|
-
source_meta: {
|
|
2311
|
-
total: sourceMatches.length,
|
|
2312
|
-
note: "These are direct x402 discovery sources, not Apiosk catalog API listings. Their public and paid endpoints are included inline.",
|
|
2313
|
-
},
|
|
2314
|
-
payment: buildDiscoveryPaymentHint({
|
|
2315
|
-
capability,
|
|
2316
|
-
mode: capability.mode,
|
|
2317
|
-
localWalletsEnabled: capability.local_wallets_enabled,
|
|
2318
|
-
}),
|
|
2319
|
-
for_providers:
|
|
2320
|
-
"Listing your own API? Call apiosk_payment_guide with role='provider' (or apiosk_publish_api) to publish it for other agents.",
|
|
2321
|
-
next_steps:
|
|
2322
|
-
sourceMatches.length > 0
|
|
2323
|
-
? "For a matched source, pass its discover_source to apiosk_discover. For endpoints marked payment_required=true, call apiosk_inspect_x402 first and only then apiosk_fetch_paid after the user confirms the live price. Catalog APIs still use apiosk_get_api/apiosk_execute."
|
|
2324
|
-
: "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.",
|
|
2325
|
-
});
|
|
2326
|
-
}
|
|
2327
|
-
|
|
2328
|
-
async function handleGetApi(argumentsObject = {}, authInfo = null) {
|
|
2329
|
-
if (!argumentsObject.slug) {
|
|
2330
|
-
return errorContent("Missing required field: slug");
|
|
2331
|
-
}
|
|
2332
|
-
|
|
2333
|
-
const client = await getClient(authInfo);
|
|
2334
|
-
const [detail, metadata, capability] = await Promise.all([
|
|
2335
|
-
client.getApi(argumentsObject.slug),
|
|
2336
|
-
client.getMetadata(argumentsObject.slug).catch(() => null),
|
|
2337
|
-
resolvePaymentCapability(authInfo),
|
|
2338
|
-
]);
|
|
2339
|
-
|
|
2340
|
-
return content({
|
|
2341
|
-
detail,
|
|
2342
|
-
metadata,
|
|
2343
|
-
payment: buildPaymentGuidance({
|
|
2344
|
-
api: {
|
|
2345
|
-
slug: argumentsObject.slug,
|
|
2346
|
-
price_usd: detail?.price_usd ?? metadata?.cost_per_call ?? null,
|
|
2347
|
-
listing_metadata: metadata || detail?.listing_metadata || null,
|
|
2348
|
-
},
|
|
2349
|
-
capability,
|
|
2350
|
-
mode: capability.mode,
|
|
2351
|
-
localWalletsEnabled: capability.local_wallets_enabled,
|
|
2352
|
-
}),
|
|
2353
|
-
});
|
|
2354
|
-
}
|
|
2355
|
-
|
|
2356
|
-
async function handleHealth(argumentsObject = {}, authInfo = null) {
|
|
2357
|
-
const savedConfig = await getSavedConfig();
|
|
2358
|
-
let gateway = null;
|
|
2359
|
-
|
|
2360
|
-
try {
|
|
2361
|
-
const client = await getClient(authInfo);
|
|
2362
|
-
gateway =
|
|
2363
|
-
typeof client.requestJson === "function" ?
|
|
2364
|
-
await client.requestJson("/health", { method: "GET" }) :
|
|
2365
|
-
{ status: "unknown" };
|
|
2366
|
-
} catch (error) {
|
|
2367
|
-
gateway = {
|
|
2368
|
-
status: "degraded",
|
|
2369
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2370
|
-
};
|
|
2371
|
-
}
|
|
2372
|
-
|
|
2373
|
-
if (typeof gateway === "string") {
|
|
2374
|
-
gateway = {
|
|
2375
|
-
status: gateway.toLowerCase() === "ok" ? "ok" : gateway,
|
|
2376
|
-
raw: gateway,
|
|
2377
|
-
};
|
|
2378
|
-
} else if (gateway && typeof gateway === "object" && !gateway.status && gateway.ok === true) {
|
|
2379
|
-
gateway = {
|
|
2380
|
-
...gateway,
|
|
2381
|
-
status: "ok",
|
|
2382
|
-
};
|
|
2383
|
-
}
|
|
2384
|
-
|
|
2385
|
-
return content({
|
|
2386
|
-
status: gateway?.status || "ok",
|
|
2387
|
-
mcp: {
|
|
2388
|
-
mode: hostedAuthEnabled ? "hosted" : localWalletStore ? "local" : "remote",
|
|
2389
|
-
tools: hostedAuthEnabled ? HOSTED_REMOTE_TOOLS.map((tool) => tool.name) : undefined,
|
|
2390
|
-
},
|
|
2391
|
-
gateway,
|
|
2392
|
-
gateway_base_url: resolveGatewayBaseUrl(env, savedConfig),
|
|
2393
|
-
});
|
|
2394
|
-
}
|
|
2395
|
-
|
|
2396
|
-
// Agentic discovery: aggregate + rank candidate x402 endpoints across sources
|
|
2397
|
-
// (Phase 1: the Apiosk catalog, which already includes federated externals).
|
|
2398
|
-
// Reuses the request-scoped client so catalog reads honour the same gateway
|
|
2399
|
-
// base URL and connect-token threading as every other tool.
|
|
2400
|
-
async function handleDiscover(argumentsObject = {}, authInfo = null) {
|
|
2401
|
-
const client = await getClient(authInfo);
|
|
2402
|
-
const savedConfig = await getSavedConfig().catch(() => null);
|
|
2403
|
-
return runDiscover(argumentsObject, {
|
|
2404
|
-
listApis: (params) => client.listApis(params),
|
|
2405
|
-
gatewayBaseUrl: resolveGatewayBaseUrl(env, savedConfig),
|
|
2406
|
-
});
|
|
2407
|
-
}
|
|
2408
|
-
|
|
2409
|
-
// Read an arbitrary URL's x402 402 terms without paying. Read-only probe; no
|
|
2410
|
-
// wallet/connect token is used, so it needs no auth.
|
|
2411
|
-
async function handleInspect(argumentsObject = {}, authInfo = null) {
|
|
2412
|
-
const savedConfig = await getSavedConfig().catch(() => null);
|
|
2413
|
-
let gatewayHost = "";
|
|
2414
|
-
try {
|
|
2415
|
-
gatewayHost = new URL(resolveGatewayBaseUrl(env, savedConfig)).hostname;
|
|
2416
|
-
} catch {
|
|
2417
|
-
gatewayHost = "";
|
|
2418
|
-
}
|
|
2419
|
-
return runInspect(argumentsObject, { gatewayHost });
|
|
2420
|
-
}
|
|
2421
|
-
|
|
2422
|
-
// Pay an external (non-Apiosk-hosted) x402 endpoint through the gateway payer
|
|
2423
|
-
// proxy, using the same connect token the runtime threads to the gateway for
|
|
2424
|
-
// catalog settlement. The gateway enforces spend caps and does the signing.
|
|
2425
|
-
async function handleFetchPaid(argumentsObject = {}, authInfo = null) {
|
|
2426
|
-
const savedConfig = await getSavedConfig().catch(() => null);
|
|
2427
|
-
const requestConnectToken = trimString(authInfo?.extra?.apiosk_connect_token);
|
|
2428
|
-
const envConnectToken = trimString(env.APIOSK_CONNECT_TOKEN || savedConfig?.connect_token);
|
|
2429
|
-
return runFetchPaid(argumentsObject, {
|
|
2430
|
-
connectToken: requestConnectToken || envConnectToken,
|
|
2431
|
-
gatewayBaseUrl: resolveGatewayBaseUrl(env, savedConfig),
|
|
2432
|
-
});
|
|
2433
|
-
}
|
|
2434
|
-
|
|
2435
|
-
async function handleExecute(argumentsObject = {}, authInfo = null) {
|
|
2436
|
-
if (!argumentsObject.slug) {
|
|
2437
|
-
return errorContent("Missing required field: slug");
|
|
2438
|
-
}
|
|
2439
|
-
|
|
2440
|
-
const client = await getClient(authInfo);
|
|
2441
|
-
const resolvedSlug = await resolveExecutableApiSlug(argumentsObject.slug, authInfo);
|
|
2442
|
-
const result = await client.execute(resolvedSlug, argumentsObject.input, {
|
|
2443
|
-
operation: argumentsObject.operation,
|
|
2444
|
-
query: argumentsObject.query,
|
|
2445
|
-
pathParams: argumentsObject.path_params,
|
|
2446
|
-
headers: {
|
|
2447
|
-
accept: "application/json",
|
|
2448
|
-
},
|
|
2449
|
-
});
|
|
2450
|
-
|
|
2451
|
-
return content(
|
|
2452
|
-
resolvedSlug === argumentsObject.slug
|
|
2453
|
-
? result
|
|
2454
|
-
: {
|
|
2455
|
-
requested_slug: argumentsObject.slug,
|
|
2456
|
-
resolved_slug: resolvedSlug,
|
|
2457
|
-
result,
|
|
2458
|
-
}
|
|
2459
|
-
);
|
|
2460
|
-
}
|
|
2461
|
-
|
|
2462
|
-
async function handleDynamicExecute(tool, argumentsObject = {}, authInfo = null) {
|
|
2463
|
-
const client = await getClient(authInfo);
|
|
2464
|
-
const result = hasExecuteEnvelope(argumentsObject)
|
|
2465
|
-
? await client.execute(tool.api.slug, argumentsObject.input, {
|
|
2466
|
-
operation: argumentsObject.operation,
|
|
2467
|
-
query: argumentsObject.query,
|
|
2468
|
-
pathParams: argumentsObject.path_params,
|
|
2469
|
-
headers: {
|
|
2470
|
-
accept: "application/json",
|
|
2471
|
-
},
|
|
2472
|
-
})
|
|
2473
|
-
: await client.execute(tool.api.slug, argumentsObject, {
|
|
2474
|
-
headers: {
|
|
2475
|
-
accept: "application/json",
|
|
2476
|
-
},
|
|
2477
|
-
});
|
|
2478
|
-
const summary = summarizeDynamicToolResult(tool, result);
|
|
2479
|
-
if (summary) {
|
|
2480
|
-
return {
|
|
2481
|
-
content: [
|
|
2482
|
-
{
|
|
2483
|
-
type: "text",
|
|
2484
|
-
text: summary,
|
|
2485
|
-
},
|
|
2486
|
-
],
|
|
2487
|
-
structuredContent: result,
|
|
2488
|
-
};
|
|
2489
|
-
}
|
|
2490
|
-
return content(result);
|
|
2491
|
-
}
|
|
2492
|
-
|
|
2493
|
-
async function handleHelp(argumentsObject = {}) {
|
|
2494
|
-
return content(
|
|
2495
|
-
buildHelpPayload(argumentsObject.topic, {
|
|
2496
|
-
localWalletsEnabled: Boolean(localWalletStore),
|
|
2497
|
-
hostedAuthEnabled,
|
|
2498
|
-
})
|
|
2499
|
-
);
|
|
2500
|
-
}
|
|
2501
|
-
|
|
2502
|
-
async function handlePaymentGuide(argumentsObject = {}, authInfo = null) {
|
|
2503
|
-
const capability = await resolvePaymentCapability(authInfo);
|
|
2504
|
-
const slug = trimString(argumentsObject.slug);
|
|
2505
|
-
|
|
2506
|
-
let api = null;
|
|
2507
|
-
if (slug) {
|
|
2508
|
-
try {
|
|
2509
|
-
const client = await getClient(authInfo);
|
|
2510
|
-
const [detail, metadata] = await Promise.all([
|
|
2511
|
-
client.getApi(slug).catch(() => null),
|
|
2512
|
-
client.getMetadata(slug).catch(() => null),
|
|
2513
|
-
]);
|
|
2514
|
-
api = {
|
|
2515
|
-
slug,
|
|
2516
|
-
price_usd: detail?.price_usd ?? metadata?.cost_per_call ?? null,
|
|
2517
|
-
listing_metadata: metadata || detail?.listing_metadata || null,
|
|
2518
|
-
};
|
|
2519
|
-
} catch {
|
|
2520
|
-
api = { slug };
|
|
2521
|
-
}
|
|
2522
|
-
}
|
|
2523
|
-
|
|
2524
|
-
return content(
|
|
2525
|
-
buildPaymentGuide({
|
|
2526
|
-
role: argumentsObject.role,
|
|
2527
|
-
api,
|
|
2528
|
-
capability,
|
|
2529
|
-
mode: capability.mode,
|
|
2530
|
-
localWalletsEnabled: capability.local_wallets_enabled,
|
|
2531
|
-
})
|
|
2532
|
-
);
|
|
2533
|
-
}
|
|
2534
|
-
|
|
2535
|
-
function ensureLocalDashboardSessionTools(toolName) {
|
|
2536
|
-
if (!localWalletStore) {
|
|
2537
|
-
return errorContent(
|
|
2538
|
-
`${toolName} is only available in the local stdio package because it stores dashboard session state on the local machine.`
|
|
2539
|
-
);
|
|
2540
|
-
}
|
|
2541
|
-
|
|
2542
|
-
return null;
|
|
2543
|
-
}
|
|
2544
|
-
|
|
2545
|
-
function ensureDashboardSessionTool(toolName, authInfo = null) {
|
|
2546
|
-
if (localWalletStore || hasConfiguredDashboardAccess(authInfo) || hostedAuthEnabled) {
|
|
2547
|
-
return null;
|
|
2548
|
-
}
|
|
2549
|
-
|
|
2550
|
-
return errorContent(
|
|
2551
|
-
"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."
|
|
2552
|
-
);
|
|
2553
|
-
}
|
|
2554
|
-
|
|
2555
|
-
async function handleCreateAccount(argumentsObject = {}) {
|
|
2556
|
-
const localOnlyError = ensureLocalDashboardSessionTools("apiosk_create_account");
|
|
2557
|
-
if (localOnlyError) return localOnlyError;
|
|
2558
|
-
|
|
2559
|
-
const email = trimString(argumentsObject.email);
|
|
2560
|
-
const password = trimString(argumentsObject.password);
|
|
2561
|
-
|
|
2562
|
-
if (!email) return errorContent("Missing required field: email");
|
|
2563
|
-
if (!password) return errorContent("Missing required field: password");
|
|
2564
|
-
|
|
2565
|
-
const payload = await requestDashboard(
|
|
2566
|
-
"/api/auth/mcp-sign-up",
|
|
2567
|
-
{
|
|
2568
|
-
method: "POST",
|
|
2569
|
-
body: { email, password },
|
|
2570
|
-
},
|
|
2571
|
-
{ requireAuth: false }
|
|
2572
|
-
);
|
|
2573
|
-
const record = asObject(payload) || {};
|
|
2574
|
-
|
|
2575
|
-
let savedSession = false;
|
|
2576
|
-
if (typeof record.session_token === "string" && record.session_token.trim() && argumentsObject.save_session !== false) {
|
|
2577
|
-
await saveLocalApioskDashboardSession(
|
|
2578
|
-
{
|
|
2579
|
-
session_token: record.session_token,
|
|
2580
|
-
expires_at: record.expires_at,
|
|
2581
|
-
email,
|
|
2582
|
-
control_plane_url: resolveControlPlaneUrl(env),
|
|
2583
|
-
dashboard_url: trimString(env.APIOSK_DASHBOARD_URL || "https://dashboard.apiosk.com"),
|
|
2584
|
-
},
|
|
2585
|
-
env
|
|
2586
|
-
);
|
|
2587
|
-
savedSession = true;
|
|
2588
|
-
}
|
|
2589
|
-
|
|
2590
|
-
return content({
|
|
2591
|
-
...record,
|
|
2592
|
-
saved_session: savedSession,
|
|
2593
|
-
local_config_paths: createApioskLocalConfigPaths(env),
|
|
2594
|
-
next_steps:
|
|
2595
|
-
record.email_confirmation_required ?
|
|
2596
|
-
[
|
|
2597
|
-
"Tell the user to confirm their email from the Supabase/Apiosk email they just received.",
|
|
2598
|
-
"After confirmation, call apiosk_sign_in.",
|
|
2599
|
-
] :
|
|
2600
|
-
[
|
|
2601
|
-
"Explore the Apiosk catalog with apiosk_search or apiosk_explore.",
|
|
2602
|
-
],
|
|
2603
|
-
});
|
|
2604
|
-
}
|
|
2605
|
-
|
|
2606
|
-
async function handleSignIn(argumentsObject = {}) {
|
|
2607
|
-
const localOnlyError = ensureLocalDashboardSessionTools("apiosk_sign_in");
|
|
2608
|
-
if (localOnlyError) return localOnlyError;
|
|
2609
|
-
|
|
2610
|
-
const email = trimString(argumentsObject.email);
|
|
2611
|
-
const password = trimString(argumentsObject.password);
|
|
2612
|
-
|
|
2613
|
-
if (!email) return errorContent("Missing required field: email");
|
|
2614
|
-
if (!password) return errorContent("Missing required field: password");
|
|
2615
|
-
|
|
2616
|
-
const payload = await requestDashboard(
|
|
2617
|
-
"/api/auth/mcp-sign-in",
|
|
2618
|
-
{
|
|
2619
|
-
method: "POST",
|
|
2620
|
-
body: { email, password },
|
|
2621
|
-
},
|
|
2622
|
-
{ requireAuth: false }
|
|
2623
|
-
);
|
|
2624
|
-
const record = asObject(payload) || {};
|
|
2625
|
-
|
|
2626
|
-
let savedSession = false;
|
|
2627
|
-
if (typeof record.session_token === "string" && record.session_token.trim() && argumentsObject.save_session !== false) {
|
|
2628
|
-
await saveLocalApioskDashboardSession(
|
|
2629
|
-
{
|
|
2630
|
-
session_token: record.session_token,
|
|
2631
|
-
expires_at: record.expires_at,
|
|
2632
|
-
email,
|
|
2633
|
-
control_plane_url: resolveControlPlaneUrl(env),
|
|
2634
|
-
dashboard_url: trimString(env.APIOSK_DASHBOARD_URL || "https://dashboard.apiosk.com"),
|
|
2635
|
-
},
|
|
2636
|
-
env
|
|
2637
|
-
);
|
|
2638
|
-
savedSession = true;
|
|
2639
|
-
}
|
|
2640
|
-
|
|
2641
|
-
return content({
|
|
2642
|
-
...record,
|
|
2643
|
-
saved_session: savedSession,
|
|
2644
|
-
local_config_paths: createApioskLocalConfigPaths(env),
|
|
2645
|
-
next_steps: [
|
|
2646
|
-
"Explore the Apiosk catalog with apiosk_search or apiosk_explore.",
|
|
2647
|
-
],
|
|
2648
|
-
});
|
|
2649
|
-
}
|
|
2650
|
-
|
|
2651
|
-
// Hosted (OAuth / wallet sign-in) sessions manage wallets straight against
|
|
2652
|
-
// Supabase REST with the caller's own JWT — the legacy dashboard backend
|
|
2653
|
-
// these tools used to proxy to no longer exists (dashboard.apiosk.com is now
|
|
2654
|
-
// the provider-portal SPA, whose catch-all answers /api/* with index.html).
|
|
2655
|
-
// The requestDashboard proxy path is kept only for stdio setups that point
|
|
2656
|
-
// APIOSK_CONTROL_PLANE_URL at their own control plane.
|
|
2657
|
-
function hostedWalletContext(authInfo = null) {
|
|
2658
|
-
const sessionToken = resolveRequestDashboardUserToken(authInfo);
|
|
2659
|
-
if (!sessionToken) return null;
|
|
2660
|
-
return {
|
|
2661
|
-
env,
|
|
2662
|
-
sessionToken,
|
|
2663
|
-
userId: trimString(authInfo?.extra?.userId),
|
|
2664
|
-
};
|
|
2665
|
-
}
|
|
2666
|
-
|
|
2667
|
-
async function handleWalletList(authInfo = null) {
|
|
2668
|
-
const hosted = hostedWalletContext(authInfo);
|
|
2669
|
-
if (hosted) {
|
|
2670
|
-
return content(await hostedListWallets(hosted));
|
|
2671
|
-
}
|
|
2672
|
-
return content(await requestDashboard("/api/agent-wallets", {}, {}, authInfo));
|
|
2673
|
-
}
|
|
2674
|
-
|
|
2675
|
-
async function handleWalletCreate(argumentsObject = {}, authInfo = null) {
|
|
2676
|
-
const hosted = hostedWalletContext(authInfo);
|
|
2677
|
-
if (hosted) {
|
|
2678
|
-
// Key derivation + encryption lived in the retired dashboard backend, so
|
|
2679
|
-
// hosted sessions get an honest explanation instead of proxied HTML.
|
|
2680
|
-
return errorContent(hostedCreateWalletUnavailable());
|
|
2681
|
-
}
|
|
2682
|
-
return content(
|
|
2683
|
-
await requestDashboard(
|
|
2684
|
-
"/api/agent-wallets",
|
|
2685
|
-
{
|
|
2686
|
-
method: "POST",
|
|
2687
|
-
body: argumentsObject,
|
|
2688
|
-
},
|
|
2689
|
-
{},
|
|
2690
|
-
authInfo
|
|
2691
|
-
)
|
|
2692
|
-
);
|
|
2693
|
-
}
|
|
2694
|
-
|
|
2695
|
-
async function handleWalletUpdate(argumentsObject = {}, authInfo = null) {
|
|
2696
|
-
if (!argumentsObject.wallet_id) {
|
|
2697
|
-
return errorContent("Missing required field: wallet_id");
|
|
2698
|
-
}
|
|
2699
|
-
|
|
2700
|
-
const hosted = hostedWalletContext(authInfo);
|
|
2701
|
-
if (hosted) {
|
|
2702
|
-
return content(
|
|
2703
|
-
await hostedUpdateWallet({
|
|
2704
|
-
...hosted,
|
|
2705
|
-
walletId: argumentsObject.wallet_id,
|
|
2706
|
-
label: argumentsObject.label,
|
|
2707
|
-
status: argumentsObject.status,
|
|
2708
|
-
dailyLimitUsdc: argumentsObject.daily_limit_usdc,
|
|
2709
|
-
perTxLimitUsdc: argumentsObject.per_tx_limit_usdc,
|
|
2710
|
-
color: argumentsObject.color,
|
|
2711
|
-
icon: argumentsObject.icon,
|
|
2712
|
-
})
|
|
2713
|
-
);
|
|
2714
|
-
}
|
|
2715
|
-
|
|
2716
|
-
const { wallet_id, ...updates } = argumentsObject;
|
|
2717
|
-
return content(
|
|
2718
|
-
await requestDashboard(
|
|
2719
|
-
`/api/agent-wallets/${wallet_id}`,
|
|
2720
|
-
{
|
|
2721
|
-
method: "PATCH",
|
|
2722
|
-
body: updates,
|
|
2723
|
-
},
|
|
2724
|
-
{},
|
|
2725
|
-
authInfo
|
|
2726
|
-
)
|
|
2727
|
-
);
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
|
-
async function handleWalletDelete(argumentsObject = {}, authInfo = null) {
|
|
2731
|
-
if (!argumentsObject.wallet_id) {
|
|
2732
|
-
return errorContent("Missing required field: wallet_id");
|
|
2733
|
-
}
|
|
2734
|
-
|
|
2735
|
-
const hosted = hostedWalletContext(authInfo);
|
|
2736
|
-
if (hosted) {
|
|
2737
|
-
return content(
|
|
2738
|
-
await hostedDeleteWallet({ ...hosted, walletId: argumentsObject.wallet_id })
|
|
2739
|
-
);
|
|
2740
|
-
}
|
|
2741
|
-
|
|
2742
|
-
return content(
|
|
2743
|
-
await requestDashboard(
|
|
2744
|
-
`/api/agent-wallets/${argumentsObject.wallet_id}`,
|
|
2745
|
-
{
|
|
2746
|
-
method: "DELETE",
|
|
2747
|
-
},
|
|
2748
|
-
{},
|
|
2749
|
-
authInfo
|
|
2750
|
-
)
|
|
2751
|
-
);
|
|
2752
|
-
}
|
|
2753
|
-
|
|
2754
|
-
async function handleWalletActivity(argumentsObject = {}, authInfo = null) {
|
|
2755
|
-
if (!argumentsObject.wallet_id) {
|
|
2756
|
-
return errorContent("Missing required field: wallet_id");
|
|
2757
|
-
}
|
|
2758
|
-
|
|
2759
|
-
const hosted = hostedWalletContext(authInfo);
|
|
2760
|
-
if (hosted) {
|
|
2761
|
-
return content(
|
|
2762
|
-
await hostedWalletActivity({
|
|
2763
|
-
...hosted,
|
|
2764
|
-
walletId: argumentsObject.wallet_id,
|
|
2765
|
-
limit: argumentsObject.limit,
|
|
2766
|
-
})
|
|
2767
|
-
);
|
|
2768
|
-
}
|
|
2769
|
-
|
|
2770
|
-
const params = new URLSearchParams();
|
|
2771
|
-
if (argumentsObject.page !== undefined) params.set("page", String(argumentsObject.page));
|
|
2772
|
-
if (argumentsObject.limit !== undefined) params.set("limit", String(argumentsObject.limit));
|
|
2773
|
-
const query = params.toString();
|
|
2774
|
-
|
|
2775
|
-
return content(
|
|
2776
|
-
await requestDashboard(
|
|
2777
|
-
`/api/agent-wallets/${argumentsObject.wallet_id}/transactions${query ? `?${query}` : ""}`,
|
|
2778
|
-
{},
|
|
2779
|
-
{},
|
|
2780
|
-
authInfo
|
|
2781
|
-
)
|
|
2782
|
-
);
|
|
2783
|
-
}
|
|
2784
|
-
|
|
2785
|
-
async function handleWalletConnectString(argumentsObject = {}, authInfo = null) {
|
|
2786
|
-
if (!argumentsObject.wallet_id) {
|
|
2787
|
-
return errorContent("Missing required field: wallet_id");
|
|
2788
|
-
}
|
|
2789
|
-
|
|
2790
|
-
const hosted = hostedWalletContext(authInfo);
|
|
2791
|
-
if (hosted) {
|
|
2792
|
-
const created = await hostedCreateWalletToken({
|
|
2793
|
-
...hosted,
|
|
2794
|
-
walletId: argumentsObject.wallet_id,
|
|
2795
|
-
name: argumentsObject.token_name,
|
|
2796
|
-
revokeExisting: argumentsObject.revoke_existing === true,
|
|
2797
|
-
});
|
|
2798
|
-
return content({
|
|
2799
|
-
...created,
|
|
2800
|
-
connect_string: `apiosk:connect:${created.connect_token}`,
|
|
2801
|
-
});
|
|
2802
|
-
}
|
|
2803
|
-
|
|
2804
|
-
const { wallet_id, ...body } = argumentsObject;
|
|
2805
|
-
return content(
|
|
2806
|
-
await requestDashboard(
|
|
2807
|
-
`/api/agent-wallets/${wallet_id}/connect-string`,
|
|
2808
|
-
{
|
|
2809
|
-
method: "POST",
|
|
2810
|
-
body,
|
|
2811
|
-
},
|
|
2812
|
-
{},
|
|
2813
|
-
authInfo
|
|
2814
|
-
)
|
|
2815
|
-
);
|
|
2816
|
-
}
|
|
2817
|
-
|
|
2818
|
-
async function handleWalletApiKeys(argumentsObject = {}, authInfo = null) {
|
|
2819
|
-
if (!argumentsObject.wallet_id) {
|
|
2820
|
-
return errorContent("Missing required field: wallet_id");
|
|
2821
|
-
}
|
|
2822
|
-
|
|
2823
|
-
const hosted = hostedWalletContext(authInfo);
|
|
2824
|
-
if (hosted) {
|
|
2825
|
-
return content(
|
|
2826
|
-
await hostedListWalletTokens({ ...hosted, walletId: argumentsObject.wallet_id })
|
|
2827
|
-
);
|
|
2828
|
-
}
|
|
2829
|
-
|
|
2830
|
-
return content(
|
|
2831
|
-
await requestDashboard(
|
|
2832
|
-
`/api/agent-wallets/${argumentsObject.wallet_id}/api-keys`,
|
|
2833
|
-
{},
|
|
2834
|
-
{},
|
|
2835
|
-
authInfo
|
|
2836
|
-
)
|
|
2837
|
-
);
|
|
2838
|
-
}
|
|
2839
|
-
|
|
2840
|
-
async function handleWalletApiKeyCreate(argumentsObject = {}, authInfo = null) {
|
|
2841
|
-
if (!argumentsObject.wallet_id) {
|
|
2842
|
-
return errorContent("Missing required field: wallet_id");
|
|
2843
|
-
}
|
|
2844
|
-
|
|
2845
|
-
const hosted = hostedWalletContext(authInfo);
|
|
2846
|
-
if (hosted) {
|
|
2847
|
-
return content(
|
|
2848
|
-
await hostedCreateWalletToken({
|
|
2849
|
-
...hosted,
|
|
2850
|
-
walletId: argumentsObject.wallet_id,
|
|
2851
|
-
name: argumentsObject.name,
|
|
2852
|
-
expirationDays: argumentsObject.expiration_days,
|
|
2853
|
-
revokeExisting: argumentsObject.revoke_existing === true,
|
|
2854
|
-
})
|
|
2855
|
-
);
|
|
2856
|
-
}
|
|
2857
|
-
|
|
2858
|
-
const { wallet_id, ...body } = argumentsObject;
|
|
2859
|
-
return content(
|
|
2860
|
-
await requestDashboard(
|
|
2861
|
-
`/api/agent-wallets/${wallet_id}/api-keys`,
|
|
2862
|
-
{
|
|
2863
|
-
method: "POST",
|
|
2864
|
-
body,
|
|
2865
|
-
},
|
|
2866
|
-
{},
|
|
2867
|
-
authInfo
|
|
2868
|
-
)
|
|
2869
|
-
);
|
|
2870
|
-
}
|
|
2871
|
-
|
|
2872
|
-
async function handleWalletApiKeyUpdate(argumentsObject = {}, authInfo = null) {
|
|
2873
|
-
if (!argumentsObject.wallet_id) {
|
|
2874
|
-
return errorContent("Missing required field: wallet_id");
|
|
2875
|
-
}
|
|
2876
|
-
if (!argumentsObject.key_id) {
|
|
2877
|
-
return errorContent("Missing required field: key_id");
|
|
2878
|
-
}
|
|
2879
|
-
|
|
2880
|
-
const hosted = hostedWalletContext(authInfo);
|
|
2881
|
-
if (hosted) {
|
|
2882
|
-
return content(
|
|
2883
|
-
await hostedUpdateWalletToken({
|
|
2884
|
-
...hosted,
|
|
2885
|
-
walletId: argumentsObject.wallet_id,
|
|
2886
|
-
keyId: argumentsObject.key_id,
|
|
2887
|
-
name: argumentsObject.name,
|
|
2888
|
-
expirationDays: argumentsObject.expiration_days,
|
|
2889
|
-
revoke: argumentsObject.revoke,
|
|
2890
|
-
})
|
|
2891
|
-
);
|
|
2892
|
-
}
|
|
2893
|
-
|
|
2894
|
-
const { wallet_id, key_id, ...body } = argumentsObject;
|
|
2895
|
-
return content(
|
|
2896
|
-
await requestDashboard(
|
|
2897
|
-
`/api/agent-wallets/${wallet_id}/api-keys/${key_id}`,
|
|
2898
|
-
{
|
|
2899
|
-
method: "PATCH",
|
|
2900
|
-
body,
|
|
2901
|
-
},
|
|
2902
|
-
{},
|
|
2903
|
-
authInfo
|
|
2904
|
-
)
|
|
2905
|
-
);
|
|
2906
|
-
}
|
|
2907
|
-
|
|
2908
|
-
async function handleWalletApiKeyDelete(argumentsObject = {}, authInfo = null) {
|
|
2909
|
-
if (!argumentsObject.wallet_id) {
|
|
2910
|
-
return errorContent("Missing required field: wallet_id");
|
|
2911
|
-
}
|
|
2912
|
-
if (!argumentsObject.key_id) {
|
|
2913
|
-
return errorContent("Missing required field: key_id");
|
|
2914
|
-
}
|
|
2915
|
-
|
|
2916
|
-
const hosted = hostedWalletContext(authInfo);
|
|
2917
|
-
if (hosted) {
|
|
2918
|
-
return content(
|
|
2919
|
-
await hostedDeleteWalletToken({
|
|
2920
|
-
...hosted,
|
|
2921
|
-
walletId: argumentsObject.wallet_id,
|
|
2922
|
-
keyId: argumentsObject.key_id,
|
|
2923
|
-
})
|
|
2924
|
-
);
|
|
2925
|
-
}
|
|
2926
|
-
|
|
2927
|
-
return content(
|
|
2928
|
-
await requestDashboard(
|
|
2929
|
-
`/api/agent-wallets/${argumentsObject.wallet_id}/api-keys/${argumentsObject.key_id}`,
|
|
2930
|
-
{ method: "DELETE" },
|
|
2931
|
-
{},
|
|
2932
|
-
authInfo
|
|
2933
|
-
)
|
|
2934
|
-
);
|
|
2935
|
-
}
|
|
2936
|
-
|
|
2937
|
-
async function handleLocalWalletList() {
|
|
2938
|
-
if (!localWalletStore) {
|
|
2939
|
-
return errorContent("Local wallet tools are not enabled in this server mode.");
|
|
2940
|
-
}
|
|
2941
|
-
|
|
2942
|
-
return content(await localWalletStore.listWallets());
|
|
2943
|
-
}
|
|
2944
|
-
|
|
2945
|
-
async function handleLocalWalletCreate(argumentsObject = {}) {
|
|
2946
|
-
if (!localWalletStore) {
|
|
2947
|
-
return errorContent("Local wallet tools are not enabled in this server mode.");
|
|
2948
|
-
}
|
|
2949
|
-
|
|
2950
|
-
const created = await localWalletStore.createWallet(argumentsObject);
|
|
2951
|
-
|
|
2952
|
-
if (argumentsObject.return_secret === true) {
|
|
2953
|
-
const secret = await localWalletStore.revealSecret(created.wallet.id);
|
|
2954
|
-
created.private_key = secret.private_key;
|
|
2955
|
-
created.warning = secret.warning;
|
|
2956
|
-
}
|
|
2957
|
-
|
|
2958
|
-
if (argumentsObject.save_secret === true) {
|
|
2959
|
-
created.secret_export = await localWalletStore.saveSecret(created.wallet.id, {
|
|
2960
|
-
path: argumentsObject.save_to,
|
|
2961
|
-
format: argumentsObject.save_format,
|
|
2962
|
-
});
|
|
2963
|
-
}
|
|
2964
|
-
|
|
2965
|
-
// Force include_qr_data_url so we can append an inline image content
|
|
2966
|
-
// block below, clients that render images (Claude Desktop, MCP
|
|
2967
|
-
// Inspector) will then show the funding QR right next to the
|
|
2968
|
-
// newly-created wallet without a follow-up tool call.
|
|
2969
|
-
created.configure = await buildConfigurePayload({
|
|
2970
|
-
wallet_id: created.wallet.id,
|
|
2971
|
-
section: "overview",
|
|
2972
|
-
include_qr_data_url: true,
|
|
2973
|
-
});
|
|
2974
|
-
|
|
2975
|
-
const result = content(created);
|
|
2976
|
-
const dataUrl = created.configure?.funding?.receive_on_base?.qr_code_data_url;
|
|
2977
|
-
if (typeof dataUrl === "string" && dataUrl.startsWith("data:image/png;base64,")) {
|
|
2978
|
-
result.content.push({
|
|
2979
|
-
type: "image",
|
|
2980
|
-
data: dataUrl.slice("data:image/png;base64,".length),
|
|
2981
|
-
mimeType: "image/png",
|
|
2982
|
-
});
|
|
2983
|
-
}
|
|
2984
|
-
return result;
|
|
2985
|
-
}
|
|
2986
|
-
|
|
2987
|
-
async function handleConfigure(argumentsObject = {}) {
|
|
2988
|
-
return content(await buildConfigurePayload(argumentsObject));
|
|
2989
|
-
}
|
|
2990
|
-
|
|
2991
|
-
async function handleShowWalletFunding(argumentsObject = {}) {
|
|
2992
|
-
const wallet = await resolveConfigurationWallet(argumentsObject.wallet_id);
|
|
2993
|
-
if (!wallet?.address) {
|
|
2994
|
-
return errorContent(
|
|
2995
|
-
"No managed wallet is available. Use apiosk_create_wallet (or apiosk_get_started) to create one first.",
|
|
2996
|
-
);
|
|
2997
|
-
}
|
|
2998
|
-
|
|
2999
|
-
const funding = await buildFundingOptions({
|
|
3000
|
-
wallet,
|
|
3001
|
-
env,
|
|
3002
|
-
fundingProvider: "manual",
|
|
3003
|
-
includeQrDataUrl: true,
|
|
3004
|
-
});
|
|
3005
|
-
if (!funding?.receive_on_base) {
|
|
3006
|
-
return errorContent("Could not build funding instructions for this wallet.");
|
|
3007
|
-
}
|
|
3008
|
-
|
|
3009
|
-
const receive = funding.receive_on_base;
|
|
3010
|
-
const baseUsdcContract = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
3011
|
-
const lines = [
|
|
3012
|
-
"Send USDC to your Apiosk wallet:",
|
|
3013
|
-
"",
|
|
3014
|
-
`Address: ${receive.address}`,
|
|
3015
|
-
"Network: Base mainnet (chain id 8453)",
|
|
3016
|
-
`Token: USDC (${baseUsdcContract})`,
|
|
3017
|
-
"",
|
|
3018
|
-
"WARNING: only Base mainnet USDC. Sending from Ethereum, Polygon, Solana,",
|
|
3019
|
-
"or any other network will permanently lose the funds, this address is",
|
|
3020
|
-
"Base only.",
|
|
3021
|
-
"",
|
|
3022
|
-
`Block explorer: ${receive.explorer_url}`,
|
|
3023
|
-
];
|
|
3024
|
-
if (receive.qr_code_terminal) {
|
|
3025
|
-
lines.push("", receive.qr_code_terminal);
|
|
3026
|
-
} else if (receive.qr_image_url) {
|
|
3027
|
-
lines.push("", `QR image: ${receive.qr_image_url}`);
|
|
3028
|
-
}
|
|
3029
|
-
|
|
3030
|
-
const messageContent = [{ type: "text", text: lines.join("\n") }];
|
|
3031
|
-
|
|
3032
|
-
// Render the QR inline via MCP image content when the buyer's client
|
|
3033
|
-
// supports images (Claude Desktop, the MCP Inspector, etc.). Falls back
|
|
3034
|
-
// gracefully on terminals without image support, the text block above
|
|
3035
|
-
// still has the address and the ANSI QR.
|
|
3036
|
-
const dataUrl = receive.qr_code_data_url;
|
|
3037
|
-
if (typeof dataUrl === "string" && dataUrl.startsWith("data:image/png;base64,")) {
|
|
3038
|
-
messageContent.push({
|
|
3039
|
-
type: "image",
|
|
3040
|
-
data: dataUrl.slice("data:image/png;base64,".length),
|
|
3041
|
-
mimeType: "image/png",
|
|
3042
|
-
});
|
|
3043
|
-
}
|
|
3044
|
-
|
|
3045
|
-
return {
|
|
3046
|
-
content: messageContent,
|
|
3047
|
-
structuredContent: {
|
|
3048
|
-
wallet_id: wallet.id ?? null,
|
|
3049
|
-
wallet_label: wallet.label ?? null,
|
|
3050
|
-
address: receive.address,
|
|
3051
|
-
network: "base",
|
|
3052
|
-
chain_id: 8453,
|
|
3053
|
-
token_symbol: "USDC",
|
|
3054
|
-
token_contract: baseUsdcContract,
|
|
3055
|
-
explorer_url: receive.explorer_url,
|
|
3056
|
-
qr_payload: receive.qr_payload,
|
|
3057
|
-
qr_image_url: receive.qr_image_url,
|
|
3058
|
-
transfer_uri: receive.transfer_uri,
|
|
3059
|
-
},
|
|
3060
|
-
};
|
|
3061
|
-
}
|
|
3062
|
-
|
|
3063
|
-
async function handleGetStarted(argumentsObject = {}) {
|
|
3064
|
-
if (!localWalletStore) {
|
|
3065
|
-
return errorContent(
|
|
3066
|
-
"apiosk_get_started is only available in the local stdio package because it saves wallet and connect-token state on the local machine."
|
|
3067
|
-
);
|
|
3068
|
-
}
|
|
3069
|
-
|
|
3070
|
-
let savedConfig = await getSavedConfig();
|
|
3071
|
-
let savedConnectConfig = summarizeSavedConnectConfig(savedConfig, env);
|
|
3072
|
-
let createdWallet = null;
|
|
3073
|
-
let selectedWallet = null;
|
|
3074
|
-
|
|
3075
|
-
if (trimString(argumentsObject.connect_string)) {
|
|
3076
|
-
const parsed = parseConnectString(argumentsObject.connect_string);
|
|
3077
|
-
await saveLocalApioskConfig(
|
|
3078
|
-
{
|
|
3079
|
-
gateway_url: parsed.gateway_url,
|
|
3080
|
-
chain_id: parsed.chain_id,
|
|
3081
|
-
wallet_address: parsed.wallet_address,
|
|
3082
|
-
connect_token: parsed.connect_token,
|
|
3083
|
-
connect_authorization: parsed.connect_authorization,
|
|
3084
|
-
connect_header_name: parsed.connect_header_name,
|
|
3085
|
-
daily_limit_usdc: parsed.daily_limit_usdc,
|
|
3086
|
-
per_request_limit_usdc: parsed.per_request_limit_usdc,
|
|
3087
|
-
},
|
|
3088
|
-
env
|
|
3089
|
-
);
|
|
3090
|
-
savedConfig = await getSavedConfig();
|
|
3091
|
-
savedConnectConfig = summarizeSavedConnectConfig(savedConfig, env);
|
|
3092
|
-
} else if (
|
|
3093
|
-
trimString(argumentsObject.connect_token) ||
|
|
3094
|
-
trimString(argumentsObject.connect_authorization)
|
|
3095
|
-
) {
|
|
3096
|
-
await saveLocalApioskConfig(
|
|
3097
|
-
{
|
|
3098
|
-
gateway_url: resolveGatewayBaseUrl(env, savedConfig),
|
|
3099
|
-
chain_id: savedConfig?.chain_id || 8453,
|
|
3100
|
-
wallet_address:
|
|
3101
|
-
trimString(argumentsObject.wallet_address) ||
|
|
3102
|
-
trimString(savedConfig?.agent_wallet_address),
|
|
3103
|
-
connect_token:
|
|
3104
|
-
trimString(argumentsObject.connect_token) || trimString(savedConfig?.connect_token),
|
|
3105
|
-
connect_authorization:
|
|
3106
|
-
trimString(argumentsObject.connect_authorization) ||
|
|
3107
|
-
trimString(savedConfig?.connect_authorization),
|
|
3108
|
-
connect_header_name:
|
|
3109
|
-
trimString(argumentsObject.connect_header_name) ||
|
|
3110
|
-
trimString(savedConfig?.connect_header_name) ||
|
|
3111
|
-
"X-Apiosk-Connect-Token",
|
|
3112
|
-
daily_limit_usdc: savedConfig?.daily_limit_usdc,
|
|
3113
|
-
per_request_limit_usdc: savedConfig?.per_request_limit_usdc,
|
|
3114
|
-
},
|
|
3115
|
-
env
|
|
3116
|
-
);
|
|
3117
|
-
savedConfig = await getSavedConfig();
|
|
3118
|
-
savedConnectConfig = summarizeSavedConnectConfig(savedConfig, env);
|
|
3119
|
-
}
|
|
3120
|
-
|
|
3121
|
-
if (trimString(argumentsObject.wallet_id)) {
|
|
3122
|
-
const selected = await localWalletStore.selectWallet(trimString(argumentsObject.wallet_id));
|
|
3123
|
-
selectedWallet = selected.wallet;
|
|
3124
|
-
}
|
|
3125
|
-
|
|
3126
|
-
let activeWallet = await resolveConfigurationWallet(argumentsObject.wallet_id);
|
|
3127
|
-
let authState = buildAuthState({ env, savedConfig, activeWallet });
|
|
3128
|
-
|
|
3129
|
-
if (authState.mode === "none" && argumentsObject.create_wallet !== false) {
|
|
3130
|
-
const created = await localWalletStore.createWallet({
|
|
3131
|
-
label: trimString(argumentsObject.wallet_label) || "Apiosk wallet",
|
|
3132
|
-
set_active: true,
|
|
3133
|
-
});
|
|
3134
|
-
createdWallet = created.wallet;
|
|
3135
|
-
activeWallet = await resolveConfigurationWallet(created.wallet.id);
|
|
3136
|
-
authState = buildAuthState({ env, savedConfig, activeWallet });
|
|
3137
|
-
}
|
|
3138
|
-
|
|
3139
|
-
if (authState.mode === "none") {
|
|
3140
|
-
return errorContent({
|
|
3141
|
-
error:
|
|
3142
|
-
"No Apiosk payment or access method is configured yet. Provide a connect string or allow apiosk_get_started to create a local wallet.",
|
|
3143
|
-
local_config_paths: createApioskLocalConfigPaths(env),
|
|
3144
|
-
next_steps: [
|
|
3145
|
-
"Call apiosk_get_started with connect_string from the dashboard, or set create_wallet=true.",
|
|
3146
|
-
"If you already manage auth outside the MCP package, set APIOSK_PRIVATE_KEY or APIOSK_CONNECT_TOKEN in the MCP environment.",
|
|
3147
|
-
],
|
|
3148
|
-
});
|
|
3149
|
-
}
|
|
3150
|
-
|
|
3151
|
-
const client = await getClient();
|
|
3152
|
-
const discoveryResponse = await client.listApis({ limit: 5, offset: 0 });
|
|
3153
|
-
await getTools(true);
|
|
3154
|
-
|
|
3155
|
-
const discoveredApis = (discoveryResponse.apis || []).map((api) =>
|
|
3156
|
-
buildCatalogEntry(api, cache.toolNamesBySlug.get(api.slug) || null)
|
|
3157
|
-
);
|
|
3158
|
-
const testSlug =
|
|
3159
|
-
trimString(argumentsObject.test_slug) ||
|
|
3160
|
-
discoveredApis.find((api) => api.slug)?.slug ||
|
|
3161
|
-
null;
|
|
3162
|
-
const testInput =
|
|
3163
|
-
Object.prototype.hasOwnProperty.call(argumentsObject, "test_input") ?
|
|
3164
|
-
argumentsObject.test_input :
|
|
3165
|
-
{};
|
|
3166
|
-
|
|
3167
|
-
let metadata = null;
|
|
3168
|
-
if (testSlug) {
|
|
3169
|
-
metadata = await client.getMetadata(testSlug).catch((error) => ({
|
|
3170
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3171
|
-
}));
|
|
3172
|
-
}
|
|
3173
|
-
|
|
3174
|
-
let status = "configured";
|
|
3175
|
-
let summary =
|
|
3176
|
-
"Apiosk is configured locally. Discovery worked, but the test execute call still needs a specific slug or input payload.";
|
|
3177
|
-
let executeResult = null;
|
|
3178
|
-
let executeError = null;
|
|
3179
|
-
let configure = null;
|
|
3180
|
-
|
|
3181
|
-
if (testSlug) {
|
|
3182
|
-
try {
|
|
3183
|
-
executeResult = await client.execute(testSlug, testInput, {
|
|
3184
|
-
operation: argumentsObject.test_operation,
|
|
3185
|
-
});
|
|
3186
|
-
status = "ready";
|
|
3187
|
-
summary = `Apiosk is configured and the test call to ${testSlug} succeeded.`;
|
|
3188
|
-
} catch (error) {
|
|
3189
|
-
if (error instanceof ApioskPaymentRequiredError) {
|
|
3190
|
-
status = "needs_funding";
|
|
3191
|
-
summary =
|
|
3192
|
-
"Apiosk is configured, but the first paid execute call still needs wallet funding or a managed wallet with spend available.";
|
|
3193
|
-
executeError = {
|
|
3194
|
-
error: error.message,
|
|
3195
|
-
payment_required: error.paymentRequired,
|
|
3196
|
-
};
|
|
3197
|
-
if (activeWallet?.source === "local_store" && activeWallet?.id) {
|
|
3198
|
-
configure = await buildConfigurePayload({
|
|
3199
|
-
wallet_id: activeWallet.id,
|
|
3200
|
-
section: "funding",
|
|
3201
|
-
include_qr_data_url: argumentsObject.include_qr_data_url,
|
|
3202
|
-
});
|
|
3203
|
-
}
|
|
3204
|
-
} else {
|
|
3205
|
-
executeError = {
|
|
3206
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3207
|
-
};
|
|
3208
|
-
}
|
|
3209
|
-
}
|
|
3210
|
-
}
|
|
3211
|
-
|
|
3212
|
-
if (!configure && activeWallet?.source === "local_store" && activeWallet?.id) {
|
|
3213
|
-
configure = await buildConfigurePayload({
|
|
3214
|
-
wallet_id: activeWallet.id,
|
|
3215
|
-
section: "overview",
|
|
3216
|
-
include_qr_data_url: argumentsObject.include_qr_data_url,
|
|
3217
|
-
});
|
|
3218
|
-
}
|
|
3219
|
-
|
|
3220
|
-
const nextSteps = [];
|
|
3221
|
-
if (status === "ready") {
|
|
3222
|
-
nextSteps.push(
|
|
3223
|
-
"Call the API-specific dynamic tool from discovery, or use apiosk_execute with a different slug.",
|
|
3224
|
-
"Use apiosk_publish_api when you want to list your own API behind the gateway."
|
|
3225
|
-
);
|
|
3226
|
-
} else if (status === "needs_funding") {
|
|
3227
|
-
nextSteps.push(
|
|
3228
|
-
"Fund the selected local wallet, then rerun apiosk_get_started or retry the API-specific tool.",
|
|
3229
|
-
"If you prefer managed access, import a dashboard connect string into apiosk_get_started instead of using a local wallet."
|
|
3230
|
-
);
|
|
3231
|
-
} else {
|
|
3232
|
-
nextSteps.push(
|
|
3233
|
-
"Provide test_slug and test_input to apiosk_get_started if you want a more representative first execute call.",
|
|
3234
|
-
"Use apiosk_get_api on one of the discovered slugs to inspect its metadata and input shape before retrying."
|
|
3235
|
-
);
|
|
3236
|
-
}
|
|
3237
|
-
|
|
3238
|
-
return content({
|
|
3239
|
-
status,
|
|
3240
|
-
summary,
|
|
3241
|
-
auth: {
|
|
3242
|
-
...authState,
|
|
3243
|
-
saved_connect_config: savedConnectConfig,
|
|
3244
|
-
local_config_paths: createApioskLocalConfigPaths(env),
|
|
3245
|
-
},
|
|
3246
|
-
setup: {
|
|
3247
|
-
created_wallet: createdWallet,
|
|
3248
|
-
selected_wallet: selectedWallet,
|
|
3249
|
-
active_wallet: activeWallet,
|
|
3250
|
-
},
|
|
3251
|
-
discovery: {
|
|
3252
|
-
apis: discoveredApis,
|
|
3253
|
-
meta: discoveryResponse.meta || null,
|
|
3254
|
-
},
|
|
3255
|
-
test: {
|
|
3256
|
-
slug: testSlug,
|
|
3257
|
-
operation: trimString(argumentsObject.test_operation) || null,
|
|
3258
|
-
input: testInput,
|
|
3259
|
-
metadata,
|
|
3260
|
-
result: executeResult,
|
|
3261
|
-
error: executeError,
|
|
3262
|
-
},
|
|
3263
|
-
configure,
|
|
3264
|
-
next_steps: nextSteps,
|
|
3265
|
-
});
|
|
3266
|
-
}
|
|
3267
|
-
|
|
3268
|
-
async function handleLocalWalletSelect(argumentsObject = {}) {
|
|
3269
|
-
if (!localWalletStore) {
|
|
3270
|
-
return errorContent("Local wallet tools are not enabled in this server mode.");
|
|
3271
|
-
}
|
|
3272
|
-
if (!argumentsObject.wallet_id) {
|
|
3273
|
-
return errorContent("Missing required field: wallet_id");
|
|
3274
|
-
}
|
|
3275
|
-
|
|
3276
|
-
const selected = await localWalletStore.selectWallet(argumentsObject.wallet_id);
|
|
3277
|
-
selected.configure = await buildConfigurePayload({
|
|
3278
|
-
wallet_id: argumentsObject.wallet_id,
|
|
3279
|
-
section: "overview",
|
|
3280
|
-
include_qr_data_url: argumentsObject.include_qr_data_url,
|
|
3281
|
-
});
|
|
3282
|
-
return content(selected);
|
|
3283
|
-
}
|
|
3284
|
-
|
|
3285
|
-
async function handleLocalWalletUpdate(argumentsObject = {}) {
|
|
3286
|
-
if (!localWalletStore) {
|
|
3287
|
-
return errorContent("Local wallet tools are not enabled in this server mode.");
|
|
3288
|
-
}
|
|
3289
|
-
if (!argumentsObject.wallet_id) {
|
|
3290
|
-
return errorContent("Missing required field: wallet_id");
|
|
3291
|
-
}
|
|
3292
|
-
|
|
3293
|
-
return content(
|
|
3294
|
-
await localWalletStore.updateWallet(argumentsObject.wallet_id, {
|
|
3295
|
-
label: argumentsObject.label,
|
|
3296
|
-
set_active: argumentsObject.set_active,
|
|
3297
|
-
})
|
|
3298
|
-
);
|
|
3299
|
-
}
|
|
3300
|
-
|
|
3301
|
-
async function handleLocalWalletDelete(argumentsObject = {}) {
|
|
3302
|
-
if (!localWalletStore) {
|
|
3303
|
-
return errorContent("Local wallet tools are not enabled in this server mode.");
|
|
3304
|
-
}
|
|
3305
|
-
if (!argumentsObject.wallet_id) {
|
|
3306
|
-
return errorContent("Missing required field: wallet_id");
|
|
3307
|
-
}
|
|
3308
|
-
|
|
3309
|
-
return content(await localWalletStore.deleteWallet(argumentsObject.wallet_id));
|
|
3310
|
-
}
|
|
3311
|
-
|
|
3312
|
-
async function handleLocalWalletReveal(argumentsObject = {}) {
|
|
3313
|
-
if (!localWalletStore) {
|
|
3314
|
-
return errorContent("Local wallet tools are not enabled in this server mode.");
|
|
3315
|
-
}
|
|
3316
|
-
if (!argumentsObject.wallet_id) {
|
|
3317
|
-
return errorContent("Missing required field: wallet_id");
|
|
3318
|
-
}
|
|
3319
|
-
|
|
3320
|
-
return content(await localWalletStore.revealSecret(argumentsObject.wallet_id));
|
|
3321
|
-
}
|
|
3322
|
-
|
|
3323
|
-
async function handleLocalWalletSave(argumentsObject = {}) {
|
|
3324
|
-
if (!localWalletStore) {
|
|
3325
|
-
return errorContent("Local wallet tools are not enabled in this server mode.");
|
|
3326
|
-
}
|
|
3327
|
-
if (!argumentsObject.wallet_id) {
|
|
3328
|
-
return errorContent("Missing required field: wallet_id");
|
|
3329
|
-
}
|
|
3330
|
-
|
|
3331
|
-
return content(
|
|
3332
|
-
await localWalletStore.saveSecret(argumentsObject.wallet_id, {
|
|
3333
|
-
path: argumentsObject.path,
|
|
3334
|
-
format: argumentsObject.format,
|
|
3335
|
-
})
|
|
3336
|
-
);
|
|
3337
|
-
}
|
|
3338
|
-
|
|
3339
|
-
async function handlePublishApi(argumentsObject = {}) {
|
|
3340
|
-
const wallet = await resolvePublishingWallet(argumentsObject.wallet_id);
|
|
3341
|
-
const slug = trimString(argumentsObject.slug);
|
|
3342
|
-
const endpointUrl = trimString(argumentsObject.endpoint_url);
|
|
3343
|
-
const priceUsd = Number(argumentsObject.price_usd);
|
|
3344
|
-
const name = trimString(argumentsObject.name);
|
|
3345
|
-
const description = trimString(argumentsObject.description);
|
|
3346
|
-
|
|
3347
|
-
if (!name || !slug || !endpointUrl || !description || !Number.isFinite(priceUsd)) {
|
|
3348
|
-
return errorContent(
|
|
3349
|
-
"Missing required publish fields. Expected name, slug, endpoint_url, price_usd, and description."
|
|
3350
|
-
);
|
|
3351
|
-
}
|
|
3352
|
-
|
|
3353
|
-
if (!validateSlug(slug)) {
|
|
3354
|
-
return errorContent("Slug must use lowercase letters, numbers, and hyphens only.");
|
|
3355
|
-
}
|
|
3356
|
-
|
|
3357
|
-
if (!endpointUrl.startsWith("https://")) {
|
|
3358
|
-
return errorContent("endpoint_url must start with https://");
|
|
3359
|
-
}
|
|
3360
|
-
|
|
3361
|
-
const category = resolveCategory(argumentsObject.category, argumentsObject.listing_group);
|
|
3362
|
-
const payload = {
|
|
3363
|
-
name,
|
|
3364
|
-
slug,
|
|
3365
|
-
endpoint_url: endpointUrl,
|
|
3366
|
-
price_usd: priceUsd,
|
|
3367
|
-
description,
|
|
3368
|
-
owner_wallet: wallet.address,
|
|
3369
|
-
category,
|
|
3370
|
-
listing_metadata: buildListingMetadata({
|
|
3371
|
-
name,
|
|
3372
|
-
slug,
|
|
3373
|
-
description,
|
|
3374
|
-
endpoint_url: endpointUrl,
|
|
3375
|
-
price_usd: priceUsd,
|
|
3376
|
-
category,
|
|
3377
|
-
listing_group: argumentsObject.listing_group,
|
|
3378
|
-
listing_metadata: argumentsObject.listing_metadata,
|
|
3379
|
-
}),
|
|
3380
|
-
};
|
|
3381
|
-
|
|
3382
|
-
const response = await requestGatewayManagement({
|
|
3383
|
-
baseUrl: resolveGatewayBaseUrl(env),
|
|
3384
|
-
path: "/v1/apis/register",
|
|
3385
|
-
method: "POST",
|
|
3386
|
-
body: payload,
|
|
3387
|
-
action: "register_api",
|
|
3388
|
-
resource: `register:${slug}`,
|
|
3389
|
-
wallet,
|
|
3390
|
-
});
|
|
3391
|
-
|
|
3392
|
-
cache.expiresAt = 0;
|
|
3393
|
-
|
|
3394
|
-
return content({
|
|
3395
|
-
...response,
|
|
3396
|
-
wallet: {
|
|
3397
|
-
address: wallet.address,
|
|
3398
|
-
source: wallet.source,
|
|
3399
|
-
},
|
|
3400
|
-
});
|
|
3401
|
-
}
|
|
3402
|
-
|
|
3403
|
-
async function handleListMyApis(argumentsObject = {}) {
|
|
3404
|
-
const wallet = await resolvePublishingWallet(argumentsObject.wallet_id);
|
|
3405
|
-
const response = await requestGatewayManagement({
|
|
3406
|
-
baseUrl: resolveGatewayBaseUrl(env),
|
|
3407
|
-
path: `/v1/apis/mine?wallet=${encodeURIComponent(wallet.address)}`,
|
|
3408
|
-
method: "GET",
|
|
3409
|
-
action: "my_apis",
|
|
3410
|
-
resource: `mine:${wallet.address}`,
|
|
3411
|
-
wallet,
|
|
3412
|
-
});
|
|
3413
|
-
|
|
3414
|
-
return content({
|
|
3415
|
-
wallet: {
|
|
3416
|
-
address: wallet.address,
|
|
3417
|
-
source: wallet.source,
|
|
3418
|
-
},
|
|
3419
|
-
...response,
|
|
3420
|
-
});
|
|
3421
|
-
}
|
|
3422
|
-
|
|
3423
|
-
async function handleUpdateApi(argumentsObject = {}) {
|
|
3424
|
-
const wallet = await resolvePublishingWallet(argumentsObject.wallet_id);
|
|
3425
|
-
const slug = trimString(argumentsObject.slug);
|
|
3426
|
-
|
|
3427
|
-
if (!slug) {
|
|
3428
|
-
return errorContent("Missing required field: slug");
|
|
3429
|
-
}
|
|
3430
|
-
|
|
3431
|
-
const body = {
|
|
3432
|
-
owner_wallet: wallet.address,
|
|
3433
|
-
};
|
|
3434
|
-
|
|
3435
|
-
if (argumentsObject.endpoint_url !== undefined) {
|
|
3436
|
-
const endpointUrl = trimString(argumentsObject.endpoint_url);
|
|
3437
|
-
if (!endpointUrl.startsWith("https://")) {
|
|
3438
|
-
return errorContent("endpoint_url must start with https://");
|
|
3439
|
-
}
|
|
3440
|
-
body.endpoint_url = endpointUrl;
|
|
3441
|
-
}
|
|
3442
|
-
|
|
3443
|
-
if (argumentsObject.price_usd !== undefined) {
|
|
3444
|
-
const priceUsd = Number(argumentsObject.price_usd);
|
|
3445
|
-
if (!Number.isFinite(priceUsd)) {
|
|
3446
|
-
return errorContent("price_usd must be a valid number");
|
|
3447
|
-
}
|
|
3448
|
-
body.price_usd = priceUsd;
|
|
3449
|
-
}
|
|
3450
|
-
|
|
3451
|
-
if (argumentsObject.description !== undefined) {
|
|
3452
|
-
body.description = trimString(argumentsObject.description);
|
|
3453
|
-
}
|
|
3454
|
-
|
|
3455
|
-
if (argumentsObject.active !== undefined) {
|
|
3456
|
-
body.active = Boolean(argumentsObject.active);
|
|
3457
|
-
}
|
|
3458
|
-
|
|
3459
|
-
if (argumentsObject.listing_metadata !== undefined) {
|
|
3460
|
-
body.listing_metadata = argumentsObject.listing_metadata;
|
|
3461
|
-
}
|
|
3462
|
-
|
|
3463
|
-
const response = await requestGatewayManagement({
|
|
3464
|
-
baseUrl: resolveGatewayBaseUrl(env),
|
|
3465
|
-
path: `/v1/apis/${encodeURIComponent(slug)}`,
|
|
3466
|
-
method: "POST",
|
|
3467
|
-
body,
|
|
3468
|
-
action: "update_api",
|
|
3469
|
-
resource: `update:${slug}`,
|
|
3470
|
-
wallet,
|
|
3471
|
-
});
|
|
3472
|
-
|
|
3473
|
-
cache.expiresAt = 0;
|
|
3474
|
-
|
|
3475
|
-
return content({
|
|
3476
|
-
slug,
|
|
3477
|
-
wallet: {
|
|
3478
|
-
address: wallet.address,
|
|
3479
|
-
source: wallet.source,
|
|
3480
|
-
},
|
|
3481
|
-
...response,
|
|
3482
|
-
});
|
|
3483
|
-
}
|
|
3484
|
-
|
|
3485
|
-
async function handleDeleteApi(argumentsObject = {}) {
|
|
3486
|
-
const wallet = await resolvePublishingWallet(argumentsObject.wallet_id);
|
|
3487
|
-
const slug = trimString(argumentsObject.slug);
|
|
3488
|
-
|
|
3489
|
-
if (!slug) {
|
|
3490
|
-
return errorContent("Missing required field: slug");
|
|
3491
|
-
}
|
|
3492
|
-
|
|
3493
|
-
const response = await requestGatewayManagement({
|
|
3494
|
-
baseUrl: resolveGatewayBaseUrl(env),
|
|
3495
|
-
path: `/v1/apis/${encodeURIComponent(slug)}?wallet=${encodeURIComponent(wallet.address)}`,
|
|
3496
|
-
method: "DELETE",
|
|
3497
|
-
action: "delete_api",
|
|
3498
|
-
resource: `delete:${slug}`,
|
|
3499
|
-
wallet,
|
|
3500
|
-
});
|
|
3501
|
-
|
|
3502
|
-
cache.expiresAt = 0;
|
|
3503
|
-
|
|
3504
|
-
return content({
|
|
3505
|
-
slug,
|
|
3506
|
-
wallet: {
|
|
3507
|
-
address: wallet.address,
|
|
3508
|
-
source: wallet.source,
|
|
3509
|
-
},
|
|
3510
|
-
...response,
|
|
3511
|
-
});
|
|
3512
|
-
}
|
|
3513
|
-
|
|
3514
|
-
// A 402 means the buyer's configured rails could not cover the call. Tell
|
|
3515
|
-
// the caller what THEIR next step is, based on how this request
|
|
3516
|
-
// authenticated, instead of always giving local-stdio advice.
|
|
3517
|
-
function buildPaymentRequiredHint(authInfo = null) {
|
|
3518
|
-
const extra = authInfo?.extra || {};
|
|
3519
|
-
const connectWallet = trimString(
|
|
3520
|
-
extra.apiosk_connect_wallet_address || extra.apioskConnectWalletAddress
|
|
3521
|
-
);
|
|
3522
|
-
|
|
3523
|
-
if (trimString(extra.apiosk_connect_token)) {
|
|
3524
|
-
const walletNote = connectWallet ? ` (${connectWallet})` : "";
|
|
3525
|
-
return (
|
|
3526
|
-
`Your managed Apiosk wallet${walletNote} could not cover this call — it is out of USDC, ` +
|
|
3527
|
-
"over its spending limit, or missing settlement approval. Send USDC on Base mainnet " +
|
|
3528
|
-
"(chain 8453) to the wallet address and retry; payment then settles automatically. " +
|
|
3529
|
-
"Use apiosk_list_wallets to see your wallets and funding instructions."
|
|
3530
|
-
);
|
|
3531
|
-
}
|
|
3532
|
-
|
|
3533
|
-
if (hasRequestScopedDashboardAccess(authInfo)) {
|
|
3534
|
-
return (
|
|
3535
|
-
"You are signed in, but no payable managed wallet is linked to this session. " +
|
|
3536
|
-
"Use apiosk_list_wallets to check your wallets: if one exists, fund it with USDC on Base mainnet " +
|
|
3537
|
-
"and re-authorize the Apiosk app so a fresh payment token is minted; if none exists, set one up " +
|
|
3538
|
-
"in the Apiosk buyer portal first."
|
|
3539
|
-
);
|
|
3540
|
-
}
|
|
3541
|
-
|
|
3542
|
-
return hostedAuthEnabled
|
|
3543
|
-
? "Authorize Apiosk in your MCP client, then use apiosk_list_wallets to confirm that a funded managed wallet is linked before retrying."
|
|
3544
|
-
: "Run apiosk_get_started in the local stdio package, or configure APIOSK_PRIVATE_KEY, to enable automatic x402 settlement.";
|
|
3545
|
-
}
|
|
3546
|
-
|
|
3547
|
-
// Observability wrapper: time + log every tools/call dispatch to mcp_tool_calls
|
|
3548
|
-
// (fire-and-forget — a logging failure never affects the tool result). See
|
|
3549
|
-
// observability.mjs. Raw tokens/args are never persisted (hash + key-names only).
|
|
3550
|
-
async function callTool(name, argumentsObject = {}, authInfo = null) {
|
|
3551
|
-
const startedAt = Date.now();
|
|
3552
|
-
let outcome = "ok";
|
|
3553
|
-
let errorCode = null;
|
|
3554
|
-
try {
|
|
3555
|
-
const result = await dispatchTool(name, argumentsObject, authInfo);
|
|
3556
|
-
if (result && result.isError) outcome = "error";
|
|
3557
|
-
else if (result && result.status === "payment_required") outcome = "refused";
|
|
3558
|
-
return result;
|
|
3559
|
-
} catch (error) {
|
|
3560
|
-
outcome = "error";
|
|
3561
|
-
errorCode = (error && (error.code || error.name)) || null;
|
|
3562
|
-
throw error;
|
|
3563
|
-
} finally {
|
|
3564
|
-
try {
|
|
3565
|
-
logToolCall(env, {
|
|
3566
|
-
toolName: name,
|
|
3567
|
-
outcome,
|
|
3568
|
-
errorCode,
|
|
3569
|
-
latencyMs: Date.now() - startedAt,
|
|
3570
|
-
authInfo,
|
|
3571
|
-
argKeys: argumentsObject && typeof argumentsObject === "object" ? Object.keys(argumentsObject) : [],
|
|
3572
|
-
});
|
|
3573
|
-
} catch {
|
|
3574
|
-
/* observability must never break a tool call */
|
|
3575
|
-
}
|
|
3576
|
-
}
|
|
3577
|
-
}
|
|
3578
|
-
|
|
3579
|
-
async function dispatchTool(name, argumentsObject = {}, authInfo = null) {
|
|
3580
|
-
try {
|
|
3581
|
-
if (hostedAuthEnabled && LOCAL_ONLY_TOOL_NAMES.has(name)) {
|
|
3582
|
-
return content({
|
|
3583
|
-
status: "unsupported",
|
|
3584
|
-
error_code: "tool.local_only",
|
|
3585
|
-
message: `${name} is only available in the local stdio package and cannot run on the hosted Apiosk MCP server.`,
|
|
3586
|
-
next_steps: [
|
|
3587
|
-
"Use apiosk_search, apiosk_explore, or apiosk_discover anonymously.",
|
|
3588
|
-
"For paid or account-specific actions, authorize Apiosk in the MCP client and use apiosk_list_wallets or apiosk_execute.",
|
|
3589
|
-
],
|
|
3590
|
-
});
|
|
3591
|
-
}
|
|
3592
|
-
|
|
3593
|
-
if (name === "apiosk_help") return await handleHelp(argumentsObject);
|
|
3594
|
-
if (name === "apiosk_payment_guide") return await handlePaymentGuide(argumentsObject, authInfo);
|
|
3595
|
-
if (name === "apiosk_explore") return await handleExplore(argumentsObject, authInfo);
|
|
3596
|
-
if (name === "apiosk_search") return await handleSearch(argumentsObject, authInfo);
|
|
3597
|
-
if (name === "apiosk_discover") return await handleDiscover(argumentsObject, authInfo);
|
|
3598
|
-
if (name === "apiosk_inspect_x402") return await handleInspect(argumentsObject, authInfo);
|
|
3599
|
-
if (name === "apiosk_fetch_paid") return await handleFetchPaid(argumentsObject, authInfo);
|
|
3600
|
-
if (name === "apiosk_get_api" || name === "apiosk_metadata") {
|
|
3601
|
-
return await handleGetApi(argumentsObject, authInfo);
|
|
3602
|
-
}
|
|
3603
|
-
if (name === "apiosk_execute") return await handleExecute(argumentsObject, authInfo);
|
|
3604
|
-
if (name === "apiosk_health") return await handleHealth(argumentsObject, authInfo);
|
|
3605
|
-
|
|
3606
|
-
if (name === "apiosk_wallet_list") return await handleLocalWalletList();
|
|
3607
|
-
if (name === "apiosk_wallet_create") return await handleLocalWalletCreate(argumentsObject);
|
|
3608
|
-
if (name === "apiosk_get_started") return await handleGetStarted(argumentsObject);
|
|
3609
|
-
if (name === "apiosk_configure") return await handleConfigure(argumentsObject);
|
|
3610
|
-
if (name === "apiosk_show_wallet_funding")
|
|
3611
|
-
return await handleShowWalletFunding(argumentsObject);
|
|
3612
|
-
if (name === "apiosk_wallet_select") return await handleLocalWalletSelect(argumentsObject);
|
|
3613
|
-
if (name === "apiosk_wallet_update") return await handleLocalWalletUpdate(argumentsObject);
|
|
3614
|
-
if (name === "apiosk_wallet_delete") return await handleLocalWalletDelete(argumentsObject);
|
|
3615
|
-
if (name === "apiosk_wallet_reveal_secret") return await handleLocalWalletReveal(argumentsObject);
|
|
3616
|
-
if (name === "apiosk_wallet_save_secret") return await handleLocalWalletSave(argumentsObject);
|
|
3617
|
-
|
|
3618
|
-
if (isPublisherTool(name)) return await handlePublisherTool(name, argumentsObject, authInfo, { env });
|
|
3619
|
-
|
|
3620
|
-
if (name === "apiosk_publish_api") return await handlePublishApi(argumentsObject);
|
|
3621
|
-
if (name === "apiosk_list_my_apis") return await handleListMyApis(argumentsObject);
|
|
3622
|
-
if (name === "apiosk_update_api") return await handleUpdateApi(argumentsObject);
|
|
3623
|
-
if (name === "apiosk_delete_api") return await handleDeleteApi(argumentsObject);
|
|
3624
|
-
|
|
3625
|
-
if (name === "apiosk_create_account") return await handleCreateAccount(argumentsObject);
|
|
3626
|
-
if (name === "apiosk_sign_in") return await handleSignIn(argumentsObject);
|
|
3627
|
-
if (name === "apiosk_list_wallets") return await handleWalletList(authInfo);
|
|
3628
|
-
if (name === "apiosk_create_wallet") return await handleWalletCreate(argumentsObject, authInfo);
|
|
3629
|
-
if (name === "apiosk_update_wallet") return await handleWalletUpdate(argumentsObject, authInfo);
|
|
3630
|
-
if (name === "apiosk_delete_wallet") return await handleWalletDelete(argumentsObject, authInfo);
|
|
3631
|
-
if (name === "apiosk_get_wallet_activity") return await handleWalletActivity(argumentsObject, authInfo);
|
|
3632
|
-
if (name === "apiosk_create_wallet_connect_string") return await handleWalletConnectString(argumentsObject, authInfo);
|
|
3633
|
-
if (name === "apiosk_list_wallet_api_keys") return await handleWalletApiKeys(argumentsObject, authInfo);
|
|
3634
|
-
if (name === "apiosk_create_wallet_api_key") return await handleWalletApiKeyCreate(argumentsObject, authInfo);
|
|
3635
|
-
if (name === "apiosk_update_wallet_api_key") return await handleWalletApiKeyUpdate(argumentsObject, authInfo);
|
|
3636
|
-
if (name === "apiosk_delete_wallet_api_key") return await handleWalletApiKeyDelete(argumentsObject, authInfo);
|
|
3637
|
-
|
|
3638
|
-
const tool = await resolveDynamicTool(name, authInfo);
|
|
3639
|
-
if (!tool) {
|
|
3640
|
-
return errorContent(`Unknown Apiosk tool: ${name}`);
|
|
3641
|
-
}
|
|
3642
|
-
|
|
3643
|
-
return await handleDynamicExecute(tool, argumentsObject, authInfo);
|
|
3644
|
-
} catch (error) {
|
|
3645
|
-
if (error instanceof ApioskPaymentRequiredError) {
|
|
3646
|
-
// A valid 402 is a business state, not an MCP protocol failure. Returning
|
|
3647
|
-
// isError made ChatGPT/Claude collapse it into JSON-RPC -32603 and hid
|
|
3648
|
-
// the actionable funding guidance from the model.
|
|
3649
|
-
return content({
|
|
3650
|
-
status: "payment_required",
|
|
3651
|
-
error_code: "payment.wallet_unfunded_or_unavailable",
|
|
3652
|
-
message: error.message,
|
|
3653
|
-
hint: buildPaymentRequiredHint(authInfo),
|
|
3654
|
-
payment_required: error.paymentRequired,
|
|
3655
|
-
});
|
|
3656
|
-
}
|
|
3657
|
-
|
|
3658
|
-
return errorContent({
|
|
3659
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3660
|
-
});
|
|
3661
|
-
}
|
|
3662
|
-
}
|
|
3663
|
-
|
|
3664
|
-
return {
|
|
3665
|
-
listTools: async (authInfo = null) => {
|
|
3666
|
-
const tools = await getTools(false, authInfo);
|
|
3667
|
-
return Promise.all(
|
|
3668
|
-
tools.map(async (tool) =>
|
|
3669
|
-
normalizeToolForClient(tool, {
|
|
3670
|
-
hosted: hostedAuthEnabled,
|
|
3671
|
-
protectedTool: hostedAuthEnabled
|
|
3672
|
-
? await isToolProtected(tool.name, authInfo)
|
|
3673
|
-
: false,
|
|
3674
|
-
})
|
|
3675
|
-
)
|
|
3676
|
-
);
|
|
3677
|
-
},
|
|
3678
|
-
isToolProtected,
|
|
3679
|
-
callTool,
|
|
3680
|
-
};
|
|
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) } });
|
|
3681
35
|
}
|