@secondlayer/mcp 7.4.0 → 7.5.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 +9 -9
- package/dist/bin-http.js +345 -45
- package/dist/bin-http.js.map +8 -8
- package/dist/bin.js +345 -45
- package/dist/bin.js.map +8 -8
- package/dist/index.js +345 -45
- package/dist/index.js.map +8 -8
- package/package.json +7 -7
package/dist/bin.js
CHANGED
|
@@ -5,7 +5,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
5
5
|
|
|
6
6
|
// src/server.ts
|
|
7
7
|
import { readFileSync } from "node:fs";
|
|
8
|
-
import { dirname, join } from "node:path";
|
|
8
|
+
import { dirname, join as join2 } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11
11
|
|
|
@@ -20,10 +20,13 @@ function isHostedArchiveUrl(url) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// src/resources.ts
|
|
23
|
+
import {
|
|
24
|
+
ResourceTemplate
|
|
25
|
+
} from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
23
26
|
import { CHAIN_TRIGGER_FIELDS } from "@secondlayer/shared";
|
|
24
27
|
import { BOOTSTRAP_STEP } from "@secondlayer/shared/archive/instance-diagnosis";
|
|
25
28
|
import { TRAIT_STANDARDS } from "@secondlayer/stacks/clarity";
|
|
26
|
-
import { filterFieldsByType } from "@secondlayer/subgraphs";
|
|
29
|
+
import { camelizeKeys, filterFieldsByType } from "@secondlayer/subgraphs";
|
|
27
30
|
import { TYPE_MAP } from "@secondlayer/subgraphs/schema";
|
|
28
31
|
|
|
29
32
|
// src/lib/client.ts
|
|
@@ -184,7 +187,8 @@ var PRODUCT_BLURBS = {
|
|
|
184
187
|
streams: "raw chain event firehose — cursor-paginated reads, tip/canonical/reorg checks, bulk parquet dumps",
|
|
185
188
|
contracts: "trait-based contract discovery and ABIs",
|
|
186
189
|
subgraphs: "author/scaffold/deploy/query custom indexes",
|
|
187
|
-
|
|
190
|
+
webhooks: "a signed POST to a URL you run, on subgraph rows or raw chain events",
|
|
191
|
+
subscriptions: "a signed POST to a URL you run, on subgraph rows or raw chain events",
|
|
188
192
|
account: "identity and self-provisioned API keys",
|
|
189
193
|
codegen: "ORM schemas for the tables you read",
|
|
190
194
|
instance: "decoder health and empty-index diagnosis",
|
|
@@ -197,6 +201,7 @@ var PRODUCT_ORDER = [
|
|
|
197
201
|
"streams",
|
|
198
202
|
"contracts",
|
|
199
203
|
"subgraphs",
|
|
204
|
+
"webhooks",
|
|
200
205
|
"subscriptions",
|
|
201
206
|
"account",
|
|
202
207
|
"codegen",
|
|
@@ -253,7 +258,8 @@ async function buildContext(deps = { clientProvider: getClient }) {
|
|
|
253
258
|
streamsTip: orNull(snap?.streamsTip),
|
|
254
259
|
indexTip: orNull(snap?.indexTip),
|
|
255
260
|
subgraphs: snap?.subgraphs.value ? snap.subgraphs.value.map(formatSubgraphSummary) : orNull(snap?.subgraphs),
|
|
256
|
-
|
|
261
|
+
webhooks: orNull(snap?.webhooks),
|
|
262
|
+
subscriptions: orNull(snap?.webhooks),
|
|
257
263
|
activeOperations: orNull(snap?.activeOperations),
|
|
258
264
|
instance: instance2
|
|
259
265
|
},
|
|
@@ -264,7 +270,7 @@ async function buildContext(deps = { clientProvider: getClient }) {
|
|
|
264
270
|
}
|
|
265
271
|
function registerResources(server) {
|
|
266
272
|
server.resource("context", "secondlayer://context", {
|
|
267
|
-
description: "Live agent context — what exists (your subgraphs,
|
|
273
|
+
description: "Live agent context — what exists (your subgraphs, webhooks, account), what you can do, and read-auth tiers. Read this first."
|
|
268
274
|
}, async () => ({
|
|
269
275
|
contents: [
|
|
270
276
|
{
|
|
@@ -309,7 +315,7 @@ function registerResources(server) {
|
|
|
309
315
|
]
|
|
310
316
|
}));
|
|
311
317
|
server.resource("chain-triggers", "secondlayer://chain-triggers", {
|
|
312
|
-
description: "Chain-
|
|
318
|
+
description: "Chain-webhook trigger types and the filter fields each accepts (for webhooks_create triggers)."
|
|
313
319
|
}, async () => ({
|
|
314
320
|
contents: [
|
|
315
321
|
{
|
|
@@ -319,6 +325,73 @@ function registerResources(server) {
|
|
|
319
325
|
}
|
|
320
326
|
]
|
|
321
327
|
}));
|
|
328
|
+
server.resource("samples", new ResourceTemplate("secondlayer://samples/{contractId}", {
|
|
329
|
+
list: undefined
|
|
330
|
+
}), {
|
|
331
|
+
description: "Decoded camelCase print samples for a contract (schema + up to 5 events). Use before writing a print_event handler."
|
|
332
|
+
}, async (uri, variables) => {
|
|
333
|
+
const contractId = String(Array.isArray(variables.contractId) ? variables.contractId[0] : variables.contractId ?? "");
|
|
334
|
+
const body = await buildSamplesResource(contractId);
|
|
335
|
+
return {
|
|
336
|
+
contents: [
|
|
337
|
+
{
|
|
338
|
+
uri: uri.href,
|
|
339
|
+
mimeType: "application/json",
|
|
340
|
+
text: JSON.stringify(body, null, 2)
|
|
341
|
+
}
|
|
342
|
+
]
|
|
343
|
+
};
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
function jsonSafe(value) {
|
|
347
|
+
if (typeof value === "bigint")
|
|
348
|
+
return value.toString();
|
|
349
|
+
if (value === null || value === undefined)
|
|
350
|
+
return value;
|
|
351
|
+
if (Array.isArray(value))
|
|
352
|
+
return value.map(jsonSafe);
|
|
353
|
+
if (typeof value === "object") {
|
|
354
|
+
const out = {};
|
|
355
|
+
for (const [k, v] of Object.entries(value)) {
|
|
356
|
+
out[k] = jsonSafe(v);
|
|
357
|
+
}
|
|
358
|
+
return out;
|
|
359
|
+
}
|
|
360
|
+
return value;
|
|
361
|
+
}
|
|
362
|
+
async function buildSamplesResource(contractId, clientProvider = getClient) {
|
|
363
|
+
const client = clientProvider();
|
|
364
|
+
const printSchema = await client.index.printSchema(contractId);
|
|
365
|
+
const envelope = await client.index.events.list({
|
|
366
|
+
eventType: "print",
|
|
367
|
+
contractId,
|
|
368
|
+
limit: 5
|
|
369
|
+
});
|
|
370
|
+
const schema = printSchema?.topics.map((t) => ({
|
|
371
|
+
topic: t.topic,
|
|
372
|
+
fields: t.fields.map((f) => ({
|
|
373
|
+
camel: f.camel_name,
|
|
374
|
+
snake: f.name.replace(/-/g, "_"),
|
|
375
|
+
column_type: f.column_type,
|
|
376
|
+
always: f.always_present
|
|
377
|
+
}))
|
|
378
|
+
})) ?? [];
|
|
379
|
+
const samples = (envelope.events ?? []).slice(0, 5).map((ev) => {
|
|
380
|
+
const payload = ev.payload;
|
|
381
|
+
const data = jsonSafe(camelizeKeys(payload?.value));
|
|
382
|
+
return {
|
|
383
|
+
txId: ev.tx_id,
|
|
384
|
+
height: ev.block_height,
|
|
385
|
+
topic: payload?.topic ?? "",
|
|
386
|
+
data: data ?? {}
|
|
387
|
+
};
|
|
388
|
+
});
|
|
389
|
+
return {
|
|
390
|
+
contractId,
|
|
391
|
+
handlerShape: "event.data is camelCase; columns are snake_case",
|
|
392
|
+
schema,
|
|
393
|
+
samples
|
|
394
|
+
};
|
|
322
395
|
}
|
|
323
396
|
|
|
324
397
|
// src/tools/account.ts
|
|
@@ -477,7 +550,11 @@ function registerInstanceTools(server, clientProvider = getClient) {
|
|
|
477
550
|
}
|
|
478
551
|
|
|
479
552
|
// src/tools/scaffold.ts
|
|
480
|
-
import {
|
|
553
|
+
import {
|
|
554
|
+
generatePrintSchemaSubgraph,
|
|
555
|
+
generateSubgraphCode,
|
|
556
|
+
generateTokenSubgraphFromAbi
|
|
557
|
+
} from "@secondlayer/scaffold";
|
|
481
558
|
import { z as z6 } from "zod";
|
|
482
559
|
async function fetchAbi(clientProvider, contractId) {
|
|
483
560
|
const contract = await clientProvider().contracts.get(contractId, {
|
|
@@ -489,14 +566,32 @@ async function fetchAbi(clientProvider, contractId) {
|
|
|
489
566
|
if (!abi) {
|
|
490
567
|
throw new Error(`No ABI available for ${contractId} (abi_status: ${contract.abi_status})`);
|
|
491
568
|
}
|
|
492
|
-
return { functions: abi.functions ?? [] };
|
|
569
|
+
return { functions: abi.functions ?? [], abi };
|
|
493
570
|
}
|
|
494
571
|
function registerScaffoldTools(server, clientProvider = getClient) {
|
|
495
|
-
defineTool(server, "subgraphs_scaffold", "Generate a subgraph
|
|
572
|
+
defineTool(server, "subgraphs_scaffold", "Generate a subgraph from observed print events when available; ABI token transfers or public functions only when the contract has never printed. Review the TypeScript, run subgraphs_test until ok: true with written >= 1, read skipped, then subgraphs_deploy. dryRun is DDL only.", {
|
|
496
573
|
contractId: z6.string().describe("Fully qualified contract ID (e.g. SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01)"),
|
|
497
574
|
subgraphName: z6.string().optional().describe("Override the subgraph name (defaults to contract name)")
|
|
498
575
|
}, async ({ contractId, subgraphName }) => {
|
|
499
|
-
const
|
|
576
|
+
const printSchema = await clientProvider().index.printSchema(contractId);
|
|
577
|
+
if (printSchema && printSchema.topics.length > 0) {
|
|
578
|
+
const code2 = generatePrintSchemaSubgraph({
|
|
579
|
+
contractId,
|
|
580
|
+
name: subgraphName,
|
|
581
|
+
topics: printSchema.topics,
|
|
582
|
+
sample: printSchema.sample
|
|
583
|
+
});
|
|
584
|
+
return { content: [{ type: "text", text: code2 }] };
|
|
585
|
+
}
|
|
586
|
+
const { functions, abi } = await fetchAbi(clientProvider, contractId);
|
|
587
|
+
const token = generateTokenSubgraphFromAbi({
|
|
588
|
+
contractId,
|
|
589
|
+
abi,
|
|
590
|
+
name: subgraphName
|
|
591
|
+
});
|
|
592
|
+
if (token) {
|
|
593
|
+
return { content: [{ type: "text", text: token }] };
|
|
594
|
+
}
|
|
500
595
|
const code = generateSubgraphCode(contractId, functions, subgraphName);
|
|
501
596
|
return { content: [{ type: "text", text: code }] };
|
|
502
597
|
});
|
|
@@ -749,14 +844,42 @@ function registerStreamsTools(server, clientProvider = getClient) {
|
|
|
749
844
|
}
|
|
750
845
|
|
|
751
846
|
// src/tools/subgraphs.ts
|
|
847
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
848
|
+
import { tmpdir } from "node:os";
|
|
849
|
+
import { join } from "node:path";
|
|
850
|
+
import { pathToFileURL } from "node:url";
|
|
752
851
|
import { bundleSubgraphCode } from "@secondlayer/bundler";
|
|
852
|
+
import { DECODED_EVENT_TYPES as DECODED_EVENT_TYPES2 } from "@secondlayer/shared";
|
|
853
|
+
import {
|
|
854
|
+
runSubgraphTest
|
|
855
|
+
} from "@secondlayer/subgraphs/testing";
|
|
753
856
|
import { z as z9 } from "zod";
|
|
857
|
+
function eventTypeFor(filter) {
|
|
858
|
+
if (filter.type === "contract_deploy")
|
|
859
|
+
return null;
|
|
860
|
+
const candidate = filter.type === "print_event" ? "print" : filter.type;
|
|
861
|
+
return DECODED_EVENT_TYPES2.includes(candidate) ? candidate : null;
|
|
862
|
+
}
|
|
863
|
+
function skipReason(name, filter) {
|
|
864
|
+
return filter.type === "contract_deploy" ? `source "${name}" (contract_deploy) has no Index list endpoint — skipped.` : `source "${name}" (${filter.type}) is not readable from Index — skipped.`;
|
|
865
|
+
}
|
|
866
|
+
async function loadBundledDefinition(handlerCode) {
|
|
867
|
+
const dir = await mkdtemp(join(tmpdir(), "sl-mcp-sg-test-"));
|
|
868
|
+
try {
|
|
869
|
+
const file = join(dir, `handler-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`);
|
|
870
|
+
await writeFile(file, handlerCode);
|
|
871
|
+
const mod = await import(pathToFileURL(file).href);
|
|
872
|
+
return mod.default ?? {};
|
|
873
|
+
} finally {
|
|
874
|
+
await rm(dir, { recursive: true, force: true });
|
|
875
|
+
}
|
|
876
|
+
}
|
|
754
877
|
function registerSubgraphTools(server, clientProvider = getClient) {
|
|
755
878
|
defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only; read rows with subgraphs_query.", {}, async () => {
|
|
756
879
|
const { data } = await clientProvider().subgraphs.list();
|
|
757
880
|
return jsonResponse(data.map(formatSubgraphSummary));
|
|
758
881
|
});
|
|
759
|
-
defineTool(server, "subgraphs_status", "Get full details of a subgraph including schema, health, and table columns.", { name: z9.string().describe("Subgraph name") }, async ({ name }) => {
|
|
882
|
+
defineTool(server, "subgraphs_status", "Get full details of a subgraph including schema, health (incl. health.emptyMapping when processed events wrote 0 rows), and table columns.", { name: z9.string().describe("Subgraph name") }, async ({ name }) => {
|
|
760
883
|
const detail = await clientProvider().subgraphs.status(name);
|
|
761
884
|
return jsonResponse(detail);
|
|
762
885
|
});
|
|
@@ -852,10 +975,10 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
852
975
|
const result = await clientProvider().subgraphs.delete(name);
|
|
853
976
|
return textResponse(result.message);
|
|
854
977
|
});
|
|
855
|
-
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.
|
|
978
|
+
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. dryRun validates schema/DDL only and does not run handlers — call subgraphs_test first and deploy only when it returns ok: true with written >= 1. Call subgraphs_reindex separately if you need a forced reindex.", {
|
|
856
979
|
code: z9.string().describe("TypeScript source code containing a defineSubgraph() call"),
|
|
857
980
|
startBlock: z9.number().int().nonnegative().optional().describe("Override the definition startBlock for this deploy"),
|
|
858
|
-
dryRun: z9.boolean().optional().describe("Validate
|
|
981
|
+
dryRun: z9.boolean().optional().describe("Validate schema/DDL only — does not run handlers. Use subgraphs_test to prove a mapping writes rows.")
|
|
859
982
|
}, async ({ code, startBlock, dryRun }) => {
|
|
860
983
|
const bundled = await bundleSubgraphCode(code);
|
|
861
984
|
const result = await clientProvider().subgraphs.deploy({
|
|
@@ -871,24 +994,201 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
871
994
|
});
|
|
872
995
|
return jsonResponse(result);
|
|
873
996
|
});
|
|
997
|
+
defineTool(server, "subgraphs_test", "Prove a subgraph mapping writes rows against Index data before deploy. Pass full defineSubgraph() source plus a height range. Returns ok/written/tables; EMPTY_MAPPING means events matched but 0 rows were written — read hint for observed event.data keys and do not invent fields. subgraphs_deploy dryRun is DDL only; do not deploy until ok: true with written >= 1.", {
|
|
998
|
+
code: z9.string().describe("TypeScript source containing a defineSubgraph() call"),
|
|
999
|
+
fromHeight: z9.number().int().nonnegative().describe("Start block height (inclusive)"),
|
|
1000
|
+
toHeight: z9.number().int().nonnegative().optional().describe("End block height (inclusive); default fromHeight+100"),
|
|
1001
|
+
limit: z9.number().int().positive().max(200).optional().describe("Max Index rows per source (default 50, max 200)")
|
|
1002
|
+
}, async ({ code, fromHeight, toHeight, limit }) => {
|
|
1003
|
+
const bundled = await bundleSubgraphCode(code);
|
|
1004
|
+
const def = await loadBundledDefinition(bundled.handlerCode);
|
|
1005
|
+
if (!def.handlers || !def.schema) {
|
|
1006
|
+
return jsonResponse({
|
|
1007
|
+
ok: false,
|
|
1008
|
+
code: "NO_SOURCES",
|
|
1009
|
+
matched: 0,
|
|
1010
|
+
written: 0,
|
|
1011
|
+
tables: [],
|
|
1012
|
+
hint: "Bundled module must default-export defineSubgraph() with handlers and schema."
|
|
1013
|
+
}, true);
|
|
1014
|
+
}
|
|
1015
|
+
const sources = bundled.sources ?? {};
|
|
1016
|
+
const to = toHeight ?? fromHeight + 100;
|
|
1017
|
+
const pageLimit = Math.min(limit ?? 50, 200);
|
|
1018
|
+
const index = clientProvider().index;
|
|
1019
|
+
const events = {};
|
|
1020
|
+
const skipped = [];
|
|
1021
|
+
let sourcesTested = 0;
|
|
1022
|
+
for (const [name, filter] of Object.entries(sources)) {
|
|
1023
|
+
if (filter.type === "contract_call") {
|
|
1024
|
+
sourcesTested++;
|
|
1025
|
+
const envelope2 = await index.contractCalls.list({
|
|
1026
|
+
...filter.contractId ? {
|
|
1027
|
+
contractId: Array.isArray(filter.contractId) ? filter.contractId : filter.contractId
|
|
1028
|
+
} : {},
|
|
1029
|
+
...filter.functionName ? { functionName: filter.functionName } : {},
|
|
1030
|
+
fromHeight,
|
|
1031
|
+
toHeight: to,
|
|
1032
|
+
limit: pageLimit
|
|
1033
|
+
});
|
|
1034
|
+
events[name] = envelope2.contract_calls;
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
const eventType = eventTypeFor(filter);
|
|
1038
|
+
if (eventType === null) {
|
|
1039
|
+
skipped.push({ source: name, reason: skipReason(name, filter) });
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
1042
|
+
sourcesTested++;
|
|
1043
|
+
const envelope = await index.events.list({
|
|
1044
|
+
eventType,
|
|
1045
|
+
...filter.contractId ? {
|
|
1046
|
+
contractId: Array.isArray(filter.contractId) ? filter.contractId : filter.contractId
|
|
1047
|
+
} : {},
|
|
1048
|
+
fromHeight,
|
|
1049
|
+
toHeight: to,
|
|
1050
|
+
limit: pageLimit
|
|
1051
|
+
});
|
|
1052
|
+
events[name] = envelope.events;
|
|
1053
|
+
}
|
|
1054
|
+
if (sourcesTested === 0) {
|
|
1055
|
+
return jsonResponse({
|
|
1056
|
+
ok: false,
|
|
1057
|
+
code: "NO_SOURCES",
|
|
1058
|
+
matched: 0,
|
|
1059
|
+
written: 0,
|
|
1060
|
+
tables: [],
|
|
1061
|
+
hint: "No sources were tested — every source is unreadable from Index.",
|
|
1062
|
+
skipped
|
|
1063
|
+
}, true);
|
|
1064
|
+
}
|
|
1065
|
+
const result = await runSubgraphTest({
|
|
1066
|
+
schema: def.schema,
|
|
1067
|
+
handlers: def.handlers,
|
|
1068
|
+
sources,
|
|
1069
|
+
events
|
|
1070
|
+
});
|
|
1071
|
+
const body = { ...result, skipped };
|
|
1072
|
+
if (skipped.length > 0) {
|
|
1073
|
+
const skipHints = skipped.map((s) => s.reason).join(" ");
|
|
1074
|
+
body.hint = result.hint ? `${result.hint} ${skipHints}` : skipHints;
|
|
1075
|
+
}
|
|
1076
|
+
return jsonResponse(body, !result.ok);
|
|
1077
|
+
});
|
|
874
1078
|
}
|
|
875
1079
|
|
|
876
|
-
// src/tools/
|
|
1080
|
+
// src/tools/webhooks.ts
|
|
877
1081
|
import { CHAIN_TRIGGER_TYPES } from "@secondlayer/shared";
|
|
878
1082
|
import { z as z10 } from "zod";
|
|
879
|
-
function
|
|
880
|
-
defineTool(server, "
|
|
881
|
-
const { data } = await clientProvider().
|
|
1083
|
+
function registerWebhookTools(server, clientProvider = getClient) {
|
|
1084
|
+
defineTool(server, "webhooks_list", "List all webhooks for the current account. Returns summary fields (no secrets).", {}, async () => {
|
|
1085
|
+
const { data } = await clientProvider().webhooks.list();
|
|
1086
|
+
return jsonResponse(data);
|
|
1087
|
+
});
|
|
1088
|
+
defineTool(server, "webhooks_get", "Get full detail for a webhook (filter, auth, retry config, circuit state).", { id: z10.string().describe("Webhook id") }, async ({ id }) => {
|
|
1089
|
+
const detail = await clientProvider().webhooks.get(id);
|
|
1090
|
+
return jsonResponse(detail);
|
|
1091
|
+
});
|
|
1092
|
+
defineTool(server, "webhooks_create", "Create a webhook. Two kinds (mutually exclusive): a SUBGRAPH webhook fires on a subgraph table's rows (set subgraphName + tableName + optional filter); a CHAIN webhook 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.", {
|
|
1093
|
+
name: z10.string().describe("Human-readable name, unique per account"),
|
|
1094
|
+
subgraphName: z10.string().optional().describe("Subgraph to subscribe to (subgraph webhook)"),
|
|
1095
|
+
tableName: z10.string().optional().describe("Table within the subgraph (subgraph webhook)"),
|
|
1096
|
+
triggers: z10.array(z10.object({
|
|
1097
|
+
type: z10.enum(CHAIN_TRIGGER_TYPES),
|
|
1098
|
+
contractId: z10.string().optional(),
|
|
1099
|
+
functionName: z10.string().optional(),
|
|
1100
|
+
caller: z10.string().optional(),
|
|
1101
|
+
sender: z10.string().optional(),
|
|
1102
|
+
recipient: z10.string().optional(),
|
|
1103
|
+
assetIdentifier: z10.string().optional(),
|
|
1104
|
+
deployer: z10.string().optional(),
|
|
1105
|
+
contractName: z10.string().optional(),
|
|
1106
|
+
topic: z10.string().optional(),
|
|
1107
|
+
lockedAddress: z10.string().optional(),
|
|
1108
|
+
trait: z10.string().optional(),
|
|
1109
|
+
minAmount: z10.union([z10.string(), z10.number()]).optional(),
|
|
1110
|
+
maxAmount: z10.union([z10.string(), z10.number()]).optional()
|
|
1111
|
+
})).optional().describe("Chain triggers (chain webhook) — 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."),
|
|
1112
|
+
url: z10.string().describe("Webhook URL"),
|
|
1113
|
+
format: z10.enum([
|
|
1114
|
+
"standard-webhooks",
|
|
1115
|
+
"inngest",
|
|
1116
|
+
"trigger",
|
|
1117
|
+
"cloudflare",
|
|
1118
|
+
"cloudevents",
|
|
1119
|
+
"raw"
|
|
1120
|
+
]).optional().describe("Wire format (default standard-webhooks)"),
|
|
1121
|
+
runtime: z10.enum(["inngest", "trigger", "cloudflare", "node"]).optional().describe("Receiver runtime label (display only)"),
|
|
1122
|
+
filter: z10.record(z10.string(), z10.unknown()).optional().describe('Scalar filter DSL, e.g. {"amount": {"gte": 100}, "sender": "SP..."}'),
|
|
1123
|
+
authConfig: z10.record(z10.string(), z10.unknown()).optional().describe('Receiver auth sent with each delivery, e.g. {"type": "bearer", "token": "..."}')
|
|
1124
|
+
}, async (input) => {
|
|
1125
|
+
const res = await clientProvider().webhooks.create(input);
|
|
1126
|
+
return jsonResponse(res);
|
|
1127
|
+
});
|
|
1128
|
+
defineTool(server, "webhooks_update", "Patch a webhook (name, url, filter, authConfig, format, runtime, retry, timeout, concurrency).", {
|
|
1129
|
+
id: z10.string(),
|
|
1130
|
+
name: z10.string().optional().describe("Rename the webhook"),
|
|
1131
|
+
url: z10.string().optional(),
|
|
1132
|
+
filter: z10.record(z10.string(), z10.unknown()).optional(),
|
|
1133
|
+
authConfig: z10.record(z10.string(), z10.unknown()).optional().describe("Receiver auth sent with each delivery (bearer/etc.)"),
|
|
1134
|
+
format: z10.enum([
|
|
1135
|
+
"standard-webhooks",
|
|
1136
|
+
"inngest",
|
|
1137
|
+
"trigger",
|
|
1138
|
+
"cloudflare",
|
|
1139
|
+
"cloudevents",
|
|
1140
|
+
"raw"
|
|
1141
|
+
]).optional(),
|
|
1142
|
+
runtime: z10.enum(["inngest", "trigger", "cloudflare", "node"]).nullable().optional(),
|
|
1143
|
+
maxRetries: z10.number().int().min(0).optional(),
|
|
1144
|
+
timeoutMs: z10.number().int().min(100).optional(),
|
|
1145
|
+
concurrency: z10.number().int().min(1).optional()
|
|
1146
|
+
}, async ({ id, ...patch }) => {
|
|
1147
|
+
const res = await clientProvider().webhooks.update(id, patch);
|
|
1148
|
+
return jsonResponse(res);
|
|
1149
|
+
});
|
|
1150
|
+
defineTool(server, "webhooks_delete", "Delete a webhook. Pending outbox rows are cascade-deleted.", { id: z10.string() }, async ({ id }) => {
|
|
1151
|
+
const res = await clientProvider().webhooks.delete(id);
|
|
1152
|
+
return jsonResponse(res);
|
|
1153
|
+
});
|
|
1154
|
+
defineTool(server, "webhooks_test", "Send a one-off test webhook to a webhook's URL (built for its format, SSRF-guarded). Logged as a delivery row. Returns {ok, statusCode, error, durationMs, deliveryId}.", { id: z10.string().describe("Webhook id") }, async ({ id }) => {
|
|
1155
|
+
const res = await clientProvider().webhooks.test(id);
|
|
1156
|
+
return jsonResponse(res);
|
|
1157
|
+
});
|
|
1158
|
+
defineTool(server, "webhooks_pause", "Pause a webhook: stops delivery attempts and stops queueing new ones. Nothing is deleted — resume picks the webhook back up from the tip. Verify with webhooks_get (status).", { id: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.pause(id)));
|
|
1159
|
+
defineTool(server, "webhooks_resume", "Resume a paused webhook (also clears a tripped circuit breaker). Deliveries restart from the current tip — use webhooks_replay for the blocks missed while paused. Verify with webhooks_get (status).", { id: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.resume(id)));
|
|
1160
|
+
defineTool(server, "webhooks_rotate_secret", "Rotate a webhook's 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: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.rotateSecret(id)));
|
|
1161
|
+
defineTool(server, "webhooks_deliveries", "List recent delivery attempts for a webhook — status code, attempt count, error, duration, timestamps. THIS IS THE VERIFY CALL: after webhooks_create, webhooks_test, or webhooks_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: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.deliveries(id)));
|
|
1162
|
+
defineTool(server, "webhooks_dead", "List a webhook's dead-letter queue — deliveries that exhausted their retries, with the outbox id, payload, and last error. Diagnose the receiver first (webhooks_get for the URL/auth, webhooks_test for a live probe), then requeue rows with webhooks_requeue.", { id: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.dead(id)));
|
|
1163
|
+
defineTool(server, "webhooks_requeue", "Requeue ONE dead-lettered delivery for another attempt, by its outbox id from webhooks_dead. Fix the receiver first — a requeue against a still-broken endpoint just dies again. Verify with webhooks_deliveries.", {
|
|
1164
|
+
id: z10.string().describe("Webhook id"),
|
|
1165
|
+
outboxId: z10.string().describe("Outbox id of the dead delivery (from webhooks_dead)")
|
|
1166
|
+
}, async ({ id, outboxId }) => jsonResponse(await clientProvider().webhooks.requeue(id, outboxId)));
|
|
1167
|
+
defineTool(server, "webhooks_replay", "Replay a block range for a webhook. Replays run at 10% of batch capacity — use sparingly. Pass `force` (a short idempotency suffix) to re-run a range that was already replayed.", {
|
|
1168
|
+
id: z10.string(),
|
|
1169
|
+
fromBlock: z10.number().int().nonnegative(),
|
|
1170
|
+
toBlock: z10.number().int().nonnegative(),
|
|
1171
|
+
force: z10.string().optional().describe("Idempotency suffix to force a duplicate replay of the range")
|
|
1172
|
+
}, async ({ id, fromBlock, toBlock, force }) => {
|
|
1173
|
+
const res = await clientProvider().webhooks.replay(id, {
|
|
1174
|
+
fromBlock,
|
|
1175
|
+
toBlock,
|
|
1176
|
+
...force !== undefined ? { force } : {}
|
|
1177
|
+
});
|
|
1178
|
+
return jsonResponse(res);
|
|
1179
|
+
});
|
|
1180
|
+
defineTool(server, "subscriptions_list", "Deprecated alias of webhooks_list. Removed next minor. List all webhooks for the current account. Returns summary fields (no secrets).", {}, async () => {
|
|
1181
|
+
const { data } = await clientProvider().webhooks.list();
|
|
882
1182
|
return jsonResponse(data);
|
|
883
1183
|
});
|
|
884
|
-
defineTool(server, "subscriptions_get", "Get full detail for a
|
|
885
|
-
const detail = await clientProvider().
|
|
1184
|
+
defineTool(server, "subscriptions_get", "Deprecated alias of webhooks_get. Removed next minor. Get full detail for a webhook (filter, auth, retry config, circuit state).", { id: z10.string().describe("Webhook id") }, async ({ id }) => {
|
|
1185
|
+
const detail = await clientProvider().webhooks.get(id);
|
|
886
1186
|
return jsonResponse(detail);
|
|
887
1187
|
});
|
|
888
|
-
defineTool(server, "subscriptions_create", "Create a
|
|
1188
|
+
defineTool(server, "subscriptions_create", "Deprecated alias of webhooks_create. Removed next minor. Create a webhook. Two kinds (mutually exclusive): a SUBGRAPH webhook fires on a subgraph table's rows (set subgraphName + tableName + optional filter); a CHAIN webhook 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.", {
|
|
889
1189
|
name: z10.string().describe("Human-readable name, unique per account"),
|
|
890
|
-
subgraphName: z10.string().optional().describe("Subgraph to subscribe to (subgraph
|
|
891
|
-
tableName: z10.string().optional().describe("Table within the subgraph (subgraph
|
|
1190
|
+
subgraphName: z10.string().optional().describe("Subgraph to subscribe to (subgraph webhook)"),
|
|
1191
|
+
tableName: z10.string().optional().describe("Table within the subgraph (subgraph webhook)"),
|
|
892
1192
|
triggers: z10.array(z10.object({
|
|
893
1193
|
type: z10.enum(CHAIN_TRIGGER_TYPES),
|
|
894
1194
|
contractId: z10.string().optional(),
|
|
@@ -904,7 +1204,7 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
904
1204
|
trait: z10.string().optional(),
|
|
905
1205
|
minAmount: z10.union([z10.string(), z10.number()]).optional(),
|
|
906
1206
|
maxAmount: z10.union([z10.string(), z10.number()]).optional()
|
|
907
|
-
})).optional().describe("Chain triggers (chain
|
|
1207
|
+
})).optional().describe("Chain triggers (chain webhook) — 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."),
|
|
908
1208
|
url: z10.string().describe("Webhook URL"),
|
|
909
1209
|
format: z10.enum([
|
|
910
1210
|
"standard-webhooks",
|
|
@@ -918,12 +1218,12 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
918
1218
|
filter: z10.record(z10.string(), z10.unknown()).optional().describe('Scalar filter DSL, e.g. {"amount": {"gte": 100}, "sender": "SP..."}'),
|
|
919
1219
|
authConfig: z10.record(z10.string(), z10.unknown()).optional().describe('Receiver auth sent with each delivery, e.g. {"type": "bearer", "token": "..."}')
|
|
920
1220
|
}, async (input) => {
|
|
921
|
-
const res = await clientProvider().
|
|
1221
|
+
const res = await clientProvider().webhooks.create(input);
|
|
922
1222
|
return jsonResponse(res);
|
|
923
1223
|
});
|
|
924
|
-
defineTool(server, "subscriptions_update", "Patch a
|
|
1224
|
+
defineTool(server, "subscriptions_update", "Deprecated alias of webhooks_update. Removed next minor. Patch a webhook (name, url, filter, authConfig, format, runtime, retry, timeout, concurrency).", {
|
|
925
1225
|
id: z10.string(),
|
|
926
|
-
name: z10.string().optional().describe("Rename the
|
|
1226
|
+
name: z10.string().optional().describe("Rename the webhook"),
|
|
927
1227
|
url: z10.string().optional(),
|
|
928
1228
|
filter: z10.record(z10.string(), z10.unknown()).optional(),
|
|
929
1229
|
authConfig: z10.record(z10.string(), z10.unknown()).optional().describe("Receiver auth sent with each delivery (bearer/etc.)"),
|
|
@@ -940,33 +1240,33 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
940
1240
|
timeoutMs: z10.number().int().min(100).optional(),
|
|
941
1241
|
concurrency: z10.number().int().min(1).optional()
|
|
942
1242
|
}, async ({ id, ...patch }) => {
|
|
943
|
-
const res = await clientProvider().
|
|
1243
|
+
const res = await clientProvider().webhooks.update(id, patch);
|
|
944
1244
|
return jsonResponse(res);
|
|
945
1245
|
});
|
|
946
|
-
defineTool(server, "subscriptions_delete", "Delete a
|
|
947
|
-
const res = await clientProvider().
|
|
1246
|
+
defineTool(server, "subscriptions_delete", "Deprecated alias of webhooks_delete. Removed next minor. Delete a webhook. Pending outbox rows are cascade-deleted.", { id: z10.string() }, async ({ id }) => {
|
|
1247
|
+
const res = await clientProvider().webhooks.delete(id);
|
|
948
1248
|
return jsonResponse(res);
|
|
949
1249
|
});
|
|
950
|
-
defineTool(server, "subscriptions_test", "Send a one-off test webhook to a
|
|
951
|
-
const res = await clientProvider().
|
|
1250
|
+
defineTool(server, "subscriptions_test", "Deprecated alias of webhooks_test. Removed next minor. Send a one-off test webhook to a webhook's URL (built for its format, SSRF-guarded). Logged as a delivery row. Returns {ok, statusCode, error, durationMs, deliveryId}.", { id: z10.string().describe("Webhook id") }, async ({ id }) => {
|
|
1251
|
+
const res = await clientProvider().webhooks.test(id);
|
|
952
1252
|
return jsonResponse(res);
|
|
953
1253
|
});
|
|
954
|
-
defineTool(server, "subscriptions_pause", "Pause a
|
|
955
|
-
defineTool(server, "subscriptions_resume", "Resume a paused
|
|
956
|
-
defineTool(server, "subscriptions_rotate_secret", "Rotate a
|
|
957
|
-
defineTool(server, "subscriptions_deliveries", "List recent delivery attempts for a
|
|
958
|
-
defineTool(server, "subscriptions_dead", "List a
|
|
959
|
-
defineTool(server, "subscriptions_requeue", "Requeue ONE dead-lettered delivery for another attempt, by its outbox id from
|
|
960
|
-
id: z10.string().describe("
|
|
961
|
-
outboxId: z10.string().describe("Outbox id of the dead delivery (from
|
|
962
|
-
}, async ({ id, outboxId }) => jsonResponse(await clientProvider().
|
|
963
|
-
defineTool(server, "subscriptions_replay", "Replay a block range for a
|
|
1254
|
+
defineTool(server, "subscriptions_pause", "Deprecated alias of webhooks_pause. Removed next minor. Pause a webhook: stops delivery attempts and stops queueing new ones. Nothing is deleted — resume picks the webhook back up from the tip. Verify with webhooks_get (status).", { id: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.pause(id)));
|
|
1255
|
+
defineTool(server, "subscriptions_resume", "Deprecated alias of webhooks_resume. Removed next minor. Resume a paused webhook (also clears a tripped circuit breaker). Deliveries restart from the current tip — use webhooks_replay for the blocks missed while paused. Verify with webhooks_get (status).", { id: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.resume(id)));
|
|
1256
|
+
defineTool(server, "subscriptions_rotate_secret", "Deprecated alias of webhooks_rotate_secret. Removed next minor. Rotate a webhook's 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: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.rotateSecret(id)));
|
|
1257
|
+
defineTool(server, "subscriptions_deliveries", "Deprecated alias of webhooks_deliveries. Removed next minor. List recent delivery attempts for a webhook — status code, attempt count, error, duration, timestamps. THIS IS THE VERIFY CALL: after webhooks_create, webhooks_test, or webhooks_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: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.deliveries(id)));
|
|
1258
|
+
defineTool(server, "subscriptions_dead", "Deprecated alias of webhooks_dead. Removed next minor. List a webhook's dead-letter queue — deliveries that exhausted their retries, with the outbox id, payload, and last error. Diagnose the receiver first (webhooks_get for the URL/auth, webhooks_test for a live probe), then requeue rows with webhooks_requeue.", { id: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.dead(id)));
|
|
1259
|
+
defineTool(server, "subscriptions_requeue", "Deprecated alias of webhooks_requeue. Removed next minor. Requeue ONE dead-lettered delivery for another attempt, by its outbox id from webhooks_dead. Fix the receiver first — a requeue against a still-broken endpoint just dies again. Verify with webhooks_deliveries.", {
|
|
1260
|
+
id: z10.string().describe("Webhook id"),
|
|
1261
|
+
outboxId: z10.string().describe("Outbox id of the dead delivery (from webhooks_dead)")
|
|
1262
|
+
}, async ({ id, outboxId }) => jsonResponse(await clientProvider().webhooks.requeue(id, outboxId)));
|
|
1263
|
+
defineTool(server, "subscriptions_replay", "Deprecated alias of webhooks_replay. Removed next minor. Replay a block range for a webhook. Replays run at 10% of batch capacity — use sparingly. Pass `force` (a short idempotency suffix) to re-run a range that was already replayed.", {
|
|
964
1264
|
id: z10.string(),
|
|
965
1265
|
fromBlock: z10.number().int().nonnegative(),
|
|
966
1266
|
toBlock: z10.number().int().nonnegative(),
|
|
967
1267
|
force: z10.string().optional().describe("Idempotency suffix to force a duplicate replay of the range")
|
|
968
1268
|
}, async ({ id, fromBlock, toBlock, force }) => {
|
|
969
|
-
const res = await clientProvider().
|
|
1269
|
+
const res = await clientProvider().webhooks.replay(id, {
|
|
970
1270
|
fromBlock,
|
|
971
1271
|
toBlock,
|
|
972
1272
|
...force !== undefined ? { force } : {}
|
|
@@ -977,7 +1277,7 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
977
1277
|
|
|
978
1278
|
// src/server.ts
|
|
979
1279
|
var __dirname2 = dirname(fileURLToPath(import.meta.url));
|
|
980
|
-
var pkg = JSON.parse(readFileSync(
|
|
1280
|
+
var pkg = JSON.parse(readFileSync(join2(__dirname2, "../package.json"), "utf-8"));
|
|
981
1281
|
function createServer() {
|
|
982
1282
|
const server = new McpServer({
|
|
983
1283
|
name: "secondlayer",
|
|
@@ -985,7 +1285,7 @@ function createServer() {
|
|
|
985
1285
|
});
|
|
986
1286
|
registerScaffoldTools(server);
|
|
987
1287
|
registerSubgraphTools(server);
|
|
988
|
-
|
|
1288
|
+
registerWebhookTools(server);
|
|
989
1289
|
registerIndexTools(server);
|
|
990
1290
|
registerStreamsTools(server);
|
|
991
1291
|
registerContractTools(server);
|
|
@@ -1006,5 +1306,5 @@ var server = createServer();
|
|
|
1006
1306
|
var transport = new StdioServerTransport;
|
|
1007
1307
|
await server.connect(transport);
|
|
1008
1308
|
|
|
1009
|
-
//# debugId=
|
|
1309
|
+
//# debugId=AA11B5B2A18DEA7664756E2164756E21
|
|
1010
1310
|
//# sourceMappingURL=bin.js.map
|