@secondlayer/mcp 6.0.0 → 7.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 +21 -21
- package/dist/bin-http.js +222 -136
- package/dist/bin-http.js.map +14 -13
- package/dist/bin.js +222 -136
- package/dist/bin.js.map +14 -13
- package/dist/index.js +222 -136
- package/dist/index.js.map +14 -13
- package/package.json +5 -4
package/dist/bin.js
CHANGED
|
@@ -16,10 +16,10 @@ import { filterFieldsByType } from "@secondlayer/subgraphs";
|
|
|
16
16
|
import { TYPE_MAP } from "@secondlayer/subgraphs/schema";
|
|
17
17
|
|
|
18
18
|
// src/lib/client.ts
|
|
19
|
-
import { SecondLayer } from "@secondlayer/sdk";
|
|
19
|
+
import { SecondLayer, resolveApiKey } from "@secondlayer/sdk";
|
|
20
20
|
var instance = null;
|
|
21
21
|
function readApiKey() {
|
|
22
|
-
return
|
|
22
|
+
return resolveApiKey();
|
|
23
23
|
}
|
|
24
24
|
function getClient() {
|
|
25
25
|
if (!instance) {
|
|
@@ -35,17 +35,35 @@ function getClient() {
|
|
|
35
35
|
}
|
|
36
36
|
return instance;
|
|
37
37
|
}
|
|
38
|
+
var keyHint = " — set INSTANCE_TOKEN from `sl init` for writes (SL_API_KEY is a legacy alias)";
|
|
39
|
+
async function apiRequest(method, path, body) {
|
|
40
|
+
const apiKey = readApiKey();
|
|
41
|
+
const baseUrl = process.env.SECONDLAYER_API_URL || process.env.SL_API_URL || "http://127.0.0.1:3800";
|
|
42
|
+
const res = await fetch(`${baseUrl}${path}`, {
|
|
43
|
+
method,
|
|
44
|
+
headers: {
|
|
45
|
+
"Content-Type": "application/json",
|
|
46
|
+
...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
|
|
47
|
+
},
|
|
48
|
+
body: body ? JSON.stringify(body) : undefined
|
|
49
|
+
});
|
|
50
|
+
if (!res.ok) {
|
|
51
|
+
const text = await res.text().catch(() => "");
|
|
52
|
+
const needsKey = !apiKey && (res.status === 401 || res.status === 403);
|
|
53
|
+
throw Object.assign(new Error((text || `HTTP ${res.status}`) + (needsKey ? keyHint : "")), { status: res.status });
|
|
54
|
+
}
|
|
55
|
+
if (res.status === 204)
|
|
56
|
+
return;
|
|
57
|
+
return res.json();
|
|
58
|
+
}
|
|
38
59
|
|
|
39
60
|
// src/lib/format.ts
|
|
40
61
|
function formatSubgraphSummary(s) {
|
|
41
|
-
const apiUrl = process.env.SECONDLAYER_API_URL || process.env.SL_API_URL || "http://127.0.0.1:3800";
|
|
42
62
|
return {
|
|
43
63
|
name: s.name,
|
|
44
64
|
status: s.status,
|
|
45
65
|
tables: Array.isArray(s.tables) ? s.tables : Object.keys(s.tables),
|
|
46
|
-
lastProcessedBlock: s.lastProcessedBlock
|
|
47
|
-
...s.visibility ? { visibility: s.visibility } : {},
|
|
48
|
-
...s.visibility === "public" ? { publicUrl: `${apiUrl}/v1/subgraphs/${s.name}` } : {}
|
|
66
|
+
lastProcessedBlock: s.lastProcessedBlock
|
|
49
67
|
};
|
|
50
68
|
}
|
|
51
69
|
function withCap(items, cap) {
|
|
@@ -73,7 +91,7 @@ var registeredToolNames = new Set;
|
|
|
73
91
|
function getRegisteredToolNames() {
|
|
74
92
|
return [...registeredToolNames];
|
|
75
93
|
}
|
|
76
|
-
function defineTool(server, name, description, schema, handler
|
|
94
|
+
function defineTool(server, name, description, schema, handler) {
|
|
77
95
|
const wrappedHandler = async (args) => {
|
|
78
96
|
try {
|
|
79
97
|
return await handler(args);
|
|
@@ -94,9 +112,6 @@ function defineTool(server, name, description, schema, handler, options = {}) {
|
|
|
94
112
|
};
|
|
95
113
|
registeredToolNames.add(name);
|
|
96
114
|
server.tool(name, description, schema, wrappedHandler);
|
|
97
|
-
for (const alias of options.aliases ?? []) {
|
|
98
|
-
server.tool(alias, `Deprecated alias for \`${name}\`. ${description}`, schema, wrappedHandler);
|
|
99
|
-
}
|
|
100
115
|
}
|
|
101
116
|
|
|
102
117
|
// src/resources.ts
|
|
@@ -128,12 +143,11 @@ var COLUMN_TYPES = [
|
|
|
128
143
|
];
|
|
129
144
|
var PRODUCT_BLURBS = {
|
|
130
145
|
index: "decoded events, transfers, contract calls, blocks, transactions",
|
|
131
|
-
streams: "
|
|
146
|
+
streams: "raw chain event firehose — cursor-paginated reads, tip/canonical/reorg checks, bulk parquet dumps",
|
|
132
147
|
contracts: "trait-based contract discovery and ABIs",
|
|
133
|
-
subgraphs: "author/deploy/query custom indexes",
|
|
148
|
+
subgraphs: "author/scaffold/deploy/query custom indexes",
|
|
134
149
|
subscriptions: "webhook delivery on subgraph rows or raw chain events",
|
|
135
150
|
account: "identity and self-provisioned API keys",
|
|
136
|
-
scaffold: "generate deploy-ready subgraph code from a deployed contract",
|
|
137
151
|
codegen: "ORM schemas for the tables you read"
|
|
138
152
|
};
|
|
139
153
|
var PRODUCT_ORDER = [
|
|
@@ -143,7 +157,6 @@ var PRODUCT_ORDER = [
|
|
|
143
157
|
"subgraphs",
|
|
144
158
|
"subscriptions",
|
|
145
159
|
"account",
|
|
146
|
-
"scaffold",
|
|
147
160
|
"codegen"
|
|
148
161
|
];
|
|
149
162
|
function buildCapabilities() {
|
|
@@ -169,17 +182,17 @@ function buildCapabilities() {
|
|
|
169
182
|
};
|
|
170
183
|
}
|
|
171
184
|
var READ_AUTH_TIERS = {
|
|
172
|
-
index: "
|
|
173
|
-
streams: "API
|
|
174
|
-
subgraphs: "
|
|
185
|
+
index: "keyless while the API is only reachable on loopback; the instance token is required once it is published past loopback",
|
|
186
|
+
streams: "keyless while the API is only reachable on loopback; the instance token is required once it is published past loopback",
|
|
187
|
+
subgraphs: "reads at /v1/subgraphs/<name>/<table> ({ rows, next_cursor, tip } cursor envelope) follow the same loopback rule as the other read planes; writes always require the instance token"
|
|
175
188
|
};
|
|
176
189
|
async function buildContext(deps = { clientProvider: getClient }) {
|
|
177
|
-
const unavailable = "unavailable: set
|
|
190
|
+
const unavailable = "unavailable: set INSTANCE_TOKEN";
|
|
178
191
|
const orNull = (v) => v == null ? unavailable : v;
|
|
179
192
|
const snap = await deps.clientProvider().context().catch(() => null);
|
|
180
193
|
return {
|
|
181
194
|
authState: {
|
|
182
|
-
apiKeySet: Boolean(
|
|
195
|
+
apiKeySet: Boolean(readApiKey())
|
|
183
196
|
},
|
|
184
197
|
whatExists: {
|
|
185
198
|
account: orNull(snap?.account),
|
|
@@ -252,18 +265,31 @@ function registerResources(server) {
|
|
|
252
265
|
}));
|
|
253
266
|
}
|
|
254
267
|
|
|
268
|
+
// src/tools/account.ts
|
|
269
|
+
import { z } from "zod";
|
|
270
|
+
function registerAccountTools(server) {
|
|
271
|
+
defineTool(server, "account_whoami", "Show the authenticated account's email.", {}, async () => {
|
|
272
|
+
const result = await apiRequest("GET", "/api/accounts/me");
|
|
273
|
+
return jsonResponse(result);
|
|
274
|
+
});
|
|
275
|
+
defineTool(server, "account_create_key", "Mint a scoped streams/index read key. Only the metered archive deployment can do this — a self-hosted instance has no account system and returns 404, so on your own instance use the INSTANCE_TOKEN from `secondlayer init` instead. The returned `key` is shown ONCE.", {
|
|
276
|
+
product: z.enum(["streams", "index"]).optional().describe("Key scope (default streams)"),
|
|
277
|
+
name: z.string().optional().describe("Optional label for the key")
|
|
278
|
+
}, async ({ product, name }) => jsonResponse(await apiRequest("POST", "/api/keys", { product, name })));
|
|
279
|
+
}
|
|
280
|
+
|
|
255
281
|
// src/tools/codegen.ts
|
|
256
282
|
import {
|
|
257
283
|
generateIndexSchema
|
|
258
284
|
} from "@secondlayer/subgraphs";
|
|
259
|
-
import { z } from "zod";
|
|
285
|
+
import { z as z2 } from "zod";
|
|
260
286
|
var TARGETS = ["prisma", "kysely", "drizzle", "json-schema"];
|
|
261
287
|
function registerCodegenTools(server) {
|
|
262
288
|
defineTool(server, "codegen_index_schema", "Generate an ORM schema for the Index tables (blocks, transactions, decoded_events, …) — the same output as `sl codegen index`. Returns the file contents as text; write it wherever your project keeps its schema. Use `tables` to emit only what you read.", {
|
|
263
|
-
target:
|
|
264
|
-
tables:
|
|
265
|
-
schemaName:
|
|
266
|
-
datasourceEnv:
|
|
289
|
+
target: z2.enum(TARGETS).optional().describe("Output flavor (default kysely)"),
|
|
290
|
+
tables: z2.array(z2.string()).optional().describe("Restrict to these Index tables (default: all)"),
|
|
291
|
+
schemaName: z2.string().optional().describe("Postgres schema to qualify table names with"),
|
|
292
|
+
datasourceEnv: z2.string().optional().describe("Prisma only: datasource url env var (default DATABASE_URL)")
|
|
267
293
|
}, ({ target, tables, schemaName, datasourceEnv }) => textResponse(generateIndexSchema(target ?? "kysely", {
|
|
268
294
|
tables,
|
|
269
295
|
schemaName,
|
|
@@ -272,17 +298,17 @@ function registerCodegenTools(server) {
|
|
|
272
298
|
}
|
|
273
299
|
|
|
274
300
|
// src/tools/contracts.ts
|
|
275
|
-
import { z as
|
|
301
|
+
import { z as z3 } from "zod";
|
|
276
302
|
function registerContractTools(server, clientProvider = getClient) {
|
|
277
303
|
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.', {
|
|
278
|
-
trait:
|
|
279
|
-
conformance:
|
|
280
|
-
include:
|
|
281
|
-
limit:
|
|
282
|
-
cursor:
|
|
304
|
+
trait: z3.string().describe('Required. Trait to match (e.g. "sip-010").'),
|
|
305
|
+
conformance: z3.enum(["declared", "inferred", "any"]).optional().describe("declared = parsed from source, inferred = ABI shape-match, any = either (default)"),
|
|
306
|
+
include: z3.literal("abi").optional().describe(`Set to "abi" to include each contract's full ABI`),
|
|
307
|
+
limit: z3.number().optional().describe("Page size, 1–500 (default 100)"),
|
|
308
|
+
cursor: z3.string().optional().describe("Opaque cursor from a prior response's next_cursor")
|
|
283
309
|
}, async (params) => jsonResponse(await clientProvider().contracts.list(params)));
|
|
284
|
-
defineTool(server, "contracts_get_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
|
|
285
|
-
contractId:
|
|
310
|
+
defineTool(server, "contracts_get_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 subgraphs_scaffold.", {
|
|
311
|
+
contractId: z3.string().describe("Fully qualified contract id (e.g. SP….amm-pool-v2-01)")
|
|
286
312
|
}, async ({ contractId }) => {
|
|
287
313
|
const contract = await clientProvider().contracts.get(contractId, {
|
|
288
314
|
includeAbi: true
|
|
@@ -294,19 +320,19 @@ function registerContractTools(server, clientProvider = getClient) {
|
|
|
294
320
|
message: `Contract not in registry: ${contractId}`
|
|
295
321
|
}
|
|
296
322
|
}, true);
|
|
297
|
-
}
|
|
323
|
+
});
|
|
298
324
|
}
|
|
299
325
|
|
|
300
326
|
// src/tools/index.ts
|
|
301
327
|
import { DECODED_EVENT_TYPES } from "@secondlayer/shared";
|
|
302
|
-
import { z as
|
|
328
|
+
import { z as z4 } from "zod";
|
|
303
329
|
var INDEX_EVENT_TYPES = DECODED_EVENT_TYPES;
|
|
304
330
|
var rangeFilters = {
|
|
305
|
-
contractId:
|
|
306
|
-
fromHeight:
|
|
307
|
-
toHeight:
|
|
308
|
-
cursor:
|
|
309
|
-
limit:
|
|
331
|
+
contractId: z4.string().optional().describe("Filter by contract id"),
|
|
332
|
+
fromHeight: z4.number().optional().describe("Start block height (inclusive)"),
|
|
333
|
+
toHeight: z4.number().optional().describe("End block height (inclusive)"),
|
|
334
|
+
cursor: z4.string().optional().describe("Opaque cursor from a prior response's next_cursor"),
|
|
335
|
+
limit: z4.number().optional().describe("Max rows for this page")
|
|
310
336
|
};
|
|
311
337
|
var heightFilters = {
|
|
312
338
|
fromHeight: rangeFilters.fromHeight,
|
|
@@ -317,52 +343,52 @@ var heightFilters = {
|
|
|
317
343
|
function registerIndexTools(server, clientProvider = getClient) {
|
|
318
344
|
defineTool(server, "index_ft_transfers", "List decoded SIP-010 fungible-token transfers from the Index (decoded layer). Anonymous reads allowed (free-tier API keys are rejected — Build+ required).", {
|
|
319
345
|
...rangeFilters,
|
|
320
|
-
sender:
|
|
321
|
-
recipient:
|
|
322
|
-
assetIdentifier:
|
|
346
|
+
sender: z4.string().optional().describe("Filter by sender principal"),
|
|
347
|
+
recipient: z4.string().optional().describe("Filter by recipient principal"),
|
|
348
|
+
assetIdentifier: z4.string().optional().describe("Filter by asset identifier (contract::asset), e.g. SP…sbtc-token::sbtc-token")
|
|
323
349
|
}, async (params) => jsonResponse(await clientProvider().index.ftTransfers.list(params)));
|
|
324
350
|
defineTool(server, "index_nft_transfers", "List decoded SIP-009 non-fungible-token transfers from the Index. Anonymous reads allowed (free-tier keys rejected).", {
|
|
325
351
|
...rangeFilters,
|
|
326
|
-
sender:
|
|
327
|
-
recipient:
|
|
328
|
-
assetIdentifier:
|
|
352
|
+
sender: z4.string().optional().describe("Filter by sender principal"),
|
|
353
|
+
recipient: z4.string().optional().describe("Filter by recipient principal"),
|
|
354
|
+
assetIdentifier: z4.string().optional().describe("Filter by asset identifier (contract::asset)")
|
|
329
355
|
}, async (params) => jsonResponse(await clientProvider().index.nftTransfers.list(params)));
|
|
330
356
|
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.", {
|
|
331
|
-
eventType:
|
|
357
|
+
eventType: z4.enum(INDEX_EVENT_TYPES).describe("Required. Decoded event type to list."),
|
|
332
358
|
...rangeFilters,
|
|
333
|
-
sender:
|
|
334
|
-
recipient:
|
|
335
|
-
assetIdentifier:
|
|
336
|
-
trait:
|
|
337
|
-
txContext:
|
|
359
|
+
sender: z4.string().optional().describe("Filter by sender principal"),
|
|
360
|
+
recipient: z4.string().optional().describe("Filter by recipient principal"),
|
|
361
|
+
assetIdentifier: z4.string().optional().describe("Filter by asset identifier where applicable"),
|
|
362
|
+
trait: z4.string().optional().describe("Match contracts conforming to a trait/standard (e.g. sip-010). Mutually exclusive with contractId; contract-keyed event types only."),
|
|
363
|
+
txContext: z4.boolean().optional().describe("Join the submitting transaction into each event (tx_sender, tx_type, tx_status, tx_contract_id, tx_function_name). For print events it's the only source of the submitting sender.")
|
|
338
364
|
}, async (params) => jsonResponse(await clientProvider().index.events.list(params)));
|
|
339
365
|
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.", {
|
|
340
366
|
...rangeFilters,
|
|
341
|
-
functionName:
|
|
342
|
-
sender:
|
|
343
|
-
trait:
|
|
367
|
+
functionName: z4.string().optional().describe("Filter by called function name"),
|
|
368
|
+
sender: z4.string().optional().describe("Filter by caller principal"),
|
|
369
|
+
trait: z4.string().optional().describe("Match contracts conforming to a trait/standard (e.g. sip-010). Mutually exclusive with contractId.")
|
|
344
370
|
}, async (params) => jsonResponse(await clientProvider().index.contractCalls.list(params)));
|
|
345
371
|
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)));
|
|
346
372
|
defineTool(server, "index_transactions", "List decoded transactions from the Index. Filter by type, sender, or contract. Anonymous reads allowed (free-tier keys rejected).", {
|
|
347
373
|
...rangeFilters,
|
|
348
|
-
type:
|
|
349
|
-
sender:
|
|
374
|
+
type: z4.string().optional().describe("Filter by transaction type"),
|
|
375
|
+
sender: z4.string().optional().describe("Filter by sender principal")
|
|
350
376
|
}, async (params) => jsonResponse(await clientProvider().index.transactions.list(params)));
|
|
351
377
|
defineTool(server, "index_print_schema", "Empirical per-topic print payload schemas for a contract, inferred from sampled on-chain events: each topic's fields with observed Clarity types, decoded TS types, subgraph column types, and presence rates. Use before writing print_event subgraph handlers to learn what's on e.data. Anonymous read.", {
|
|
352
|
-
contractId:
|
|
378
|
+
contractId: z4.string().describe("Contract id (SP….name) whose print events to profile")
|
|
353
379
|
}, async (params) => jsonResponse(await clientProvider().index.printSchema(params.contractId)));
|
|
354
380
|
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()));
|
|
355
381
|
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.", {
|
|
356
|
-
requests:
|
|
357
|
-
path:
|
|
358
|
-
params:
|
|
382
|
+
requests: z4.array(z4.object({
|
|
383
|
+
path: z4.string().describe("Public /v1 read path"),
|
|
384
|
+
params: z4.record(z4.string(), z4.string()).optional()
|
|
359
385
|
})).min(1).max(10).describe("Read descriptors, executed concurrently")
|
|
360
386
|
}, async (params) => jsonResponse(await clientProvider().batch(params.requests)));
|
|
361
387
|
}
|
|
362
388
|
|
|
363
389
|
// src/tools/scaffold.ts
|
|
364
390
|
import { generateSubgraphCode } from "@secondlayer/scaffold";
|
|
365
|
-
import { z as
|
|
391
|
+
import { z as z5 } from "zod";
|
|
366
392
|
async function fetchAbi(clientProvider, contractId) {
|
|
367
393
|
const contract = await clientProvider().contracts.get(contractId, {
|
|
368
394
|
includeAbi: true
|
|
@@ -376,9 +402,9 @@ async function fetchAbi(clientProvider, contractId) {
|
|
|
376
402
|
return { functions: abi.functions ?? [] };
|
|
377
403
|
}
|
|
378
404
|
function registerScaffoldTools(server, clientProvider = getClient) {
|
|
379
|
-
defineTool(server, "
|
|
380
|
-
contractId:
|
|
381
|
-
subgraphName:
|
|
405
|
+
defineTool(server, "subgraphs_scaffold", "Generate a subgraph scaffold from a deployed Stacks contract. Fetches the ABI automatically. Returns TypeScript source — review it, then pass it to subgraphs_deploy.", {
|
|
406
|
+
contractId: z5.string().describe("Fully qualified contract ID (e.g. SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01)"),
|
|
407
|
+
subgraphName: z5.string().optional().describe("Override the subgraph name (defaults to contract name)")
|
|
382
408
|
}, async ({ contractId, subgraphName }) => {
|
|
383
409
|
const { functions } = await fetchAbi(clientProvider, contractId);
|
|
384
410
|
const code = generateSubgraphCode(contractId, functions, subgraphName);
|
|
@@ -387,32 +413,75 @@ function registerScaffoldTools(server, clientProvider = getClient) {
|
|
|
387
413
|
}
|
|
388
414
|
|
|
389
415
|
// src/tools/streams.ts
|
|
416
|
+
import { STREAMS_EVENT_TYPES } from "@secondlayer/shared";
|
|
417
|
+
import { z as z6 } from "zod";
|
|
418
|
+
var filterValue = z6.union([z6.string(), z6.array(z6.string())]);
|
|
390
419
|
function registerStreamsTools(server, clientProvider = getClient) {
|
|
391
|
-
defineTool(server, "
|
|
420
|
+
defineTool(server, "streams_tip", "Current canonical chain tip as Streams sees it: block height/hash, burn height, finalized_height, lag_seconds, and the oldest seekable height/cursor for this key's retention window. Call it before a cursor walk to know where the stream ends, and after an ingest to check lag.", {}, async () => jsonResponse(await clientProvider().streams.tip()));
|
|
421
|
+
defineTool(server, "streams_events", "List raw chain events from Streams — ONE page per call, newest filters applied server-side. Returns { events, next_cursor, tip, reorgs }. To follow the chain live, poll this tool with `cursor` set to the previous response's `next_cursor` (input is exclusive, so no duplicates); there is no open-stream tool because a tool call cannot hold a connection. ALWAYS narrow with types/contractId/sender/recipient — an unfiltered page is the full firehose.", {
|
|
422
|
+
cursor: z6.string().optional().describe("Resume strictly after this cursor (`<block_height>:<event_index>`, e.g. 951475:3) — pass a prior response's next_cursor"),
|
|
423
|
+
fromHeight: z6.number().int().nonnegative().optional().describe("Start block height (inclusive)"),
|
|
424
|
+
toHeight: z6.number().int().nonnegative().optional().describe("End block height (inclusive)"),
|
|
425
|
+
types: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to include"),
|
|
426
|
+
notTypes: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to exclude (applied after types)"),
|
|
427
|
+
contractId: filterValue.optional().describe("Contract id, or a list of contract ids to OR together"),
|
|
428
|
+
sender: filterValue.optional().describe("Sender principal, or a list to OR together"),
|
|
429
|
+
recipient: filterValue.optional().describe("Recipient principal, or a list to OR together"),
|
|
430
|
+
assetIdentifier: z6.string().optional().describe("Asset identifier (contract::asset), e.g. SP…sbtc-token::sbtc-token"),
|
|
431
|
+
limit: z6.number().int().min(1).max(1000).optional().describe("Page size, 1–1000 (default 100)")
|
|
432
|
+
}, async (params) => jsonResponse(await clientProvider().streams.events.list(params)));
|
|
433
|
+
defineTool(server, "streams_events_by_tx", "List every event emitted by a single transaction, in emission order. Use it to explain one transaction end to end after finding it in streams_events or the Index.", {
|
|
434
|
+
txId: z6.string().describe("Transaction id (0x-prefixed)")
|
|
435
|
+
}, async ({ txId }) => jsonResponse(await clientProvider().streams.events.byTxId(txId)));
|
|
436
|
+
defineTool(server, "streams_block_events", "List every event in one block, addressed by block height or block hash. Use it to audit a block a consumer flagged, or to diff what a reorg replaced.", {
|
|
437
|
+
heightOrHash: z6.union([z6.number().int().nonnegative(), z6.string()]).describe("Block height (number) or block hash (0x-prefixed string)")
|
|
438
|
+
}, async ({ heightOrHash }) => jsonResponse(await clientProvider().streams.blocks.events(heightOrHash)));
|
|
439
|
+
defineTool(server, "streams_canonical", "Get the canonical block at a height: hash, burn block hash, parent, and whether the height is still canonical. Use it to confirm a height you already consumed was not reorged away before you trust derived state.", {
|
|
440
|
+
height: z6.number().int().nonnegative().describe("Block height")
|
|
441
|
+
}, async ({ height }) => jsonResponse(await clientProvider().streams.canonical(height)));
|
|
442
|
+
defineTool(server, "streams_reorgs", "List reorgs detected since a point in time, newest last, with the new canonical tip for each. Returns { reorgs, next_since } — resume with next_since. Call it when a consumer's derived state disagrees with streams_canonical, or on a schedule to know which heights to reprocess.", {
|
|
443
|
+
since: z6.string().describe("Required. ISO 8601 timestamp, or a `next_since` resume token from a prior response"),
|
|
444
|
+
limit: z6.number().int().positive().optional().describe("Max reorgs to return")
|
|
445
|
+
}, async ({ since, limit }) => jsonResponse(await clientProvider().streams.reorgs.list({
|
|
446
|
+
since,
|
|
447
|
+
...limit !== undefined ? { limit } : {}
|
|
448
|
+
})));
|
|
449
|
+
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). For live reads use streams_events (cursor-paginated) and streams_tip.", {}, async () => jsonResponse(await clientProvider().streams.dumps.list()));
|
|
392
450
|
}
|
|
393
451
|
|
|
394
452
|
// src/tools/subgraphs.ts
|
|
395
453
|
import { bundleSubgraphCode } from "@secondlayer/bundler";
|
|
396
|
-
import { z as
|
|
454
|
+
import { z as z7 } from "zod";
|
|
397
455
|
function registerSubgraphTools(server, clientProvider = getClient) {
|
|
398
|
-
defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only
|
|
456
|
+
defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only; read rows with subgraphs_query.", {}, async () => {
|
|
399
457
|
const { data } = await clientProvider().subgraphs.list();
|
|
400
458
|
return jsonResponse(data.map(formatSubgraphSummary));
|
|
401
459
|
});
|
|
402
|
-
defineTool(server, "
|
|
403
|
-
const detail = await clientProvider().subgraphs.
|
|
460
|
+
defineTool(server, "subgraphs_status", "Get full details of a subgraph including schema, health, and table columns.", { name: z7.string().describe("Subgraph name") }, async ({ name }) => {
|
|
461
|
+
const detail = await clientProvider().subgraphs.status(name);
|
|
404
462
|
return jsonResponse(detail);
|
|
405
463
|
});
|
|
406
|
-
defineTool(server, "
|
|
407
|
-
name:
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
464
|
+
defineTool(server, "subgraphs_spec", "Fetch a subgraph's self-describing spec — the tables, columns, filters, and read URLs a consumer needs, without guessing. `agent` (default) returns the compact JSON schema built for tool use, `openapi` the OpenAPI document for the subgraph's read routes, `markdown` human-readable docs. Read it before writing queries against an unfamiliar subgraph.", {
|
|
465
|
+
name: z7.string().describe("Subgraph name"),
|
|
466
|
+
format: z7.enum(["agent", "openapi", "markdown"]).optional().describe("agent = JSON schema for tool use (default), openapi = OpenAPI JSON, markdown = docs.md")
|
|
467
|
+
}, async ({ name, format }) => {
|
|
468
|
+
const client = clientProvider();
|
|
469
|
+
if (format === "openapi")
|
|
470
|
+
return jsonResponse(await client.subgraphs.openapi(name));
|
|
471
|
+
if (format === "markdown")
|
|
472
|
+
return textResponse(await client.subgraphs.markdown(name));
|
|
473
|
+
return jsonResponse(await client.subgraphs.schema(name));
|
|
474
|
+
});
|
|
475
|
+
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 — when tailing with a `fields` list, include "_id" in it or the next poll filter cannot be formed. Fetch one row by id with {"_id": "<id>"}. The same rows are readable 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.', {
|
|
476
|
+
name: z7.string().describe("Subgraph name"),
|
|
477
|
+
table: z7.string().describe("Table name"),
|
|
478
|
+
filters: z7.record(z7.string(), z7.string()).optional().describe('Column filters — plain values or with operators (e.g. {"amount.gte": "1000", "sender": "SP..."})'),
|
|
479
|
+
sort: z7.string().optional().describe("Column to sort by"),
|
|
480
|
+
order: z7.enum(["asc", "desc"]).optional().describe("Sort order"),
|
|
481
|
+
limit: z7.number().max(200).optional().describe("Max rows (default 50, max 200)"),
|
|
482
|
+
offset: z7.number().optional().describe("Offset for pagination"),
|
|
483
|
+
fields: z7.string().optional().describe('Comma-separated column list to return (e.g. "sender,amount")'),
|
|
484
|
+
count: z7.boolean().optional().describe("If true, return row count instead of rows")
|
|
416
485
|
}, async ({
|
|
417
486
|
name,
|
|
418
487
|
table,
|
|
@@ -439,16 +508,16 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
439
508
|
const result = withCap(rows, Math.min(limit ?? 50, 200));
|
|
440
509
|
return jsonResponse(result);
|
|
441
510
|
});
|
|
442
|
-
defineTool(server, "subgraphs_reindex", "Reindex a subgraph: drops and rebuilds the whole subgraph from its start block to chain tip. Takes no block range — use subgraphs_backfill to process a specific range. Returns an operationId — check
|
|
443
|
-
name:
|
|
511
|
+
defineTool(server, "subgraphs_reindex", "Reindex a subgraph: drops and rebuilds the whole subgraph from its start block to chain tip. Takes no block range — use subgraphs_backfill to process a specific range. Returns an operationId — check subgraphs_status (health) or the REST operations endpoint to track progress to completion.", {
|
|
512
|
+
name: z7.string().describe("Subgraph name")
|
|
444
513
|
}, async ({ name }) => {
|
|
445
514
|
const result = await clientProvider().subgraphs.reindex(name);
|
|
446
515
|
return jsonResponse(result);
|
|
447
516
|
});
|
|
448
|
-
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
|
|
449
|
-
name:
|
|
450
|
-
fromBlock:
|
|
451
|
-
toBlock:
|
|
517
|
+
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_status (health) or the REST operations endpoint to track progress.", {
|
|
518
|
+
name: z7.string().describe("Subgraph name"),
|
|
519
|
+
fromBlock: z7.number().int().nonnegative().describe("Start block (inclusive)"),
|
|
520
|
+
toBlock: z7.number().int().nonnegative().describe("End block (inclusive)")
|
|
452
521
|
}, async ({ name, fromBlock, toBlock }) => {
|
|
453
522
|
const result = await clientProvider().subgraphs.backfill(name, {
|
|
454
523
|
fromBlock,
|
|
@@ -456,15 +525,15 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
456
525
|
});
|
|
457
526
|
return jsonResponse(result);
|
|
458
527
|
});
|
|
459
|
-
defineTool(server, "subgraphs_stop", "Cancel an in-flight reindex or backfill operation for a subgraph. Returns the stop request status; check
|
|
528
|
+
defineTool(server, "subgraphs_stop", "Cancel an in-flight reindex or backfill operation for a subgraph. Returns the stop request status; check subgraphs_status (health) or the REST operations endpoint to confirm it reaches a terminal state.", { name: z7.string().describe("Subgraph name") }, async ({ name }) => {
|
|
460
529
|
const result = await clientProvider().subgraphs.stop(name);
|
|
461
530
|
return jsonResponse(result);
|
|
462
531
|
});
|
|
463
532
|
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.", {
|
|
464
|
-
name:
|
|
465
|
-
limit:
|
|
466
|
-
offset:
|
|
467
|
-
resolved:
|
|
533
|
+
name: z7.string().describe("Subgraph name"),
|
|
534
|
+
limit: z7.number().int().positive().optional().describe("Max gaps to return"),
|
|
535
|
+
offset: z7.number().int().nonnegative().optional().describe("Pagination offset"),
|
|
536
|
+
resolved: z7.boolean().optional().describe("Filter by resolved state (omit for unresolved only)")
|
|
468
537
|
}, async ({ name, limit, offset, resolved }) => {
|
|
469
538
|
const result = await clientProvider().subgraphs.gaps(name, {
|
|
470
539
|
limit,
|
|
@@ -473,14 +542,21 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
473
542
|
});
|
|
474
543
|
return jsonResponse(result);
|
|
475
544
|
});
|
|
476
|
-
defineTool(server, "
|
|
545
|
+
defineTool(server, "subgraphs_operations", "Operation history for a subgraph (deploy/reindex/backfill/stop), newest first — status, block range, progress, and error. THIS IS THE VERIFY CALL: after subgraphs_deploy, subgraphs_reindex, subgraphs_backfill, or subgraphs_stop, poll it with the returned operationId until `status` is terminal (completed/failed/cancelled) before reporting success. Omit operationId for the recent history.", {
|
|
546
|
+
name: z7.string().describe("Subgraph name"),
|
|
547
|
+
operationId: z7.string().optional().describe("Fetch one operation by id (the operationId returned by reindex/backfill/stop); omit for recent history")
|
|
548
|
+
}, async ({ name, operationId }) => {
|
|
549
|
+
const client = clientProvider();
|
|
550
|
+
return jsonResponse(operationId ? await client.subgraphs.getOperation(name, operationId) : await client.subgraphs.operations(name));
|
|
551
|
+
});
|
|
552
|
+
defineTool(server, "subgraphs_delete", "Delete a subgraph permanently.", { name: z7.string().describe("Subgraph name") }, async ({ name }) => {
|
|
477
553
|
const result = await clientProvider().subgraphs.delete(name);
|
|
478
554
|
return textResponse(result.message);
|
|
479
555
|
});
|
|
480
556
|
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. Call `subgraphs_reindex` separately if you need a forced reindex.", {
|
|
481
|
-
code:
|
|
482
|
-
startBlock:
|
|
483
|
-
dryRun:
|
|
557
|
+
code: z7.string().describe("TypeScript source code containing a defineSubgraph() call"),
|
|
558
|
+
startBlock: z7.number().int().nonnegative().optional().describe("Override the definition startBlock for this deploy"),
|
|
559
|
+
dryRun: z7.boolean().optional().describe("Validate and preview the deploy (schema/DDL) without writing changes")
|
|
484
560
|
}, async ({ code, startBlock, dryRun }) => {
|
|
485
561
|
const bundled = await bundleSubgraphCode(code);
|
|
486
562
|
const result = await clientProvider().subgraphs.deploy({
|
|
@@ -500,38 +576,38 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
500
576
|
|
|
501
577
|
// src/tools/subscriptions.ts
|
|
502
578
|
import { CHAIN_TRIGGER_TYPES } from "@secondlayer/shared";
|
|
503
|
-
import { z as
|
|
579
|
+
import { z as z8 } from "zod";
|
|
504
580
|
function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
505
581
|
defineTool(server, "subscriptions_list", "List all subscriptions for the current account. Returns summary fields (no secrets).", {}, async () => {
|
|
506
582
|
const { data } = await clientProvider().subscriptions.list();
|
|
507
583
|
return jsonResponse(data);
|
|
508
584
|
});
|
|
509
|
-
defineTool(server, "subscriptions_get", "Get full detail for a subscription (filter, auth, retry config, circuit state).", { id:
|
|
585
|
+
defineTool(server, "subscriptions_get", "Get full detail for a subscription (filter, auth, retry config, circuit state).", { id: z8.string().describe("Subscription id") }, async ({ id }) => {
|
|
510
586
|
const detail = await clientProvider().subscriptions.get(id);
|
|
511
587
|
return jsonResponse(detail);
|
|
512
588
|
});
|
|
513
589
|
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.", {
|
|
514
|
-
name:
|
|
515
|
-
subgraphName:
|
|
516
|
-
tableName:
|
|
517
|
-
triggers:
|
|
518
|
-
type:
|
|
519
|
-
contractId:
|
|
520
|
-
functionName:
|
|
521
|
-
caller:
|
|
522
|
-
sender:
|
|
523
|
-
recipient:
|
|
524
|
-
assetIdentifier:
|
|
525
|
-
deployer:
|
|
526
|
-
contractName:
|
|
527
|
-
topic:
|
|
528
|
-
lockedAddress:
|
|
529
|
-
trait:
|
|
530
|
-
minAmount:
|
|
531
|
-
maxAmount:
|
|
590
|
+
name: z8.string().describe("Human-readable name, unique per account"),
|
|
591
|
+
subgraphName: z8.string().optional().describe("Subgraph to subscribe to (subgraph subscription)"),
|
|
592
|
+
tableName: z8.string().optional().describe("Table within the subgraph (subgraph subscription)"),
|
|
593
|
+
triggers: z8.array(z8.object({
|
|
594
|
+
type: z8.enum(CHAIN_TRIGGER_TYPES),
|
|
595
|
+
contractId: z8.string().optional(),
|
|
596
|
+
functionName: z8.string().optional(),
|
|
597
|
+
caller: z8.string().optional(),
|
|
598
|
+
sender: z8.string().optional(),
|
|
599
|
+
recipient: z8.string().optional(),
|
|
600
|
+
assetIdentifier: z8.string().optional(),
|
|
601
|
+
deployer: z8.string().optional(),
|
|
602
|
+
contractName: z8.string().optional(),
|
|
603
|
+
topic: z8.string().optional(),
|
|
604
|
+
lockedAddress: z8.string().optional(),
|
|
605
|
+
trait: z8.string().optional(),
|
|
606
|
+
minAmount: z8.union([z8.string(), z8.number()]).optional(),
|
|
607
|
+
maxAmount: z8.union([z8.string(), z8.number()]).optional()
|
|
532
608
|
})).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."),
|
|
533
|
-
url:
|
|
534
|
-
format:
|
|
609
|
+
url: z8.string().describe("Webhook URL"),
|
|
610
|
+
format: z8.enum([
|
|
535
611
|
"standard-webhooks",
|
|
536
612
|
"inngest",
|
|
537
613
|
"trigger",
|
|
@@ -539,20 +615,20 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
539
615
|
"cloudevents",
|
|
540
616
|
"raw"
|
|
541
617
|
]).optional().describe("Wire format (default standard-webhooks)"),
|
|
542
|
-
runtime:
|
|
543
|
-
filter:
|
|
544
|
-
authConfig:
|
|
618
|
+
runtime: z8.enum(["inngest", "trigger", "cloudflare", "node"]).optional().describe("Receiver runtime label (display only)"),
|
|
619
|
+
filter: z8.record(z8.string(), z8.unknown()).optional().describe('Scalar filter DSL, e.g. {"amount": {"gte": 100}, "sender": "SP..."}'),
|
|
620
|
+
authConfig: z8.record(z8.string(), z8.unknown()).optional().describe('Receiver auth sent with each delivery, e.g. {"type": "bearer", "token": "..."}')
|
|
545
621
|
}, async (input) => {
|
|
546
622
|
const res = await clientProvider().subscriptions.create(input);
|
|
547
623
|
return jsonResponse(res);
|
|
548
624
|
});
|
|
549
625
|
defineTool(server, "subscriptions_update", "Patch a subscription (name, url, filter, authConfig, format, runtime, retry, timeout, concurrency).", {
|
|
550
|
-
id:
|
|
551
|
-
name:
|
|
552
|
-
url:
|
|
553
|
-
filter:
|
|
554
|
-
authConfig:
|
|
555
|
-
format:
|
|
626
|
+
id: z8.string(),
|
|
627
|
+
name: z8.string().optional().describe("Rename the subscription"),
|
|
628
|
+
url: z8.string().optional(),
|
|
629
|
+
filter: z8.record(z8.string(), z8.unknown()).optional(),
|
|
630
|
+
authConfig: z8.record(z8.string(), z8.unknown()).optional().describe("Receiver auth sent with each delivery (bearer/etc.)"),
|
|
631
|
+
format: z8.enum([
|
|
556
632
|
"standard-webhooks",
|
|
557
633
|
"inngest",
|
|
558
634
|
"trigger",
|
|
@@ -560,27 +636,36 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
560
636
|
"cloudevents",
|
|
561
637
|
"raw"
|
|
562
638
|
]).optional(),
|
|
563
|
-
runtime:
|
|
564
|
-
maxRetries:
|
|
565
|
-
timeoutMs:
|
|
566
|
-
concurrency:
|
|
639
|
+
runtime: z8.enum(["inngest", "trigger", "cloudflare", "node"]).nullable().optional(),
|
|
640
|
+
maxRetries: z8.number().int().min(0).optional(),
|
|
641
|
+
timeoutMs: z8.number().int().min(100).optional(),
|
|
642
|
+
concurrency: z8.number().int().min(1).optional()
|
|
567
643
|
}, async ({ id, ...patch }) => {
|
|
568
644
|
const res = await clientProvider().subscriptions.update(id, patch);
|
|
569
645
|
return jsonResponse(res);
|
|
570
646
|
});
|
|
571
|
-
defineTool(server, "subscriptions_delete", "Delete a subscription. Pending outbox rows are cascade-deleted.", { id:
|
|
647
|
+
defineTool(server, "subscriptions_delete", "Delete a subscription. Pending outbox rows are cascade-deleted.", { id: z8.string() }, async ({ id }) => {
|
|
572
648
|
const res = await clientProvider().subscriptions.delete(id);
|
|
573
649
|
return jsonResponse(res);
|
|
574
650
|
});
|
|
575
|
-
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:
|
|
651
|
+
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: z8.string().describe("Subscription id") }, async ({ id }) => {
|
|
576
652
|
const res = await clientProvider().subscriptions.test(id);
|
|
577
653
|
return jsonResponse(res);
|
|
578
654
|
});
|
|
655
|
+
defineTool(server, "subscriptions_pause", "Pause a subscription: stops delivery attempts and stops queueing new ones. Nothing is deleted — resume picks the subscription back up from the tip. Verify with subscriptions_get (status).", { id: z8.string().describe("Subscription id") }, async ({ id }) => jsonResponse(await clientProvider().subscriptions.pause(id)));
|
|
656
|
+
defineTool(server, "subscriptions_resume", "Resume a paused subscription (also clears a tripped circuit breaker). Deliveries restart from the current tip — use subscriptions_replay for the blocks missed while paused. Verify with subscriptions_get (status).", { id: z8.string().describe("Subscription id") }, async ({ id }) => jsonResponse(await clientProvider().subscriptions.resume(id)));
|
|
657
|
+
defineTool(server, "subscriptions_rotate_secret", "Rotate a subscription's webhook signing secret. Returns the NEW `signingSecret` ONCE — forward it to the user; deliveries signed with the old secret stop verifying as soon as this returns, so the receiver must be updated.", { id: z8.string().describe("Subscription id") }, async ({ id }) => jsonResponse(await clientProvider().subscriptions.rotateSecret(id)));
|
|
658
|
+
defineTool(server, "subscriptions_deliveries", "List recent delivery attempts for a subscription — status code, attempt count, error, duration, timestamps. THIS IS THE VERIFY CALL: after subscriptions_create, subscriptions_test, or subscriptions_replay, read it to confirm the webhook actually landed (and to see the receiver's response when it didn't) instead of trusting the enqueue result.", { id: z8.string().describe("Subscription id") }, async ({ id }) => jsonResponse(await clientProvider().subscriptions.deliveries(id)));
|
|
659
|
+
defineTool(server, "subscriptions_dead", "List a subscription's dead-letter queue — deliveries that exhausted their retries, with the outbox id, payload, and last error. Diagnose the receiver first (subscriptions_get for the URL/auth, subscriptions_test for a live probe), then requeue rows with subscriptions_requeue.", { id: z8.string().describe("Subscription id") }, async ({ id }) => jsonResponse(await clientProvider().subscriptions.dead(id)));
|
|
660
|
+
defineTool(server, "subscriptions_requeue", "Requeue ONE dead-lettered delivery for another attempt, by its outbox id from subscriptions_dead. Fix the receiver first — a requeue against a still-broken endpoint just dies again. Verify with subscriptions_deliveries.", {
|
|
661
|
+
id: z8.string().describe("Subscription id"),
|
|
662
|
+
outboxId: z8.string().describe("Outbox id of the dead delivery (from subscriptions_dead)")
|
|
663
|
+
}, async ({ id, outboxId }) => jsonResponse(await clientProvider().subscriptions.requeue(id, outboxId)));
|
|
579
664
|
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.", {
|
|
580
|
-
id:
|
|
581
|
-
fromBlock:
|
|
582
|
-
toBlock:
|
|
583
|
-
force:
|
|
665
|
+
id: z8.string(),
|
|
666
|
+
fromBlock: z8.number().int().nonnegative(),
|
|
667
|
+
toBlock: z8.number().int().nonnegative(),
|
|
668
|
+
force: z8.string().optional().describe("Idempotency suffix to force a duplicate replay of the range")
|
|
584
669
|
}, async ({ id, fromBlock, toBlock, force }) => {
|
|
585
670
|
const res = await clientProvider().subscriptions.replay(id, {
|
|
586
671
|
fromBlock,
|
|
@@ -606,6 +691,7 @@ function createServer() {
|
|
|
606
691
|
registerStreamsTools(server);
|
|
607
692
|
registerContractTools(server);
|
|
608
693
|
registerCodegenTools(server);
|
|
694
|
+
registerAccountTools(server);
|
|
609
695
|
registerResources(server);
|
|
610
696
|
return server;
|
|
611
697
|
}
|
|
@@ -615,5 +701,5 @@ var server = createServer();
|
|
|
615
701
|
var transport = new StdioServerTransport;
|
|
616
702
|
await server.connect(transport);
|
|
617
703
|
|
|
618
|
-
//# debugId=
|
|
704
|
+
//# debugId=08F9439BE660189C64756E2164756E21
|
|
619
705
|
//# sourceMappingURL=bin.js.map
|