@secondlayer/mcp 3.9.0 → 4.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 +20 -20
- package/dist/bin-http.js +163 -541
- package/dist/bin-http.js.map +13 -15
- package/dist/bin.js +163 -541
- package/dist/bin.js.map +13 -15
- package/dist/index.js +163 -541
- package/dist/index.js.map +13 -15
- package/package.json +5 -5
package/dist/bin-http.js
CHANGED
|
@@ -13,26 +13,54 @@ import { fileURLToPath } from "node:url";
|
|
|
13
13
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
14
|
|
|
15
15
|
// src/resources.ts
|
|
16
|
-
import { CHAIN_TRIGGER_FIELDS
|
|
16
|
+
import { CHAIN_TRIGGER_FIELDS } from "@secondlayer/shared";
|
|
17
17
|
import { TRAIT_STANDARDS } from "@secondlayer/stacks/clarity";
|
|
18
18
|
import { TYPE_MAP } from "@secondlayer/subgraphs/schema";
|
|
19
19
|
|
|
20
20
|
// src/lib/client.ts
|
|
21
|
-
import { SecondLayer } from "@secondlayer/sdk";
|
|
21
|
+
import { SecondLayer, readX402Receipt, withX402 } from "@secondlayer/sdk";
|
|
22
|
+
import { privateKeyToAccount } from "@secondlayer/stacks/accounts";
|
|
22
23
|
var instance = null;
|
|
23
24
|
function readApiKey() {
|
|
24
25
|
return process.env.SL_API_KEY;
|
|
25
26
|
}
|
|
27
|
+
function buildFetchImpl() {
|
|
28
|
+
const key = process.env.X402_PRIVATE_KEY;
|
|
29
|
+
if (!key)
|
|
30
|
+
return;
|
|
31
|
+
const account = privateKeyToAccount(key);
|
|
32
|
+
const topUpUsd = Number(process.env.X402_TOPUP_USD ?? "");
|
|
33
|
+
const paid = withX402(fetch, {
|
|
34
|
+
account,
|
|
35
|
+
...Number.isFinite(topUpUsd) && topUpUsd > 0 ? {
|
|
36
|
+
topUp: {
|
|
37
|
+
usd: topUpUsd,
|
|
38
|
+
whenBelow: Number(process.env.X402_TOPUP_WHEN_BELOW ?? "0.5")
|
|
39
|
+
}
|
|
40
|
+
} : {}
|
|
41
|
+
});
|
|
42
|
+
const wrapped = async (input, init) => {
|
|
43
|
+
const res = await paid(input, init);
|
|
44
|
+
const receipt = readX402Receipt(res);
|
|
45
|
+
if (receipt) {
|
|
46
|
+
console.error(`[x402] paid call settled tier=${receipt.state ?? "confirmed"} txid=${receipt.txid}`);
|
|
47
|
+
}
|
|
48
|
+
return res;
|
|
49
|
+
};
|
|
50
|
+
return wrapped;
|
|
51
|
+
}
|
|
26
52
|
function getClient() {
|
|
27
53
|
if (!instance) {
|
|
28
54
|
const apiKey = readApiKey();
|
|
29
55
|
const baseUrl = process.env.SECONDLAYER_API_URL;
|
|
30
56
|
const dumpsBaseUrl = process.env.SL_STREAMS_DUMPS_URL;
|
|
57
|
+
const fetchImpl = buildFetchImpl();
|
|
31
58
|
instance = new SecondLayer({
|
|
32
59
|
...apiKey ? { apiKey } : {},
|
|
33
60
|
origin: "mcp",
|
|
34
61
|
...baseUrl ? { baseUrl } : {},
|
|
35
|
-
...dumpsBaseUrl ? { dumpsBaseUrl } : {}
|
|
62
|
+
...dumpsBaseUrl ? { dumpsBaseUrl } : {},
|
|
63
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
36
64
|
});
|
|
37
65
|
}
|
|
38
66
|
return instance;
|
|
@@ -173,25 +201,21 @@ var COLUMN_TYPES = [
|
|
|
173
201
|
}
|
|
174
202
|
];
|
|
175
203
|
var PRODUCT_BLURBS = {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
contracts: "trait-based contract discovery",
|
|
204
|
+
index: "decoded L2 events, transfers, contract calls, blocks, transactions",
|
|
205
|
+
streams: "bulk parquet dumps of the raw chain event firehose",
|
|
206
|
+
contracts: "trait-based contract discovery and ABIs",
|
|
180
207
|
subgraphs: "author/deploy/query custom indexes",
|
|
181
208
|
subscriptions: "webhook delivery on subgraph rows or raw chain events",
|
|
182
|
-
account: "identity
|
|
183
|
-
|
|
184
|
-
scaffold: "generate typed contract clients from a deployment or ABI"
|
|
209
|
+
account: "identity and self-provisioned API keys",
|
|
210
|
+
scaffold: "generate deploy-ready subgraph code from a deployed contract"
|
|
185
211
|
};
|
|
186
212
|
var PRODUCT_ORDER = [
|
|
187
|
-
"datasets",
|
|
188
213
|
"index",
|
|
189
214
|
"streams",
|
|
190
215
|
"contracts",
|
|
191
216
|
"subgraphs",
|
|
192
217
|
"subscriptions",
|
|
193
218
|
"account",
|
|
194
|
-
"project",
|
|
195
219
|
"scaffold"
|
|
196
220
|
];
|
|
197
221
|
function buildCapabilities() {
|
|
@@ -213,11 +237,17 @@ function buildCapabilities() {
|
|
|
213
237
|
});
|
|
214
238
|
return {
|
|
215
239
|
products,
|
|
216
|
-
discoverFirst: "Call
|
|
240
|
+
discoverFirst: "Call index_discover / contracts_find to learn what exists (and which filters each surface accepts) before querying."
|
|
217
241
|
};
|
|
218
242
|
}
|
|
243
|
+
var X402_CAPABILITY = {
|
|
244
|
+
what: "Index and Streams reads (/v1/index/*, /v1/streams/*) accept x402 v2 pay-per-call (network stacks:1, sponsored transfers — the payer holds sBTC/STX/USDCx, never gas). Index grants 1,000 free reads/day/IP before any 402; a paid Streams call opens a 500-call/1h PAYMENT-SESSION.",
|
|
245
|
+
paidWrites: "POST /v1/subgraphs (x402 $2) deploys a subgraph owned by the paying wallet — live from deploy, 7-day TTL, renewable via POST /v1/subgraphs/{name}/renew ($0.50/wk); claiming the account clears the expiry.",
|
|
246
|
+
prepaid: "POST /v1/x402/deposit?usd=N loads a prepaid tab (PAYMENT-BALANCE token, $0.25 min) — calls debit it instantly, no per-call on-chain settle. GET /v1/x402/balance reads it.",
|
|
247
|
+
discovery: "GET /v1/x402/supported on the API host for scheme, assets, prices, quotas, sessions, and prepaid metadata.",
|
|
248
|
+
autopay: "Set X402_PRIVATE_KEY (a Stacks private key holding an accepted token) and this MCP server pays 402 challenges automatically (sessions replayed). Add X402_TOPUP_USD (and optionally X402_TOPUP_WHEN_BELOW) for an autonomous prepaid tab that refills itself. Receipts are logged per paid call."
|
|
249
|
+
};
|
|
219
250
|
var READ_AUTH_TIERS = {
|
|
220
|
-
datasets: "open — no API key required",
|
|
221
251
|
index: "anonymous reads allowed; free-tier API keys are rejected (Build+ required)",
|
|
222
252
|
streams: "API key required (SL_API_KEY) — keyless calls return 401",
|
|
223
253
|
subgraphs: "public subgraphs anon-readable at /v1/subgraphs/<name>/<table> ({ rows, next_cursor, tip } cursor envelope); private subgraphs need the owning account's API key (anon → 404); writes require an API key"
|
|
@@ -227,7 +257,10 @@ async function buildContext(deps = { clientProvider: getClient }) {
|
|
|
227
257
|
const orNull = (v) => v == null ? unavailable : v;
|
|
228
258
|
const snap = await deps.clientProvider().context().catch(() => null);
|
|
229
259
|
return {
|
|
230
|
-
authState: {
|
|
260
|
+
authState: {
|
|
261
|
+
apiKeySet: Boolean(process.env.SL_API_KEY),
|
|
262
|
+
x402WalletSet: Boolean(process.env.X402_PRIVATE_KEY)
|
|
263
|
+
},
|
|
231
264
|
whatExists: {
|
|
232
265
|
account: orNull(snap?.account),
|
|
233
266
|
streamsTip: orNull(snap?.streamsTip),
|
|
@@ -239,7 +272,8 @@ async function buildContext(deps = { clientProvider: getClient }) {
|
|
|
239
272
|
activeOperations: orNull(snap?.activeOperations)
|
|
240
273
|
},
|
|
241
274
|
whatYouCanDo: buildCapabilities(),
|
|
242
|
-
readAuthTiers: READ_AUTH_TIERS
|
|
275
|
+
readAuthTiers: READ_AUTH_TIERS,
|
|
276
|
+
payPerCall: X402_CAPABILITY
|
|
243
277
|
};
|
|
244
278
|
}
|
|
245
279
|
function registerResources(server) {
|
|
@@ -288,29 +322,6 @@ function registerResources(server) {
|
|
|
288
322
|
}
|
|
289
323
|
]
|
|
290
324
|
}));
|
|
291
|
-
server.resource("streams-filters", "secondlayer://streams-filters", {
|
|
292
|
-
description: "Streams firehose vocabulary — the decoded event types and the filter fields accepted by streams_events / streams_consume."
|
|
293
|
-
}, async () => ({
|
|
294
|
-
contents: [
|
|
295
|
-
{
|
|
296
|
-
uri: "secondlayer://streams-filters",
|
|
297
|
-
mimeType: "application/json",
|
|
298
|
-
text: JSON.stringify({
|
|
299
|
-
eventTypes: [...DECODED_EVENT_TYPES],
|
|
300
|
-
filters: {
|
|
301
|
-
types: "include only these event types (array)",
|
|
302
|
-
notTypes: "exclude these event types (array)",
|
|
303
|
-
contractId: "match a contract id",
|
|
304
|
-
sender: "match the sender principal",
|
|
305
|
-
recipient: "match the recipient principal",
|
|
306
|
-
assetIdentifier: "match the asset identifier (contract::asset)",
|
|
307
|
-
fromHeight: "start block height (inclusive)",
|
|
308
|
-
toHeight: "end block height (inclusive)"
|
|
309
|
-
}
|
|
310
|
-
}, null, 2)
|
|
311
|
-
}
|
|
312
|
-
]
|
|
313
|
-
}));
|
|
314
325
|
server.resource("chain-triggers", "secondlayer://chain-triggers", {
|
|
315
326
|
description: "Chain-subscription trigger types and the filter fields each accepts (for subscriptions_create triggers)."
|
|
316
327
|
}, async () => ({
|
|
@@ -331,50 +342,13 @@ function registerAccountTools(server) {
|
|
|
331
342
|
const result = await apiRequest("GET", "/api/accounts/me");
|
|
332
343
|
return jsonResponse(result);
|
|
333
344
|
});
|
|
334
|
-
defineTool(server, "account_update", "Update the authenticated account's profile. Requires an API key.", {
|
|
335
|
-
displayName: z.string().optional().describe("Display name"),
|
|
336
|
-
bio: z.string().optional().describe("Profile bio"),
|
|
337
|
-
slug: z.string().optional().describe("Account URL slug")
|
|
338
|
-
}, async ({ displayName, bio, slug }) => {
|
|
339
|
-
const body = {};
|
|
340
|
-
if (displayName !== undefined)
|
|
341
|
-
body.display_name = displayName;
|
|
342
|
-
if (bio !== undefined)
|
|
343
|
-
body.bio = bio;
|
|
344
|
-
if (slug !== undefined)
|
|
345
|
-
body.slug = slug;
|
|
346
|
-
const result = await apiRequest("PATCH", "/api/accounts/me", body);
|
|
347
|
-
return jsonResponse(result);
|
|
348
|
-
});
|
|
349
|
-
defineTool(server, "account_billing", "Show the account's plan and subscription/billing status. Requires an API key. (Plan upgrade / Stripe portal / checkout are deliberately not agent tools — they are session-only human-payment flows; use account_set_caps to bound spend.)", {}, async () => {
|
|
350
|
-
const result = await apiRequest("GET", "/api/billing/status");
|
|
351
|
-
return jsonResponse(result);
|
|
352
|
-
});
|
|
353
345
|
defineTool(server, "account_create_key", "Mint a scoped streams/index read API key so the agent can self-provision access. Requires an account-level (owner) API key. The returned `key` is shown ONCE — forward it to the user to set as SL_API_KEY.", {
|
|
354
346
|
product: z.enum(["streams", "index"]).optional().describe("Key scope (default streams)"),
|
|
355
347
|
name: z.string().optional().describe("Optional label for the key")
|
|
356
348
|
}, async ({ product, name }) => jsonResponse(await getClient().apiKeys.create({ product, name })));
|
|
357
|
-
defineTool(server, "account_list_keys", "List the account's API keys (metadata only — prefix, name, status, product, tier, last used; never the plaintext). Requires an account-level (owner) API key.", {}, async () => jsonResponse(await getClient().apiKeys.list()));
|
|
358
|
-
defineTool(server, "account_revoke_key", "Revoke an API key by id. Requests using that key stop working immediately. Requires an account-level (owner) API key.", { id: z.string().describe("Key id from account_list_keys") }, async ({ id }) => jsonResponse(await getClient().apiKeys.revoke(id)));
|
|
359
|
-
defineTool(server, "account_usage", "Show the current billing period's usage snapshot: spend (with cap/projection), compute, storage, and per-project breakdown. Requires an API key.", {}, async () => jsonResponse(await apiRequest("GET", "/api/accounts/usage")));
|
|
360
|
-
defineTool(server, "account_get_caps", "Show the account's spend caps and alert threshold. Requires an account-level (owner) API key.", {}, async () => jsonResponse(await apiRequest("GET", "/api/billing/caps")));
|
|
361
|
-
defineTool(server, "account_set_caps", "Set the account's spend caps and alert threshold (no payment — just bounds spend). Requires an account-level (owner) API key.", {
|
|
362
|
-
monthlyCapCents: z.number().int().nonnegative().optional().describe("Total monthly spend cap in cents"),
|
|
363
|
-
computeCapCents: z.number().int().nonnegative().optional().describe("Compute spend cap in cents"),
|
|
364
|
-
storageCapCents: z.number().int().nonnegative().optional().describe("Storage spend cap in cents"),
|
|
365
|
-
alertThresholdPct: z.number().int().min(1).max(100).optional().describe("Percent of cap that triggers an alert (1-100)")
|
|
366
|
-
}, async (caps) => {
|
|
367
|
-
const body = {};
|
|
368
|
-
for (const [k, v] of Object.entries(caps)) {
|
|
369
|
-
if (v !== undefined)
|
|
370
|
-
body[k] = v;
|
|
371
|
-
}
|
|
372
|
-
return jsonResponse(await apiRequest("PATCH", "/api/billing/caps", body));
|
|
373
|
-
});
|
|
374
349
|
}
|
|
375
350
|
|
|
376
351
|
// src/tools/contracts.ts
|
|
377
|
-
import { generateContractInterface } from "@secondlayer/scaffold";
|
|
378
352
|
import { z as z2 } from "zod/v4";
|
|
379
353
|
function registerContractTools(server, clientProvider = getClient) {
|
|
380
354
|
defineTool(server, "contracts_find", 'Discover deployed Stacks contracts conforming to a trait (e.g. "sip-010", "sip-009", "sip-013"). The discovery endpoint for "which contracts implement X". Reads are public.', {
|
|
@@ -384,7 +358,7 @@ function registerContractTools(server, clientProvider = getClient) {
|
|
|
384
358
|
limit: z2.number().optional().describe("Page size, 1–500 (default 100)"),
|
|
385
359
|
cursor: z2.string().optional().describe("Opaque cursor from a prior response's next_cursor")
|
|
386
360
|
}, async (params) => jsonResponse(await clientProvider().contracts.list(params)));
|
|
387
|
-
defineTool(server, "get_contract_abi", "Fetch a single deployed contract's ABI from the registry (prod-safe). Returns the contract's metadata + full ABI (functions, maps, variables, fungible/non-fungible tokens). Returns not_found if the contract isn't in the registry.
|
|
361
|
+
defineTool(server, "get_contract_abi", "Fetch a single deployed contract's ABI from the registry (prod-safe). Returns the contract's metadata + full ABI (functions, maps, variables, fungible/non-fungible tokens). Returns not_found if the contract isn't in the registry. Use it to inspect a contract before scaffold_from_contract.", {
|
|
388
362
|
contractId: z2.string().describe("Fully qualified contract id (e.g. SP….amm-pool-v2-01)")
|
|
389
363
|
}, async ({ contractId }) => {
|
|
390
364
|
const contract = await clientProvider().contracts.get(contractId, {
|
|
@@ -398,72 +372,18 @@ function registerContractTools(server, clientProvider = getClient) {
|
|
|
398
372
|
}
|
|
399
373
|
}, true);
|
|
400
374
|
});
|
|
401
|
-
defineTool(server, "generate_contract_interface", "Generate a typed TypeScript contract-client interface (typed methods + map/var/constant readers) from a deployed contract's ABI (fetched from the registry). Returns not_found if the contract isn't in the registry.", {
|
|
402
|
-
contractId: z2.string().describe("Fully qualified contract id (e.g. SP….amm-pool-v2-01)")
|
|
403
|
-
}, async ({ contractId }) => {
|
|
404
|
-
const c = await clientProvider().contracts.get(contractId, {
|
|
405
|
-
includeAbi: true
|
|
406
|
-
});
|
|
407
|
-
if (!c || !c.abi) {
|
|
408
|
-
return jsonResponse({
|
|
409
|
-
error: {
|
|
410
|
-
type: "not_found",
|
|
411
|
-
status: 404,
|
|
412
|
-
message: `Contract not in registry: ${contractId}`
|
|
413
|
-
}
|
|
414
|
-
}, true);
|
|
415
|
-
}
|
|
416
|
-
const [address, contractName] = contractId.split(".");
|
|
417
|
-
const name = (contractName ?? contractId).replace(/[^a-zA-Z0-9]/g, "_");
|
|
418
|
-
const code = generateContractInterface([
|
|
419
|
-
{
|
|
420
|
-
name,
|
|
421
|
-
address,
|
|
422
|
-
contractName: contractName ?? "",
|
|
423
|
-
abi: c.abi
|
|
424
|
-
}
|
|
425
|
-
]);
|
|
426
|
-
return { content: [{ type: "text", text: code }] };
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// src/tools/datasets.ts
|
|
431
|
-
import { z as z3 } from "zod/v4";
|
|
432
|
-
function registerDatasetTools(server, clientProvider = getClient) {
|
|
433
|
-
defineTool(server, "datasets_list", "List the Foundation Datasets catalog with freshness — the discovery endpoint for what dataset slugs exist and how current each is. Reads are public.", {}, async () => {
|
|
434
|
-
const catalog = await clientProvider().datasets.listDatasets();
|
|
435
|
-
return jsonResponse(catalog);
|
|
436
|
-
});
|
|
437
|
-
defineTool(server, "datasets_query", 'Query ANY Foundation Dataset by slug — every family returned by datasets_list is queryable, including bespoke ones (bns/resolve with {"fqn":"alice.btc"}, bns/names, bns/namespaces, network-health/summary) and any dataset added later. Accepts family ("sbtc-events") or path ("sbtc/events") slug forms. Filters are passed through as documented query params (e.g. {"sender": "SP...", "from_block": "150000"}). Returns { rows, next_cursor, tip }; single-record datasets like bns/resolve return 0-or-1 rows (empty = not found). Call datasets_list first to discover slugs and their filters.', {
|
|
438
|
-
slug: z3.string().describe("Dataset slug from datasets_list (e.g. stx-transfers)"),
|
|
439
|
-
filters: z3.record(z3.string(), z3.string()).optional().describe("Documented per-dataset query params (snake_case values)"),
|
|
440
|
-
limit: z3.number().optional().describe("Max rows for this page"),
|
|
441
|
-
cursor: z3.string().optional().describe("Opaque cursor from a prior response's next_cursor")
|
|
442
|
-
}, async ({ slug, filters, limit, cursor }) => {
|
|
443
|
-
const params = { ...filters ?? {} };
|
|
444
|
-
if (limit !== undefined)
|
|
445
|
-
params.limit = limit;
|
|
446
|
-
if (cursor !== undefined)
|
|
447
|
-
params.cursor = cursor;
|
|
448
|
-
const result = await clientProvider().datasets.get(slug, params);
|
|
449
|
-
return jsonResponse(result);
|
|
450
|
-
});
|
|
451
375
|
}
|
|
452
376
|
|
|
453
377
|
// src/tools/index.ts
|
|
454
|
-
import { DECODED_EVENT_TYPES
|
|
455
|
-
import {
|
|
456
|
-
|
|
457
|
-
generateIndexSchema
|
|
458
|
-
} from "@secondlayer/subgraphs";
|
|
459
|
-
import { z as z4 } from "zod/v4";
|
|
460
|
-
var INDEX_EVENT_TYPES = DECODED_EVENT_TYPES2;
|
|
378
|
+
import { DECODED_EVENT_TYPES } from "@secondlayer/shared";
|
|
379
|
+
import { z as z3 } from "zod/v4";
|
|
380
|
+
var INDEX_EVENT_TYPES = DECODED_EVENT_TYPES;
|
|
461
381
|
var rangeFilters = {
|
|
462
|
-
contractId:
|
|
463
|
-
fromHeight:
|
|
464
|
-
toHeight:
|
|
465
|
-
cursor:
|
|
466
|
-
limit:
|
|
382
|
+
contractId: z3.string().optional().describe("Filter by contract id"),
|
|
383
|
+
fromHeight: z3.number().optional().describe("Start block height (inclusive)"),
|
|
384
|
+
toHeight: z3.number().optional().describe("End block height (inclusive)"),
|
|
385
|
+
cursor: z3.string().optional().describe("Opaque cursor from a prior response's next_cursor"),
|
|
386
|
+
limit: z3.number().optional().describe("Max rows for this page")
|
|
467
387
|
};
|
|
468
388
|
var heightFilters = {
|
|
469
389
|
fromHeight: rangeFilters.fromHeight,
|
|
@@ -471,126 +391,52 @@ var heightFilters = {
|
|
|
471
391
|
cursor: rangeFilters.cursor,
|
|
472
392
|
limit: rangeFilters.limit
|
|
473
393
|
};
|
|
474
|
-
var notFound = (message) => jsonResponse({ error: { type: "not_found", status: 404, message } }, true);
|
|
475
394
|
function registerIndexTools(server, clientProvider = getClient) {
|
|
476
395
|
defineTool(server, "index_ft_transfers", "List decoded SIP-010 fungible-token transfers from the Index (L2 decoded layer). Anonymous reads allowed (free-tier API keys are rejected — Build+ required).", {
|
|
477
396
|
...rangeFilters,
|
|
478
|
-
sender:
|
|
479
|
-
recipient:
|
|
397
|
+
sender: z3.string().optional().describe("Filter by sender principal"),
|
|
398
|
+
recipient: z3.string().optional().describe("Filter by recipient principal")
|
|
480
399
|
}, async (params) => jsonResponse(await clientProvider().index.ftTransfers.list(params)));
|
|
481
400
|
defineTool(server, "index_nft_transfers", "List decoded SIP-009 non-fungible-token transfers from the Index. Anonymous reads allowed (free-tier keys rejected).", {
|
|
482
401
|
...rangeFilters,
|
|
483
|
-
sender:
|
|
484
|
-
recipient:
|
|
485
|
-
assetIdentifier:
|
|
402
|
+
sender: z3.string().optional().describe("Filter by sender principal"),
|
|
403
|
+
recipient: z3.string().optional().describe("Filter by recipient principal"),
|
|
404
|
+
assetIdentifier: z3.string().optional().describe("Filter by asset identifier (contract::asset)")
|
|
486
405
|
}, async (params) => jsonResponse(await clientProvider().index.nftTransfers.list(params)));
|
|
487
406
|
defineTool(server, "index_events", "List decoded chain events from the Index by event type. Use this for event types without a dedicated tool (stx_*, ft_mint/burn, nft_mint/burn, print), and for trait-scoped queries: set `trait` (e.g. sip-010) to match all contracts conforming to a standard — pair with contracts_find to discover traits. For ft/nft transfers without a trait prefer index_ft_transfers / index_nft_transfers.", {
|
|
488
|
-
eventType:
|
|
407
|
+
eventType: z3.enum(INDEX_EVENT_TYPES).describe("Required. Decoded event type to list."),
|
|
489
408
|
...rangeFilters,
|
|
490
|
-
sender:
|
|
491
|
-
recipient:
|
|
492
|
-
assetIdentifier:
|
|
493
|
-
trait:
|
|
409
|
+
sender: z3.string().optional().describe("Filter by sender principal"),
|
|
410
|
+
recipient: z3.string().optional().describe("Filter by recipient principal"),
|
|
411
|
+
assetIdentifier: z3.string().optional().describe("Filter by asset identifier where applicable"),
|
|
412
|
+
trait: z3.string().optional().describe("Match contracts conforming to a trait/standard (e.g. sip-010). Mutually exclusive with contractId; contract-keyed event types only.")
|
|
494
413
|
}, async (params) => jsonResponse(await clientProvider().index.events.list(params)));
|
|
495
414
|
defineTool(server, "index_contract_calls", "List decoded contract calls from the Index (function name, args, result). Set `trait` (e.g. sip-010) to match calls to all contracts conforming to a standard. Note: contract-call cursors are a SEPARATE keyspace from event cursors — they are not interchangeable.", {
|
|
496
415
|
...rangeFilters,
|
|
497
|
-
functionName:
|
|
498
|
-
sender:
|
|
499
|
-
trait:
|
|
416
|
+
functionName: z3.string().optional().describe("Filter by called function name"),
|
|
417
|
+
sender: z3.string().optional().describe("Filter by caller principal"),
|
|
418
|
+
trait: z3.string().optional().describe("Match contracts conforming to a trait/standard (e.g. sip-010). Mutually exclusive with contractId.")
|
|
500
419
|
}, async (params) => jsonResponse(await clientProvider().index.contractCalls.list(params)));
|
|
501
|
-
defineTool(server, "index_canonical", "List the canonical Stacks block sequence from the Index (height + hash). Anonymous reads allowed (free-tier keys rejected).", { ...heightFilters }, async (params) => jsonResponse(await clientProvider().index.canonical.list(params)));
|
|
502
420
|
defineTool(server, "index_blocks", "List decoded blocks from the Index. Anonymous reads allowed (free-tier keys rejected).", { ...heightFilters }, async (params) => jsonResponse(await clientProvider().index.blocks.list(params)));
|
|
503
|
-
defineTool(server, "index_block", "Get a single block from the Index by height or block hash. Returns not_found if unknown.", {
|
|
504
|
-
ref: z4.string().describe("Block height (digits) or block hash (0x… string)")
|
|
505
|
-
}, async ({ ref }) => {
|
|
506
|
-
const block = await clientProvider().index.blocks.get(/^\d+$/.test(ref) ? Number(ref) : ref);
|
|
507
|
-
return block ? jsonResponse(block) : notFound(`No block for ref ${ref}`);
|
|
508
|
-
});
|
|
509
421
|
defineTool(server, "index_transactions", "List decoded transactions from the Index. Filter by type, sender, or contract. Anonymous reads allowed (free-tier keys rejected).", {
|
|
510
422
|
...rangeFilters,
|
|
511
|
-
type:
|
|
512
|
-
sender:
|
|
423
|
+
type: z3.string().optional().describe("Filter by transaction type"),
|
|
424
|
+
sender: z3.string().optional().describe("Filter by sender principal")
|
|
513
425
|
}, async (params) => jsonResponse(await clientProvider().index.transactions.list(params)));
|
|
514
|
-
defineTool(server, "index_transaction", "Get a single transaction from the Index by tx_id. Returns not_found if unknown.", { txId: z4.string().describe("Transaction id (0x… hash)") }, async ({ txId }) => {
|
|
515
|
-
const tx = await clientProvider().index.transactions.get(txId);
|
|
516
|
-
return tx ? jsonResponse(tx) : notFound(`No transaction for ${txId}`);
|
|
517
|
-
});
|
|
518
|
-
defineTool(server, "index_transaction_proof", "Fetch a transaction's inclusion proof (raw tx + signed Nakamoto block header + merkle path) so it can be verified trustlessly client-side with the SDK's verifyTransactionProof. Returns not_found if the tx/block is unknown; a 503 means the proof can't be assembled on this deployment right now (PROOF_TX_SET_INCOMPLETE, or PROOF_NODE_UNAVAILABLE when the stacks-node header source isn't configured).", { txId: z4.string().describe("Transaction id (0x… hash)") }, async ({ txId }) => {
|
|
519
|
-
const proof = await clientProvider().index.transactions.getProof(txId);
|
|
520
|
-
return proof ? jsonResponse(proof) : notFound(`No proof available for ${txId}`);
|
|
521
|
-
});
|
|
522
|
-
defineTool(server, "index_stacking", "List decoded PoX-4 stacking actions from the Index (stack-stx, delegate-stx, etc.). Anonymous reads allowed (free-tier keys rejected).", {
|
|
523
|
-
...heightFilters,
|
|
524
|
-
functionName: z4.string().optional().describe("Filter by PoX function name"),
|
|
525
|
-
stacker: z4.string().optional().describe("Filter by stacker principal"),
|
|
526
|
-
caller: z4.string().optional().describe("Filter by caller principal")
|
|
527
|
-
}, async (params) => jsonResponse(await clientProvider().index.stacking.list(params)));
|
|
528
|
-
defineTool(server, "index_mempool", "List pending (unconfirmed) transactions from the Index mempool. Sequence-cursor paginated (no height range). Anonymous reads allowed (free-tier keys rejected).", {
|
|
529
|
-
sender: z4.string().optional().describe("Filter by sender principal"),
|
|
530
|
-
type: z4.string().optional().describe("Filter by transaction type"),
|
|
531
|
-
contractId: z4.string().optional().describe("Filter to pending calls to a single contract"),
|
|
532
|
-
cursor: z4.string().optional().describe("Opaque cursor from a prior response's next_cursor"),
|
|
533
|
-
limit: z4.number().optional().describe("Max rows for this page")
|
|
534
|
-
}, async (params) => jsonResponse(await clientProvider().index.mempool.list(params)));
|
|
535
|
-
defineTool(server, "index_mempool_tx", "Get a single pending transaction from the Index mempool by tx_id. Returns not_found once it is mined or dropped.", { txId: z4.string().describe("Transaction id (0x… hash)") }, async ({ txId }) => {
|
|
536
|
-
const tx = await clientProvider().index.mempool.get(txId);
|
|
537
|
-
return tx ? jsonResponse(tx) : notFound(`No pending tx for ${txId}`);
|
|
538
|
-
});
|
|
539
426
|
defineTool(server, "index_discover", "Discover the Index vocabulary: every event type and its columns, allowed/equality filters, and required-non-null fields (and which types accept `trait`). Read this before building Index queries instead of guessing filters. Anonymous read.", {}, async () => jsonResponse(await clientProvider().index.discover()));
|
|
540
|
-
defineTool(server, "
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}, async (
|
|
546
|
-
const out = generateIndexSchema(target, { tables, schemaName });
|
|
547
|
-
return { content: [{ type: "text", text: out }] };
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
// src/tools/projects.ts
|
|
552
|
-
import { z as z5 } from "zod/v4";
|
|
553
|
-
function registerProjectTools(server, clientProvider = getClient) {
|
|
554
|
-
defineTool(server, "project_list", "List the account's projects. Requires an account-level (owner) API key.", {}, async () => jsonResponse(await clientProvider().projects.list()));
|
|
555
|
-
defineTool(server, "project_get", "Get a single project by slug.", { slug: z5.string().describe("Project slug") }, async ({ slug }) => jsonResponse(await clientProvider().projects.get(slug)));
|
|
556
|
-
defineTool(server, "project_create", "Create a project. The slug is derived from the name when omitted. Requires an account-level (owner) API key.", {
|
|
557
|
-
name: z5.string().describe("Project name"),
|
|
558
|
-
slug: z5.string().optional().describe("URL slug (auto-derived from name if omitted)"),
|
|
559
|
-
network: z5.string().optional().describe("Network (e.g. mainnet, testnet)"),
|
|
560
|
-
nodeRpc: z5.string().optional().describe("Custom node RPC URL")
|
|
561
|
-
}, async ({ name, slug, network, nodeRpc }) => jsonResponse(await clientProvider().projects.create({
|
|
562
|
-
name,
|
|
563
|
-
slug,
|
|
564
|
-
network,
|
|
565
|
-
nodeRpc
|
|
566
|
-
})));
|
|
567
|
-
defineTool(server, "project_update", "Update a project. `slug` selects the project; `newSlug` renames it. Requires an account-level (owner) API key.", {
|
|
568
|
-
slug: z5.string().describe("Slug of the project to update"),
|
|
569
|
-
newSlug: z5.string().optional().describe("New slug (renames the project)"),
|
|
570
|
-
name: z5.string().optional().describe("New display name"),
|
|
571
|
-
network: z5.string().optional().describe("Network (e.g. mainnet, testnet)"),
|
|
572
|
-
nodeRpc: z5.string().optional().describe("Custom node RPC URL"),
|
|
573
|
-
settings: z5.record(z5.string(), z5.unknown()).optional().describe("Arbitrary project settings to merge")
|
|
574
|
-
}, async ({ slug, newSlug, name, network, nodeRpc, settings }) => jsonResponse(await clientProvider().projects.update(slug, {
|
|
575
|
-
...newSlug !== undefined ? { slug: newSlug } : {},
|
|
576
|
-
...name !== undefined ? { name } : {},
|
|
577
|
-
...network !== undefined ? { network } : {},
|
|
578
|
-
...nodeRpc !== undefined ? { nodeRpc } : {},
|
|
579
|
-
...settings !== undefined ? { settings } : {}
|
|
580
|
-
})));
|
|
581
|
-
defineTool(server, "project_delete", "Delete a project permanently. The account's last remaining project cannot be deleted.", { slug: z5.string().describe("Project slug") }, async ({ slug }) => jsonResponse(await clientProvider().projects.delete(slug)));
|
|
582
|
-
defineTool(server, "project_team_list", "List a project's team members and pending invitations.", { slug: z5.string().describe("Project slug") }, async ({ slug }) => jsonResponse(await clientProvider().projects.team(slug)));
|
|
427
|
+
defineTool(server, "batch_query", "Run up to 10 public /v1 reads in one round trip (POST /v1/batch). Each item keeps its own auth/quota/pay-per-call semantics; results return in order with per-item status. Paths must start with /v1/index, /v1/subgraphs, /v1/streams, or /v1/contracts.", {
|
|
428
|
+
requests: z3.array(z3.object({
|
|
429
|
+
path: z3.string().describe("Public /v1 read path"),
|
|
430
|
+
params: z3.record(z3.string(), z3.string()).optional()
|
|
431
|
+
})).min(1).max(10).describe("Read descriptors, executed concurrently")
|
|
432
|
+
}, async (params) => jsonResponse(await clientProvider().batch(params.requests)));
|
|
583
433
|
}
|
|
584
434
|
|
|
585
435
|
// src/tools/scaffold.ts
|
|
586
|
-
import {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
import { TRAIT_STANDARDS as TRAIT_STANDARDS2 } from "@secondlayer/stacks/clarity";
|
|
591
|
-
import { z as z6 } from "zod/v4";
|
|
592
|
-
async function fetchAbi(contractId) {
|
|
593
|
-
const contract = await getClient().contracts.get(contractId, {
|
|
436
|
+
import { generateSubgraphCode } from "@secondlayer/scaffold";
|
|
437
|
+
import { z as z4 } from "zod/v4";
|
|
438
|
+
async function fetchAbi(clientProvider, contractId) {
|
|
439
|
+
const contract = await clientProvider().contracts.get(contractId, {
|
|
594
440
|
includeAbi: true
|
|
595
441
|
});
|
|
596
442
|
if (!contract)
|
|
@@ -601,134 +447,26 @@ async function fetchAbi(contractId) {
|
|
|
601
447
|
}
|
|
602
448
|
return { functions: abi.functions ?? [], maps: abi.maps ?? [] };
|
|
603
449
|
}
|
|
604
|
-
function registerScaffoldTools(server) {
|
|
605
|
-
defineTool(server, "scaffold_from_trait", "Generate a deploy-ready subgraph scaffold that indexes EVERY contract conforming to a SIP trait (no specific contract needed) — sip-009 → an nft_transfer source, sip-010/sip-013 → ft_transfer. Use scaffold_from_contract/abi instead when targeting one contract. See the secondlayer://traits resource for valid trait ids.", {
|
|
606
|
-
trait: z6.enum(TRAIT_STANDARDS2).describe("SIP standard to index (sip-009 | sip-010 | sip-013)"),
|
|
607
|
-
subgraphName: z6.string().optional().describe("Override the subgraph name (defaults to <trait>-transfers)")
|
|
608
|
-
}, async ({ trait, subgraphName }) => {
|
|
609
|
-
const code = generateTraitSubgraph({ trait, name: subgraphName });
|
|
610
|
-
return { content: [{ type: "text", text: code }] };
|
|
611
|
-
});
|
|
450
|
+
function registerScaffoldTools(server, clientProvider = getClient) {
|
|
612
451
|
defineTool(server, "scaffold_from_contract", "Generate a subgraph scaffold from a deployed Stacks contract. Fetches the ABI automatically.", {
|
|
613
|
-
contractId:
|
|
614
|
-
subgraphName:
|
|
452
|
+
contractId: z4.string().describe("Fully qualified contract ID (e.g. SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01)"),
|
|
453
|
+
subgraphName: z4.string().optional().describe("Override the subgraph name (defaults to contract name)")
|
|
615
454
|
}, async ({ contractId, subgraphName }) => {
|
|
616
|
-
const { functions, maps } = await fetchAbi(contractId);
|
|
455
|
+
const { functions, maps } = await fetchAbi(clientProvider, contractId);
|
|
617
456
|
const code = generateSubgraphCode(contractId, functions, subgraphName, maps);
|
|
618
457
|
return { content: [{ type: "text", text: code }] };
|
|
619
458
|
});
|
|
620
|
-
defineTool(server, "scaffold_from_abi", "Generate a subgraph scaffold from a provided ABI JSON. Use when you already have the ABI.", {
|
|
621
|
-
abi: z6.string().describe("ABI JSON string (the full contract ABI object)"),
|
|
622
|
-
contractId: z6.string().describe("Fully qualified contract ID"),
|
|
623
|
-
subgraphName: z6.string().optional().describe("Override the subgraph name")
|
|
624
|
-
}, async ({ abi, contractId, subgraphName }) => {
|
|
625
|
-
let parsed;
|
|
626
|
-
try {
|
|
627
|
-
parsed = JSON.parse(abi);
|
|
628
|
-
} catch {
|
|
629
|
-
return {
|
|
630
|
-
content: [{ type: "text", text: "Invalid ABI JSON" }],
|
|
631
|
-
isError: true
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
const code = generateSubgraphCode(contractId, parsed.functions ?? [], subgraphName, parsed.maps ?? []);
|
|
635
|
-
return { content: [{ type: "text", text: code }] };
|
|
636
|
-
});
|
|
637
459
|
}
|
|
638
460
|
|
|
639
461
|
// src/tools/streams.ts
|
|
640
|
-
import { AuthError } from "@secondlayer/sdk";
|
|
641
|
-
import { DECODED_EVENT_TYPES as DECODED_EVENT_TYPES3 } from "@secondlayer/shared";
|
|
642
|
-
import { z as z7 } from "zod/v4";
|
|
643
|
-
var STREAMS_EVENT_TYPES = DECODED_EVENT_TYPES3;
|
|
644
|
-
async function withStreamsAuthHint(fn) {
|
|
645
|
-
try {
|
|
646
|
-
return await fn();
|
|
647
|
-
} catch (err) {
|
|
648
|
-
if (err instanceof AuthError) {
|
|
649
|
-
throw Object.assign(new Error(err.message + keyHint), { status: 401 });
|
|
650
|
-
}
|
|
651
|
-
throw err;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
462
|
function registerStreamsTools(server, clientProvider = getClient) {
|
|
655
|
-
defineTool(server, "
|
|
656
|
-
defineTool(server, "streams_events", "List raw chain events from the Streams firehose. Streams requires an API key (SL_API_KEY). Filter by event types, principals, contract, asset, or block range; page with cursor.", {
|
|
657
|
-
types: z7.array(z7.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to include"),
|
|
658
|
-
notTypes: z7.array(z7.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to exclude (applied after types)"),
|
|
659
|
-
contractId: z7.string().optional().describe("Filter by contract id"),
|
|
660
|
-
sender: z7.string().optional().describe("Filter by sender principal"),
|
|
661
|
-
recipient: z7.string().optional().describe("Filter by recipient principal"),
|
|
662
|
-
assetIdentifier: z7.string().optional().describe("Filter by asset identifier"),
|
|
663
|
-
fromHeight: z7.number().optional().describe("Start block height (inclusive)"),
|
|
664
|
-
toHeight: z7.number().optional().describe("End block height (inclusive)"),
|
|
665
|
-
cursor: z7.string().optional().describe("Opaque cursor from a prior response"),
|
|
666
|
-
limit: z7.number().optional().describe("Max events for this page")
|
|
667
|
-
}, async (params) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.events.list(params))));
|
|
668
|
-
defineTool(server, "streams_consume", "Consume a bounded, reorg-aware run of Streams events from a cursor. Walks up to maxPages pages and returns the events, any reorgs observed, and a resume `cursor` to pass back as fromCursor next call — the agent-native consume/resume primitive. Streams requires an API key (SL_API_KEY).", {
|
|
669
|
-
fromCursor: z7.string().optional().describe("Resume cursor from a prior consume (omit to start at genesis)"),
|
|
670
|
-
types: z7.array(z7.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to include"),
|
|
671
|
-
notTypes: z7.array(z7.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to exclude"),
|
|
672
|
-
contractId: z7.string().optional().describe("Filter by contract id"),
|
|
673
|
-
sender: z7.string().optional().describe("Filter by sender principal"),
|
|
674
|
-
recipient: z7.string().optional().describe("Filter by recipient principal"),
|
|
675
|
-
assetIdentifier: z7.string().optional().describe("Filter by asset identifier"),
|
|
676
|
-
batchSize: z7.number().optional().describe("Events per page (1–1000, default 100)"),
|
|
677
|
-
maxPages: z7.number().optional().describe("Max pages to walk this call (1–20, default 5)"),
|
|
678
|
-
finalizedOnly: z7.boolean().optional().describe("Only emit finalized events; skip reorg handling")
|
|
679
|
-
}, async (params) => withStreamsAuthHint(async () => {
|
|
680
|
-
const maxPages = Math.min(Math.max(params.maxPages ?? 5, 1), 20);
|
|
681
|
-
const batchSize = Math.min(Math.max(params.batchSize ?? 100, 1), 1000);
|
|
682
|
-
const events = [];
|
|
683
|
-
const reorgs = [];
|
|
684
|
-
const result = await clientProvider().streams.events.consume({
|
|
685
|
-
fromCursor: params.fromCursor ?? null,
|
|
686
|
-
mode: "bounded",
|
|
687
|
-
finalizedOnly: params.finalizedOnly,
|
|
688
|
-
types: params.types,
|
|
689
|
-
notTypes: params.notTypes,
|
|
690
|
-
contractId: params.contractId,
|
|
691
|
-
sender: params.sender,
|
|
692
|
-
recipient: params.recipient,
|
|
693
|
-
assetIdentifier: params.assetIdentifier,
|
|
694
|
-
batchSize,
|
|
695
|
-
maxPages,
|
|
696
|
-
onBatch: (batch) => {
|
|
697
|
-
events.push(...batch);
|
|
698
|
-
},
|
|
699
|
-
onReorg: (reorg) => {
|
|
700
|
-
reorgs.push(reorg);
|
|
701
|
-
}
|
|
702
|
-
});
|
|
703
|
-
return jsonResponse({
|
|
704
|
-
events,
|
|
705
|
-
reorgs,
|
|
706
|
-
cursor: result.cursor,
|
|
707
|
-
pages: result.pages
|
|
708
|
-
});
|
|
709
|
-
}));
|
|
710
|
-
defineTool(server, "streams_event_by_txid", "List all Streams events emitted by a single transaction. Streams requires an API key (SL_API_KEY).", { txId: z7.string().describe("Transaction id (0x… hash)") }, async ({ txId }) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.events.byTxId(txId))));
|
|
711
|
-
defineTool(server, "streams_block_events", "List all Streams events in a single block, by height or block hash. Streams requires an API key (SL_API_KEY).", {
|
|
712
|
-
heightOrHash: z7.string().describe("Block height (digits) or block hash (0x… string)")
|
|
713
|
-
}, async ({ heightOrHash }) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.blocks.events(/^\d+$/.test(heightOrHash) ? Number(heightOrHash) : heightOrHash))));
|
|
714
|
-
defineTool(server, "streams_reorgs", "List chain reorgs observed by Streams since a cursor. Streams requires an API key (SL_API_KEY).", {
|
|
715
|
-
since: z7.string().describe("Cursor to list reorgs since (block:index or ISO timestamp)"),
|
|
716
|
-
limit: z7.number().optional().describe("Max reorgs to return")
|
|
717
|
-
}, async (params) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.reorgs.list(params))));
|
|
718
|
-
defineTool(server, "streams_canonical", "Get the canonical block at a given height from Streams (height + hashes + is_canonical). Streams requires an API key (SL_API_KEY).", { height: z7.number().describe("Block height") }, async ({ height }) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.canonical(height))));
|
|
719
|
-
defineTool(server, "streams_usage", "Your own Streams consumption (events today + this month) and tier limits (rate limit, retention). Streams requires an API key (SL_API_KEY).", {}, async () => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.usage())));
|
|
720
|
-
defineTool(server, "streams_dumps", "List the Streams bulk parquet dumps manifest — coverage range, latest_finalized_cursor, and per-file metadata (block range, row count, size, sha256, signed URL). This is the cold backfill path for downloading all raw data; fetch the file URLs directly (e.g. with DuckDB). Requires the dumps base URL to be configured (SL_STREAMS_DUMPS_URL).", {}, async () => jsonResponse(await clientProvider().streams.dumps.list()));
|
|
463
|
+
defineTool(server, "streams_dumps", "List the Streams bulk parquet dumps manifest — coverage range, latest_finalized_cursor, and per-file metadata (block range, row count, size, sha256, signed URL). This is the cold backfill path for downloading all raw data; fetch the file URLs directly (e.g. with DuckDB). Requires the dumps base URL to be configured (SL_STREAMS_DUMPS_URL). Live Streams reads (tip, events, consume) are REST-only: see /v1/streams/* in the OpenAPI spec.", {}, async () => jsonResponse(await clientProvider().streams.dumps.list()));
|
|
721
464
|
}
|
|
722
465
|
|
|
723
466
|
// src/tools/subgraphs.ts
|
|
724
467
|
import { bundleSubgraphCode } from "@secondlayer/bundler";
|
|
725
468
|
import { ByoBreakingChangeError } from "@secondlayer/sdk";
|
|
726
|
-
import {
|
|
727
|
-
generateDrizzleSchema,
|
|
728
|
-
generateKyselySchema,
|
|
729
|
-
generatePrismaSchema
|
|
730
|
-
} from "@secondlayer/subgraphs";
|
|
731
|
-
import { z as z8 } from "zod/v4";
|
|
469
|
+
import { z as z5 } from "zod/v4";
|
|
732
470
|
function registerSubgraphTools(server, clientProvider = getClient) {
|
|
733
471
|
defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only, including visibility — public subgraphs are anon-readable at /v1/subgraphs/<name>.", {}, async () => {
|
|
734
472
|
const { data } = await clientProvider().subgraphs.list();
|
|
@@ -741,38 +479,22 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
741
479
|
]
|
|
742
480
|
};
|
|
743
481
|
});
|
|
744
|
-
defineTool(server, "subgraphs_get", "Get full details of a subgraph including schema, health, and table columns.", { name:
|
|
482
|
+
defineTool(server, "subgraphs_get", "Get full details of a subgraph including schema, health, and table columns.", { name: z5.string().describe("Subgraph name") }, async ({ name }) => {
|
|
745
483
|
const detail = await clientProvider().subgraphs.get(name);
|
|
746
484
|
return {
|
|
747
485
|
content: [{ type: "text", text: JSON.stringify(detail, null, 2) }]
|
|
748
486
|
};
|
|
749
487
|
});
|
|
750
|
-
defineTool(server, "subgraphs_spec", "Get generated API documentation for a subgraph. Defaults to compact agent schema; supports OpenAPI JSON and Markdown.", {
|
|
751
|
-
name: z8.string().describe("Subgraph name"),
|
|
752
|
-
format: z8.enum(["agent", "openapi", "markdown"]).optional().describe("Spec format to return. Defaults to agent."),
|
|
753
|
-
serverUrl: z8.string().optional().describe("Override the server URL embedded in generated docs.")
|
|
754
|
-
}, async ({ name, format = "agent", serverUrl }) => {
|
|
755
|
-
const options = serverUrl ? { serverUrl } : undefined;
|
|
756
|
-
const spec = format === "openapi" ? await clientProvider().subgraphs.openapi(name, options) : format === "markdown" ? await clientProvider().subgraphs.markdown(name, options) : await clientProvider().subgraphs.schema(name, options);
|
|
757
|
-
return {
|
|
758
|
-
content: [
|
|
759
|
-
{
|
|
760
|
-
type: "text",
|
|
761
|
-
text: typeof spec === "string" ? spec : JSON.stringify(spec, null, 2)
|
|
762
|
-
}
|
|
763
|
-
]
|
|
764
|
-
};
|
|
765
|
-
});
|
|
766
488
|
defineTool(server, "subgraphs_query", 'Query rows from a subgraph table (max 200 rows). Filters support operators: "amount.gte": "1000", "sender.neq": "SP...", "name.like": "%token%". Available operators: eq, neq, gt, gte, lt, lte, like. To TAIL new rows (no streaming over MCP): sort=_id, order=desc for the latest, then poll forward with the filter {"_id.gt": "<last _id seen>"}, order=asc. Fetch one row by id with {"_id": "<id>"}. Public subgraphs are also keyless over HTTP at GET /v1/subgraphs/<name>/<table> — { rows, next_cursor, tip } envelope, resume with ?cursor=<next_cursor> + _order=asc|desc (no _offset/_sort on /v1); hand that URL to third parties.', {
|
|
767
|
-
name:
|
|
768
|
-
table:
|
|
769
|
-
filters:
|
|
770
|
-
sort:
|
|
771
|
-
order:
|
|
772
|
-
limit:
|
|
773
|
-
offset:
|
|
774
|
-
fields:
|
|
775
|
-
count:
|
|
489
|
+
name: z5.string().describe("Subgraph name"),
|
|
490
|
+
table: z5.string().describe("Table name"),
|
|
491
|
+
filters: z5.record(z5.string(), z5.string()).optional().describe('Column filters — plain values or with operators (e.g. {"amount.gte": "1000", "sender": "SP..."})'),
|
|
492
|
+
sort: z5.string().optional().describe("Column to sort by"),
|
|
493
|
+
order: z5.enum(["asc", "desc"]).optional().describe("Sort order"),
|
|
494
|
+
limit: z5.number().max(200).optional().describe("Max rows (default 50, max 200)"),
|
|
495
|
+
offset: z5.number().optional().describe("Offset for pagination"),
|
|
496
|
+
fields: z5.string().optional().describe('Comma-separated column list to return (e.g. "sender,amount")'),
|
|
497
|
+
count: z5.boolean().optional().describe("If true, return row count instead of rows")
|
|
776
498
|
}, async ({
|
|
777
499
|
name,
|
|
778
500
|
table,
|
|
@@ -804,25 +526,10 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
804
526
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
805
527
|
};
|
|
806
528
|
});
|
|
807
|
-
defineTool(server, "
|
|
808
|
-
name:
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
count: z8.boolean().optional().describe("Include COUNT(*) of matching rows"),
|
|
812
|
-
countDistinct: z8.array(z8.string()).optional().describe("Columns to count distinct values of"),
|
|
813
|
-
sum: z8.array(z8.string()).optional().describe("Numeric columns to sum (lossless string result)"),
|
|
814
|
-
min: z8.array(z8.string()).optional().describe("Numeric columns to take the minimum of"),
|
|
815
|
-
max: z8.array(z8.string()).optional().describe("Numeric columns to take the maximum of")
|
|
816
|
-
}, async ({ name, table, filters, count, countDistinct, sum, min, max }) => {
|
|
817
|
-
const result = await clientProvider().subgraphs.queryTableAggregate(name, table, { filters, count, countDistinct, sum, min, max });
|
|
818
|
-
return {
|
|
819
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
820
|
-
};
|
|
821
|
-
});
|
|
822
|
-
defineTool(server, "subgraphs_reindex", "Reindex a subgraph from a specific block range. Returns an operationId — poll subgraphs_operation to track progress to completion.", {
|
|
823
|
-
name: z8.string().describe("Subgraph name"),
|
|
824
|
-
fromBlock: z8.number().optional().describe("Start block (defaults to beginning)"),
|
|
825
|
-
toBlock: z8.number().optional().describe("End block (defaults to latest)")
|
|
529
|
+
defineTool(server, "subgraphs_reindex", "Reindex a subgraph from a specific block range. Returns an operationId — check subgraphs_get (health) or the REST operations endpoint to track progress to completion.", {
|
|
530
|
+
name: z5.string().describe("Subgraph name"),
|
|
531
|
+
fromBlock: z5.number().optional().describe("Start block (defaults to beginning)"),
|
|
532
|
+
toBlock: z5.number().optional().describe("End block (defaults to latest)")
|
|
826
533
|
}, async ({ name, fromBlock, toBlock }) => {
|
|
827
534
|
const result = await clientProvider().subgraphs.reindex(name, {
|
|
828
535
|
fromBlock,
|
|
@@ -832,19 +539,10 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
832
539
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
833
540
|
};
|
|
834
541
|
});
|
|
835
|
-
defineTool(server, "
|
|
836
|
-
name:
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
const result = operationId ? await clientProvider().subgraphs.getOperation(name, operationId) : await clientProvider().subgraphs.operations(name);
|
|
840
|
-
return {
|
|
841
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
842
|
-
};
|
|
843
|
-
});
|
|
844
|
-
defineTool(server, "subgraphs_backfill", "Backfill a subgraph over a block range. Non-destructive forward fill (does not drop existing data) — unlike subgraphs_reindex, and the only data-fill path for BYO subgraphs (reindex is blocked there). Both blocks required. Returns an operationId — poll subgraphs_operation to track progress.", {
|
|
845
|
-
name: z8.string().describe("Subgraph name"),
|
|
846
|
-
fromBlock: z8.number().int().nonnegative().describe("Start block (inclusive)"),
|
|
847
|
-
toBlock: z8.number().int().nonnegative().describe("End block (inclusive)")
|
|
542
|
+
defineTool(server, "subgraphs_backfill", "Backfill a subgraph over a block range. Non-destructive forward fill (does not drop existing data) — unlike subgraphs_reindex, and the only data-fill path for BYO subgraphs (reindex is blocked there). Both blocks required. Returns an operationId — check subgraphs_get (health) or the REST operations endpoint to track progress.", {
|
|
543
|
+
name: z5.string().describe("Subgraph name"),
|
|
544
|
+
fromBlock: z5.number().int().nonnegative().describe("Start block (inclusive)"),
|
|
545
|
+
toBlock: z5.number().int().nonnegative().describe("End block (inclusive)")
|
|
848
546
|
}, async ({ name, fromBlock, toBlock }) => {
|
|
849
547
|
const result = await clientProvider().subgraphs.backfill(name, {
|
|
850
548
|
fromBlock,
|
|
@@ -854,17 +552,17 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
854
552
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
855
553
|
};
|
|
856
554
|
});
|
|
857
|
-
defineTool(server, "subgraphs_stop", "Cancel an in-flight reindex or backfill operation for a subgraph. Returns the stop request status;
|
|
555
|
+
defineTool(server, "subgraphs_stop", "Cancel an in-flight reindex or backfill operation for a subgraph. Returns the stop request status; check subgraphs_get (health) or the REST operations endpoint to confirm it reaches a terminal state.", { name: z5.string().describe("Subgraph name") }, async ({ name }) => {
|
|
858
556
|
const result = await clientProvider().subgraphs.stop(name);
|
|
859
557
|
return {
|
|
860
558
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
861
559
|
};
|
|
862
560
|
});
|
|
863
561
|
defineTool(server, "subgraphs_gaps", "List indexing gaps (missing block ranges) for a subgraph. Each gap reports start/end/size, reason, and detected/resolved timestamps. Feed an unresolved gap's range into subgraphs_backfill to fill it. Defaults to unresolved gaps.", {
|
|
864
|
-
name:
|
|
865
|
-
limit:
|
|
866
|
-
offset:
|
|
867
|
-
resolved:
|
|
562
|
+
name: z5.string().describe("Subgraph name"),
|
|
563
|
+
limit: z5.number().int().positive().optional().describe("Max gaps to return"),
|
|
564
|
+
offset: z5.number().int().nonnegative().optional().describe("Pagination offset"),
|
|
565
|
+
resolved: z5.boolean().optional().describe("Filter by resolved state (omit for unresolved only)")
|
|
868
566
|
}, async ({ name, limit, offset, resolved }) => {
|
|
869
567
|
const result = await clientProvider().subgraphs.gaps(name, {
|
|
870
568
|
limit,
|
|
@@ -875,16 +573,16 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
875
573
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
876
574
|
};
|
|
877
575
|
});
|
|
878
|
-
defineTool(server, "subgraphs_delete", "Delete a subgraph permanently.", { name:
|
|
576
|
+
defineTool(server, "subgraphs_delete", "Delete a subgraph permanently.", { name: z5.string().describe("Subgraph name") }, async ({ name }) => {
|
|
879
577
|
const result = await clientProvider().subgraphs.delete(name);
|
|
880
578
|
return { content: [{ type: "text", text: result.message }] };
|
|
881
579
|
});
|
|
882
580
|
defineTool(server, "subgraphs_deploy", "Deploy a subgraph from TypeScript code. Pass the full defineSubgraph() source — it will be bundled, validated, and deployed. Optional startBlock overrides the source definition for this deploy. Set dryRun to validate and preview the schema/DDL without writing anything. Set databaseUrl to deploy to your own Postgres (BYO data plane) — the server verifies the connection first; with dryRun it returns the DDL + grant script. A breaking BYO schema change is refused and returns a migration plan (drop + rebuild DDL) instead of deploying. Visibility defaults: managed deploys are public (anon-readable at /v1/subgraphs/<name>, name claimed in the global public namespace), BYO deploys are private. Call `subgraphs_reindex` separately if you need a forced reindex.", {
|
|
883
|
-
code:
|
|
884
|
-
startBlock:
|
|
885
|
-
databaseUrl:
|
|
886
|
-
dryRun:
|
|
887
|
-
visibility:
|
|
581
|
+
code: z5.string().describe("TypeScript source code containing a defineSubgraph() call"),
|
|
582
|
+
startBlock: z5.number().int().nonnegative().optional().describe("Override the definition startBlock for this deploy"),
|
|
583
|
+
databaseUrl: z5.string().optional().describe("BYO data plane: Postgres connection string to host the subgraph's schema and rows in your own database"),
|
|
584
|
+
dryRun: z5.boolean().optional().describe("Validate and preview the deploy (schema/DDL, BYO connection) without writing changes"),
|
|
585
|
+
visibility: z5.enum(["public", "private"]).optional().describe("Read visibility: public = anon /v1 reads + global name claim; private = owning account's key only. Defaults: managed → public, BYO → private.")
|
|
888
586
|
}, async ({ code, startBlock, databaseUrl, dryRun, visibility }) => {
|
|
889
587
|
const bundled = await bundleSubgraphCode(code);
|
|
890
588
|
try {
|
|
@@ -918,58 +616,23 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
918
616
|
throw err;
|
|
919
617
|
}
|
|
920
618
|
});
|
|
921
|
-
defineTool(server, "subgraphs_publish", "Make a subgraph publicly readable at /v1/subgraphs/<name> — anyone (or any agent) can read it without a key. Claims the name in the global public namespace; fails with PUBLIC_NAME_TAKEN if another account holds it.", { name:
|
|
619
|
+
defineTool(server, "subgraphs_publish", "Make a subgraph publicly readable at /v1/subgraphs/<name> — anyone (or any agent) can read it without a key. Claims the name in the global public namespace; fails with PUBLIC_NAME_TAKEN if another account holds it.", { name: z5.string().describe("Subgraph name") }, async ({ name }) => {
|
|
922
620
|
const result = await clientProvider().subgraphs.publish(name);
|
|
923
621
|
return {
|
|
924
622
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
925
623
|
};
|
|
926
624
|
});
|
|
927
|
-
defineTool(server, "subgraphs_unpublish", "Make a subgraph private again — /v1 reads then require the owning account's bearer key, and the global public name claim is released.", { name:
|
|
625
|
+
defineTool(server, "subgraphs_unpublish", "Make a subgraph private again — /v1 reads then require the owning account's bearer key, and the global public name claim is released.", { name: z5.string().describe("Subgraph name") }, async ({ name }) => {
|
|
928
626
|
const result = await clientProvider().subgraphs.unpublish(name);
|
|
929
627
|
return {
|
|
930
628
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
931
629
|
};
|
|
932
630
|
});
|
|
933
|
-
defineTool(server, "subgraphs_read_source", "Fetch the deployed TypeScript source of a subgraph (plus its stored version). Returns a readOnly payload for subgraphs deployed before source capture — in that case the caller should redeploy via CLI before editing.", { name: z8.string().describe("Subgraph name") }, async ({ name }) => {
|
|
934
|
-
const source = await clientProvider().subgraphs.getSource(name);
|
|
935
|
-
return {
|
|
936
|
-
content: [{ type: "text", text: JSON.stringify(source, null, 2) }]
|
|
937
|
-
};
|
|
938
|
-
});
|
|
939
|
-
defineTool(server, "subgraphs_codegen", "Generate a typed ORM schema (Prisma, Drizzle, or Kysely) for a subgraph's tables so they can be queried from a BYO database. Pass either `code` (defineSubgraph source) or `name` (a deployed subgraph — its captured source is used). Returns the schema as text.", {
|
|
940
|
-
code: z8.string().optional().describe("defineSubgraph() source (mutually exclusive with name)"),
|
|
941
|
-
name: z8.string().optional().describe("Deployed subgraph name (mutually exclusive with code)"),
|
|
942
|
-
target: z8.enum(["prisma", "drizzle", "kysely"]).optional().describe("ORM target (default prisma)"),
|
|
943
|
-
schemaName: z8.string().optional().describe("Postgres schema name (defaults to subgraph_<name>)")
|
|
944
|
-
}, async ({ code, name, target = "prisma", schemaName }) => {
|
|
945
|
-
if (code && name || !code && !name) {
|
|
946
|
-
throw new Error("Provide exactly one of `code` or `name`.");
|
|
947
|
-
}
|
|
948
|
-
let source = code;
|
|
949
|
-
if (name) {
|
|
950
|
-
const stored = await clientProvider().subgraphs.getSource(name);
|
|
951
|
-
if (!stored.sourceCode) {
|
|
952
|
-
throw new Error(`Subgraph "${name}" has no captured source (deployed before source capture). Redeploy it, or pass its source as \`code\`.`);
|
|
953
|
-
}
|
|
954
|
-
source = stored.sourceCode;
|
|
955
|
-
}
|
|
956
|
-
const bundled = await bundleSubgraphCode(source);
|
|
957
|
-
const def = {
|
|
958
|
-
name: bundled.name,
|
|
959
|
-
version: bundled.version,
|
|
960
|
-
description: bundled.description,
|
|
961
|
-
sources: bundled.sources,
|
|
962
|
-
schema: bundled.schema,
|
|
963
|
-
handlers: {}
|
|
964
|
-
};
|
|
965
|
-
const out = target === "drizzle" ? generateDrizzleSchema(def, { schemaName }) : target === "kysely" ? generateKyselySchema(def, { schemaName }) : generatePrismaSchema(def, { schemaName });
|
|
966
|
-
return { content: [{ type: "text", text: out }] };
|
|
967
|
-
});
|
|
968
631
|
}
|
|
969
632
|
|
|
970
633
|
// src/tools/subscriptions.ts
|
|
971
634
|
import { CHAIN_TRIGGER_TYPES } from "@secondlayer/shared";
|
|
972
|
-
import { z as
|
|
635
|
+
import { z as z6 } from "zod/v4";
|
|
973
636
|
function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
974
637
|
defineTool(server, "subscriptions_list", "List all subscriptions for the current account. Returns summary fields (no secrets).", {}, async () => {
|
|
975
638
|
const { data } = await clientProvider().subscriptions.list();
|
|
@@ -977,34 +640,34 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
977
640
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
978
641
|
};
|
|
979
642
|
});
|
|
980
|
-
defineTool(server, "subscriptions_get", "Get full detail for a subscription (filter, auth, retry config, circuit state).", { id:
|
|
643
|
+
defineTool(server, "subscriptions_get", "Get full detail for a subscription (filter, auth, retry config, circuit state).", { id: z6.string().describe("Subscription id") }, async ({ id }) => {
|
|
981
644
|
const detail = await clientProvider().subscriptions.get(id);
|
|
982
645
|
return {
|
|
983
646
|
content: [{ type: "text", text: JSON.stringify(detail, null, 2) }]
|
|
984
647
|
};
|
|
985
648
|
});
|
|
986
649
|
defineTool(server, "subscriptions_create", "Create a subscription. Two kinds (mutually exclusive): a SUBGRAPH subscription fires on a subgraph table's rows (set subgraphName + tableName + optional filter); a CHAIN subscription fires on raw chain events with no subgraph (set triggers). Returns `signingSecret` ONCE — forward it to the user so they can wire it into their receiver.", {
|
|
987
|
-
name:
|
|
988
|
-
subgraphName:
|
|
989
|
-
tableName:
|
|
990
|
-
triggers:
|
|
991
|
-
type:
|
|
992
|
-
contractId:
|
|
993
|
-
functionName:
|
|
994
|
-
caller:
|
|
995
|
-
sender:
|
|
996
|
-
recipient:
|
|
997
|
-
assetIdentifier:
|
|
998
|
-
deployer:
|
|
999
|
-
contractName:
|
|
1000
|
-
topic:
|
|
1001
|
-
lockedAddress:
|
|
1002
|
-
trait:
|
|
1003
|
-
minAmount:
|
|
1004
|
-
maxAmount:
|
|
650
|
+
name: z6.string().describe("Human-readable name, unique per account"),
|
|
651
|
+
subgraphName: z6.string().optional().describe("Subgraph to subscribe to (subgraph subscription)"),
|
|
652
|
+
tableName: z6.string().optional().describe("Table within the subgraph (subgraph subscription)"),
|
|
653
|
+
triggers: z6.array(z6.object({
|
|
654
|
+
type: z6.enum(CHAIN_TRIGGER_TYPES),
|
|
655
|
+
contractId: z6.string().optional(),
|
|
656
|
+
functionName: z6.string().optional(),
|
|
657
|
+
caller: z6.string().optional(),
|
|
658
|
+
sender: z6.string().optional(),
|
|
659
|
+
recipient: z6.string().optional(),
|
|
660
|
+
assetIdentifier: z6.string().optional(),
|
|
661
|
+
deployer: z6.string().optional(),
|
|
662
|
+
contractName: z6.string().optional(),
|
|
663
|
+
topic: z6.string().optional(),
|
|
664
|
+
lockedAddress: z6.string().optional(),
|
|
665
|
+
trait: z6.string().optional(),
|
|
666
|
+
minAmount: z6.union([z6.string(), z6.number()]).optional(),
|
|
667
|
+
maxAmount: z6.union([z6.string(), z6.number()]).optional()
|
|
1005
668
|
})).optional().describe("Chain triggers (chain subscription) — provide INSTEAD of subgraphName/tableName. Each targets a raw chain event/tx; string fields accept `*` wildcards, `trait` scopes to a SIP/trait. Per-type accepted fields: see the secondlayer://chain-triggers resource. Forward-looking: starts at chain tip, no backfill."),
|
|
1006
|
-
url:
|
|
1007
|
-
format:
|
|
669
|
+
url: z6.string().describe("Webhook URL"),
|
|
670
|
+
format: z6.enum([
|
|
1008
671
|
"standard-webhooks",
|
|
1009
672
|
"inngest",
|
|
1010
673
|
"trigger",
|
|
@@ -1012,9 +675,9 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
1012
675
|
"cloudevents",
|
|
1013
676
|
"raw"
|
|
1014
677
|
]).optional().describe("Wire format (default standard-webhooks)"),
|
|
1015
|
-
runtime:
|
|
1016
|
-
filter:
|
|
1017
|
-
authConfig:
|
|
678
|
+
runtime: z6.enum(["inngest", "trigger", "cloudflare", "node"]).optional().describe("Receiver runtime label (display only)"),
|
|
679
|
+
filter: z6.record(z6.string(), z6.unknown()).optional().describe('Scalar filter DSL, e.g. {"amount": {"gte": 100}, "sender": "SP..."}'),
|
|
680
|
+
authConfig: z6.record(z6.string(), z6.unknown()).optional().describe('Receiver auth sent with each delivery, e.g. {"type": "bearer", "token": "..."}')
|
|
1018
681
|
}, async (input) => {
|
|
1019
682
|
const res = await clientProvider().subscriptions.create(input);
|
|
1020
683
|
return {
|
|
@@ -1022,12 +685,12 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
1022
685
|
};
|
|
1023
686
|
});
|
|
1024
687
|
defineTool(server, "subscriptions_update", "Patch a subscription (name, url, filter, authConfig, format, runtime, retry, timeout, concurrency).", {
|
|
1025
|
-
id:
|
|
1026
|
-
name:
|
|
1027
|
-
url:
|
|
1028
|
-
filter:
|
|
1029
|
-
authConfig:
|
|
1030
|
-
format:
|
|
688
|
+
id: z6.string(),
|
|
689
|
+
name: z6.string().optional().describe("Rename the subscription"),
|
|
690
|
+
url: z6.string().optional(),
|
|
691
|
+
filter: z6.record(z6.string(), z6.unknown()).optional(),
|
|
692
|
+
authConfig: z6.record(z6.string(), z6.unknown()).optional().describe("Receiver auth sent with each delivery (bearer/etc.)"),
|
|
693
|
+
format: z6.enum([
|
|
1031
694
|
"standard-webhooks",
|
|
1032
695
|
"inngest",
|
|
1033
696
|
"trigger",
|
|
@@ -1035,51 +698,33 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
1035
698
|
"cloudevents",
|
|
1036
699
|
"raw"
|
|
1037
700
|
]).optional(),
|
|
1038
|
-
runtime:
|
|
1039
|
-
maxRetries:
|
|
1040
|
-
timeoutMs:
|
|
1041
|
-
concurrency:
|
|
701
|
+
runtime: z6.enum(["inngest", "trigger", "cloudflare", "node"]).nullable().optional(),
|
|
702
|
+
maxRetries: z6.number().int().min(0).optional(),
|
|
703
|
+
timeoutMs: z6.number().int().min(100).optional(),
|
|
704
|
+
concurrency: z6.number().int().min(1).optional()
|
|
1042
705
|
}, async ({ id, ...patch }) => {
|
|
1043
706
|
const res = await clientProvider().subscriptions.update(id, patch);
|
|
1044
707
|
return {
|
|
1045
708
|
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
1046
709
|
};
|
|
1047
710
|
});
|
|
1048
|
-
defineTool(server, "
|
|
1049
|
-
const res = await clientProvider().subscriptions.pause(id);
|
|
1050
|
-
return {
|
|
1051
|
-
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
1052
|
-
};
|
|
1053
|
-
});
|
|
1054
|
-
defineTool(server, "subscriptions_resume", "Resume a paused or circuit-open subscription and reset circuit failures.", { id: z9.string() }, async ({ id }) => {
|
|
1055
|
-
const res = await clientProvider().subscriptions.resume(id);
|
|
1056
|
-
return {
|
|
1057
|
-
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
1058
|
-
};
|
|
1059
|
-
});
|
|
1060
|
-
defineTool(server, "subscriptions_delete", "Delete a subscription. Pending outbox rows are cascade-deleted.", { id: z9.string() }, async ({ id }) => {
|
|
711
|
+
defineTool(server, "subscriptions_delete", "Delete a subscription. Pending outbox rows are cascade-deleted.", { id: z6.string() }, async ({ id }) => {
|
|
1061
712
|
const res = await clientProvider().subscriptions.delete(id);
|
|
1062
713
|
return {
|
|
1063
714
|
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
1064
715
|
};
|
|
1065
716
|
});
|
|
1066
|
-
defineTool(server, "
|
|
1067
|
-
const res = await clientProvider().subscriptions.rotateSecret(id);
|
|
1068
|
-
return {
|
|
1069
|
-
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
1070
|
-
};
|
|
1071
|
-
});
|
|
1072
|
-
defineTool(server, "subscriptions_test", "Send a one-off test webhook to a subscription's URL (built for its format, SSRF-guarded). Logged as a delivery row — check subscriptions_recent_deliveries. Returns {ok, statusCode, error, durationMs, deliveryId}.", { id: z9.string().describe("Subscription id") }, async ({ id }) => {
|
|
717
|
+
defineTool(server, "subscriptions_test", "Send a one-off test webhook to a subscription's URL (built for its format, SSRF-guarded). Logged as a delivery row. Returns {ok, statusCode, error, durationMs, deliveryId}.", { id: z6.string().describe("Subscription id") }, async ({ id }) => {
|
|
1073
718
|
const res = await clientProvider().subscriptions.test(id);
|
|
1074
719
|
return {
|
|
1075
720
|
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
1076
721
|
};
|
|
1077
722
|
});
|
|
1078
723
|
defineTool(server, "subscriptions_replay", "Replay a block range for a subscription. Replays run at 10% of batch capacity — use sparingly. Pass `force` (a short idempotency suffix) to re-run a range that was already replayed.", {
|
|
1079
|
-
id:
|
|
1080
|
-
fromBlock:
|
|
1081
|
-
toBlock:
|
|
1082
|
-
force:
|
|
724
|
+
id: z6.string(),
|
|
725
|
+
fromBlock: z6.number().int().nonnegative(),
|
|
726
|
+
toBlock: z6.number().int().nonnegative(),
|
|
727
|
+
force: z6.string().optional().describe("Idempotency suffix to force a duplicate replay of the range")
|
|
1083
728
|
}, async ({ id, fromBlock, toBlock, force }) => {
|
|
1084
729
|
const res = await clientProvider().subscriptions.replay(id, {
|
|
1085
730
|
fromBlock,
|
|
@@ -1090,27 +735,6 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
1090
735
|
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
1091
736
|
};
|
|
1092
737
|
});
|
|
1093
|
-
defineTool(server, "subscriptions_dead", "Return the last 100 dead-letter outbox rows for a subscription.", { id: z9.string() }, async ({ id }) => {
|
|
1094
|
-
const { data } = await clientProvider().subscriptions.dead(id);
|
|
1095
|
-
return {
|
|
1096
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1097
|
-
};
|
|
1098
|
-
});
|
|
1099
|
-
defineTool(server, "subscriptions_requeue_dead", "Requeue one dead-letter outbox row for delivery retry.", {
|
|
1100
|
-
id: z9.string(),
|
|
1101
|
-
outboxId: z9.string()
|
|
1102
|
-
}, async ({ id, outboxId }) => {
|
|
1103
|
-
const res = await clientProvider().subscriptions.requeueDead(id, outboxId);
|
|
1104
|
-
return {
|
|
1105
|
-
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
1106
|
-
};
|
|
1107
|
-
});
|
|
1108
|
-
defineTool(server, "subscriptions_recent_deliveries", "Return the last 100 delivery attempts (attempt #, status code, duration, truncated response).", { id: z9.string() }, async ({ id }) => {
|
|
1109
|
-
const { data } = await clientProvider().subscriptions.recentDeliveries(id);
|
|
1110
|
-
return {
|
|
1111
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
1112
|
-
};
|
|
1113
|
-
});
|
|
1114
738
|
}
|
|
1115
739
|
|
|
1116
740
|
// src/server.ts
|
|
@@ -1125,8 +749,6 @@ function createServer() {
|
|
|
1125
749
|
registerSubgraphTools(server);
|
|
1126
750
|
registerSubscriptionTools(server);
|
|
1127
751
|
registerAccountTools(server);
|
|
1128
|
-
registerProjectTools(server);
|
|
1129
|
-
registerDatasetTools(server);
|
|
1130
752
|
registerIndexTools(server);
|
|
1131
753
|
registerStreamsTools(server);
|
|
1132
754
|
registerContractTools(server);
|
|
@@ -1220,5 +842,5 @@ httpServer.listen(port, () => {
|
|
|
1220
842
|
console.error("Warning: SECONDLAYER_MCP_SECRET not set, authentication disabled");
|
|
1221
843
|
});
|
|
1222
844
|
|
|
1223
|
-
//# debugId=
|
|
845
|
+
//# debugId=CB0901E85835C61764756E2164756E21
|
|
1224
846
|
//# sourceMappingURL=bin-http.js.map
|