@secondlayer/mcp 3.3.1 → 3.4.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/dist/bin-http.js +127 -31
- package/dist/bin-http.js.map +8 -8
- package/dist/bin.js +127 -31
- package/dist/bin.js.map +8 -8
- package/dist/index.js +127 -31
- package/dist/index.js.map +8 -8
- package/package.json +4 -3
package/dist/bin-http.js
CHANGED
|
@@ -12,6 +12,9 @@ import { dirname, join } from "node:path";
|
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
14
|
|
|
15
|
+
// src/resources.ts
|
|
16
|
+
import { TYPE_MAP } from "@secondlayer/subgraphs/schema";
|
|
17
|
+
|
|
15
18
|
// src/lib/client.ts
|
|
16
19
|
import { SecondLayer } from "@secondlayer/sdk";
|
|
17
20
|
var instance = null;
|
|
@@ -114,45 +117,44 @@ var FILTERS_REFERENCE = [
|
|
|
114
117
|
{ type: "stx_lock", fields: ["lockedAddress", "minAmount"] },
|
|
115
118
|
{
|
|
116
119
|
type: "ft_transfer",
|
|
117
|
-
fields: [
|
|
118
|
-
"sender",
|
|
119
|
-
"recipient",
|
|
120
|
-
"assetIdentifier",
|
|
121
|
-
"minAmount",
|
|
122
|
-
"maxAmount"
|
|
123
|
-
]
|
|
120
|
+
fields: ["sender", "recipient", "assetIdentifier", "minAmount", "trait"]
|
|
124
121
|
},
|
|
125
|
-
{ type: "ft_mint", fields: ["recipient", "assetIdentifier", "minAmount"] },
|
|
126
|
-
{ type: "ft_burn", fields: ["sender", "assetIdentifier", "minAmount"] },
|
|
127
122
|
{
|
|
128
|
-
type: "
|
|
129
|
-
fields: ["
|
|
123
|
+
type: "ft_mint",
|
|
124
|
+
fields: ["recipient", "assetIdentifier", "minAmount", "trait"]
|
|
130
125
|
},
|
|
131
|
-
{ type: "nft_mint", fields: ["recipient", "assetIdentifier", "tokenId"] },
|
|
132
|
-
{ type: "nft_burn", fields: ["sender", "assetIdentifier", "tokenId"] },
|
|
133
|
-
{ type: "contract_call", fields: ["contract", "function"] },
|
|
134
|
-
{ type: "contract_deploy", fields: ["contract"] },
|
|
135
|
-
{ type: "print_event", fields: ["contract", "event", "contains"] }
|
|
136
|
-
];
|
|
137
|
-
var COLUMN_TYPES = [
|
|
138
126
|
{
|
|
139
|
-
type: "
|
|
140
|
-
|
|
141
|
-
description: "Unsigned integer (Clarity uint)"
|
|
127
|
+
type: "ft_burn",
|
|
128
|
+
fields: ["sender", "assetIdentifier", "minAmount", "trait"]
|
|
142
129
|
},
|
|
143
130
|
{
|
|
144
|
-
type: "
|
|
145
|
-
|
|
146
|
-
description: "Signed integer (Clarity int)"
|
|
131
|
+
type: "nft_transfer",
|
|
132
|
+
fields: ["sender", "recipient", "assetIdentifier", "trait"]
|
|
147
133
|
},
|
|
148
|
-
{ type: "
|
|
134
|
+
{ type: "nft_mint", fields: ["recipient", "assetIdentifier", "trait"] },
|
|
135
|
+
{ type: "nft_burn", fields: ["sender", "assetIdentifier", "trait"] },
|
|
149
136
|
{
|
|
150
|
-
type: "
|
|
151
|
-
|
|
152
|
-
description: "Stacks address (standard or contract)"
|
|
137
|
+
type: "contract_call",
|
|
138
|
+
fields: ["contractId", "functionName", "caller", "trait", "abi"]
|
|
153
139
|
},
|
|
154
|
-
{ type: "
|
|
155
|
-
{ type: "
|
|
140
|
+
{ type: "contract_deploy", fields: ["deployer", "contractName"] },
|
|
141
|
+
{ type: "print_event", fields: ["contractId", "topic", "trait"] }
|
|
142
|
+
];
|
|
143
|
+
var COLUMN_TYPE_DESCRIPTIONS = {
|
|
144
|
+
text: "UTF-8 string",
|
|
145
|
+
uint: "Unsigned integer (Clarity uint) — NUMERIC for lossless 128-bit range",
|
|
146
|
+
int: "Signed integer (Clarity int) — NUMERIC for lossless 128-bit range",
|
|
147
|
+
principal: "Stacks address (standard or contract)",
|
|
148
|
+
boolean: "Boolean value",
|
|
149
|
+
timestamp: "Timestamp with time zone",
|
|
150
|
+
jsonb: "Arbitrary JSON data"
|
|
151
|
+
};
|
|
152
|
+
var COLUMN_TYPES = [
|
|
153
|
+
...Object.keys(TYPE_MAP).map((type) => ({
|
|
154
|
+
type,
|
|
155
|
+
sqlType: TYPE_MAP[type],
|
|
156
|
+
description: COLUMN_TYPE_DESCRIPTIONS[type]
|
|
157
|
+
})),
|
|
156
158
|
{
|
|
157
159
|
options: ["nullable", "indexed", "search"],
|
|
158
160
|
description: "Column options: nullable allows NULL, indexed creates a B-tree index, search enables full-text search"
|
|
@@ -325,6 +327,10 @@ function registerDatasetTools(server, clientProvider = getClient) {
|
|
|
325
327
|
|
|
326
328
|
// src/tools/index.ts
|
|
327
329
|
import { DECODED_EVENT_TYPES } from "@secondlayer/shared";
|
|
330
|
+
import {
|
|
331
|
+
generateIndexSchema,
|
|
332
|
+
INDEX_CODEGEN_TABLES
|
|
333
|
+
} from "@secondlayer/subgraphs";
|
|
328
334
|
import { z as z4 } from "zod/v4";
|
|
329
335
|
var INDEX_EVENT_TYPES = DECODED_EVENT_TYPES;
|
|
330
336
|
var rangeFilters = {
|
|
@@ -400,6 +406,14 @@ function registerIndexTools(server, clientProvider = getClient) {
|
|
|
400
406
|
return tx ? jsonResponse(tx) : notFound(`No pending tx for ${txId}`);
|
|
401
407
|
});
|
|
402
408
|
defineTool(server, "index_usage", "Your own Index consumption (decoded events today + this month) and tier limits. Requires a Build+ API key (anonymous reads can't report usage).", {}, async () => jsonResponse(await clientProvider().index.usage()));
|
|
409
|
+
defineTool(server, "index_codegen", `Generate a typed schema (Prisma, Kysely, Drizzle, or JSON-Schema) for the public Index domain tables so they can be mirrored into a BYO database with full types. Returns the schema as text. Tables: ${INDEX_CODEGEN_TABLES.join(", ")}.`, {
|
|
410
|
+
target: z4.enum(["prisma", "kysely", "drizzle", "json-schema"]).optional().describe("Output target (default kysely)"),
|
|
411
|
+
tables: z4.array(z4.string()).optional().describe("Subset of Index tables (default: all)"),
|
|
412
|
+
schemaName: z4.string().optional().describe("Postgres schema to qualify table names with")
|
|
413
|
+
}, async ({ target = "kysely", tables, schemaName }) => {
|
|
414
|
+
const out = generateIndexSchema(target, { tables, schemaName });
|
|
415
|
+
return { content: [{ type: "text", text: out }] };
|
|
416
|
+
});
|
|
403
417
|
}
|
|
404
418
|
|
|
405
419
|
// src/tools/scaffold.ts
|
|
@@ -478,6 +492,48 @@ function registerStreamsTools(server, clientProvider = getClient) {
|
|
|
478
492
|
cursor: z6.string().optional().describe("Opaque cursor from a prior response"),
|
|
479
493
|
limit: z6.number().optional().describe("Max events for this page")
|
|
480
494
|
}, async (params) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.events.list(params))));
|
|
495
|
+
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).", {
|
|
496
|
+
fromCursor: z6.string().optional().describe("Resume cursor from a prior consume (omit to start at genesis)"),
|
|
497
|
+
types: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to include"),
|
|
498
|
+
notTypes: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to exclude"),
|
|
499
|
+
contractId: z6.string().optional().describe("Filter by contract id"),
|
|
500
|
+
sender: z6.string().optional().describe("Filter by sender principal"),
|
|
501
|
+
recipient: z6.string().optional().describe("Filter by recipient principal"),
|
|
502
|
+
assetIdentifier: z6.string().optional().describe("Filter by asset identifier"),
|
|
503
|
+
batchSize: z6.number().optional().describe("Events per page (1–1000, default 100)"),
|
|
504
|
+
maxPages: z6.number().optional().describe("Max pages to walk this call (1–20, default 5)"),
|
|
505
|
+
finalizedOnly: z6.boolean().optional().describe("Only emit finalized events; skip reorg handling")
|
|
506
|
+
}, async (params) => withStreamsAuthHint(async () => {
|
|
507
|
+
const maxPages = Math.min(Math.max(params.maxPages ?? 5, 1), 20);
|
|
508
|
+
const batchSize = Math.min(Math.max(params.batchSize ?? 100, 1), 1000);
|
|
509
|
+
const events = [];
|
|
510
|
+
const reorgs = [];
|
|
511
|
+
const result = await clientProvider().streams.events.consume({
|
|
512
|
+
fromCursor: params.fromCursor ?? null,
|
|
513
|
+
mode: "bounded",
|
|
514
|
+
finalizedOnly: params.finalizedOnly,
|
|
515
|
+
types: params.types,
|
|
516
|
+
notTypes: params.notTypes,
|
|
517
|
+
contractId: params.contractId,
|
|
518
|
+
sender: params.sender,
|
|
519
|
+
recipient: params.recipient,
|
|
520
|
+
assetIdentifier: params.assetIdentifier,
|
|
521
|
+
batchSize,
|
|
522
|
+
maxPages,
|
|
523
|
+
onBatch: (batch) => {
|
|
524
|
+
events.push(...batch);
|
|
525
|
+
},
|
|
526
|
+
onReorg: (reorg) => {
|
|
527
|
+
reorgs.push(reorg);
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
return jsonResponse({
|
|
531
|
+
events,
|
|
532
|
+
reorgs,
|
|
533
|
+
cursor: result.cursor,
|
|
534
|
+
pages: result.pages
|
|
535
|
+
});
|
|
536
|
+
}));
|
|
481
537
|
defineTool(server, "streams_event_by_txid", "List all Streams events emitted by a single transaction. Streams requires an API key (SL_API_KEY).", { txId: z6.string().describe("Transaction id (0x… hash)") }, async ({ txId }) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.events.byTxId(txId))));
|
|
482
538
|
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).", {
|
|
483
539
|
heightOrHash: z6.string().describe("Block height (digits) or block hash (0x… string)")
|
|
@@ -492,6 +548,11 @@ function registerStreamsTools(server, clientProvider = getClient) {
|
|
|
492
548
|
|
|
493
549
|
// src/tools/subgraphs.ts
|
|
494
550
|
import { bundleSubgraphCode } from "@secondlayer/bundler";
|
|
551
|
+
import {
|
|
552
|
+
generateDrizzleSchema,
|
|
553
|
+
generateKyselySchema,
|
|
554
|
+
generatePrismaSchema
|
|
555
|
+
} from "@secondlayer/subgraphs";
|
|
495
556
|
import { z as z7 } from "zod/v4";
|
|
496
557
|
function registerSubgraphTools(server, clientProvider = getClient) {
|
|
497
558
|
defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only.", {}, async () => {
|
|
@@ -619,6 +680,35 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
619
680
|
content: [{ type: "text", text: JSON.stringify(source, null, 2) }]
|
|
620
681
|
};
|
|
621
682
|
});
|
|
683
|
+
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.", {
|
|
684
|
+
code: z7.string().optional().describe("defineSubgraph() source (mutually exclusive with name)"),
|
|
685
|
+
name: z7.string().optional().describe("Deployed subgraph name (mutually exclusive with code)"),
|
|
686
|
+
target: z7.enum(["prisma", "drizzle", "kysely"]).optional().describe("ORM target (default prisma)"),
|
|
687
|
+
schemaName: z7.string().optional().describe("Postgres schema name (defaults to subgraph_<name>)")
|
|
688
|
+
}, async ({ code, name, target = "prisma", schemaName }) => {
|
|
689
|
+
if (code && name || !code && !name) {
|
|
690
|
+
throw new Error("Provide exactly one of `code` or `name`.");
|
|
691
|
+
}
|
|
692
|
+
let source = code;
|
|
693
|
+
if (name) {
|
|
694
|
+
const stored = await clientProvider().subgraphs.getSource(name);
|
|
695
|
+
if (!stored.sourceCode) {
|
|
696
|
+
throw new Error(`Subgraph "${name}" has no captured source (deployed before source capture). Redeploy it, or pass its source as \`code\`.`);
|
|
697
|
+
}
|
|
698
|
+
source = stored.sourceCode;
|
|
699
|
+
}
|
|
700
|
+
const bundled = await bundleSubgraphCode(source);
|
|
701
|
+
const def = {
|
|
702
|
+
name: bundled.name,
|
|
703
|
+
version: bundled.version,
|
|
704
|
+
description: bundled.description,
|
|
705
|
+
sources: bundled.sources,
|
|
706
|
+
schema: bundled.schema,
|
|
707
|
+
handlers: {}
|
|
708
|
+
};
|
|
709
|
+
const out = target === "drizzle" ? generateDrizzleSchema(def, { schemaName }) : target === "kysely" ? generateKyselySchema(def, { schemaName }) : generatePrismaSchema(def, { schemaName });
|
|
710
|
+
return { content: [{ type: "text", text: out }] };
|
|
711
|
+
});
|
|
622
712
|
}
|
|
623
713
|
|
|
624
714
|
// src/tools/subscriptions.ts
|
|
@@ -720,6 +810,12 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
720
810
|
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
721
811
|
};
|
|
722
812
|
});
|
|
813
|
+
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: z8.string().describe("Subscription id") }, async ({ id }) => {
|
|
814
|
+
const res = await clientProvider().subscriptions.test(id);
|
|
815
|
+
return {
|
|
816
|
+
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
817
|
+
};
|
|
818
|
+
});
|
|
723
819
|
defineTool(server, "subscriptions_replay", "Replay a block range for a subscription. Replays run at 10% of batch capacity — use sparingly.", {
|
|
724
820
|
id: z8.string(),
|
|
725
821
|
fromBlock: z8.number().int().nonnegative(),
|
|
@@ -862,5 +958,5 @@ httpServer.listen(port, () => {
|
|
|
862
958
|
console.error("Warning: SECONDLAYER_MCP_SECRET not set, authentication disabled");
|
|
863
959
|
});
|
|
864
960
|
|
|
865
|
-
//# debugId=
|
|
961
|
+
//# debugId=435B327DCA8D3DB864756E2164756E21
|
|
866
962
|
//# sourceMappingURL=bin-http.js.map
|