@secondlayer/mcp 3.3.1 → 3.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/dist/bin.js CHANGED
@@ -9,6 +9,9 @@ import { dirname, join } from "node:path";
9
9
  import { fileURLToPath } from "node:url";
10
10
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
11
11
 
12
+ // src/resources.ts
13
+ import { TYPE_MAP } from "@secondlayer/subgraphs/schema";
14
+
12
15
  // src/lib/client.ts
13
16
  import { SecondLayer } from "@secondlayer/sdk";
14
17
  var instance = null;
@@ -111,45 +114,44 @@ var FILTERS_REFERENCE = [
111
114
  { type: "stx_lock", fields: ["lockedAddress", "minAmount"] },
112
115
  {
113
116
  type: "ft_transfer",
114
- fields: [
115
- "sender",
116
- "recipient",
117
- "assetIdentifier",
118
- "minAmount",
119
- "maxAmount"
120
- ]
117
+ fields: ["sender", "recipient", "assetIdentifier", "minAmount", "trait"]
121
118
  },
122
- { type: "ft_mint", fields: ["recipient", "assetIdentifier", "minAmount"] },
123
- { type: "ft_burn", fields: ["sender", "assetIdentifier", "minAmount"] },
124
119
  {
125
- type: "nft_transfer",
126
- fields: ["sender", "recipient", "assetIdentifier", "tokenId"]
120
+ type: "ft_mint",
121
+ fields: ["recipient", "assetIdentifier", "minAmount", "trait"]
127
122
  },
128
- { type: "nft_mint", fields: ["recipient", "assetIdentifier", "tokenId"] },
129
- { type: "nft_burn", fields: ["sender", "assetIdentifier", "tokenId"] },
130
- { type: "contract_call", fields: ["contract", "function"] },
131
- { type: "contract_deploy", fields: ["contract"] },
132
- { type: "print_event", fields: ["contract", "event", "contains"] }
133
- ];
134
- var COLUMN_TYPES = [
135
123
  {
136
- type: "uint",
137
- sqlType: "bigint",
138
- description: "Unsigned integer (Clarity uint)"
124
+ type: "ft_burn",
125
+ fields: ["sender", "assetIdentifier", "minAmount", "trait"]
139
126
  },
140
127
  {
141
- type: "int",
142
- sqlType: "bigint",
143
- description: "Signed integer (Clarity int)"
128
+ type: "nft_transfer",
129
+ fields: ["sender", "recipient", "assetIdentifier", "trait"]
144
130
  },
145
- { type: "text", sqlType: "text", description: "UTF-8 string" },
131
+ { type: "nft_mint", fields: ["recipient", "assetIdentifier", "trait"] },
132
+ { type: "nft_burn", fields: ["sender", "assetIdentifier", "trait"] },
146
133
  {
147
- type: "principal",
148
- sqlType: "text",
149
- description: "Stacks address (standard or contract)"
134
+ type: "contract_call",
135
+ fields: ["contractId", "functionName", "caller", "trait", "abi"]
150
136
  },
151
- { type: "bool", sqlType: "boolean", description: "Boolean value" },
152
- { type: "json", sqlType: "jsonb", description: "Arbitrary JSON data" },
137
+ { type: "contract_deploy", fields: ["deployer", "contractName"] },
138
+ { type: "print_event", fields: ["contractId", "topic", "trait"] }
139
+ ];
140
+ var COLUMN_TYPE_DESCRIPTIONS = {
141
+ text: "UTF-8 string",
142
+ uint: "Unsigned integer (Clarity uint) — NUMERIC for lossless 128-bit range",
143
+ int: "Signed integer (Clarity int) — NUMERIC for lossless 128-bit range",
144
+ principal: "Stacks address (standard or contract)",
145
+ boolean: "Boolean value",
146
+ timestamp: "Timestamp with time zone",
147
+ jsonb: "Arbitrary JSON data"
148
+ };
149
+ var COLUMN_TYPES = [
150
+ ...Object.keys(TYPE_MAP).map((type) => ({
151
+ type,
152
+ sqlType: TYPE_MAP[type],
153
+ description: COLUMN_TYPE_DESCRIPTIONS[type]
154
+ })),
153
155
  {
154
156
  options: ["nullable", "indexed", "search"],
155
157
  description: "Column options: nullable allows NULL, indexed creates a B-tree index, search enables full-text search"
@@ -322,6 +324,10 @@ function registerDatasetTools(server, clientProvider = getClient) {
322
324
 
323
325
  // src/tools/index.ts
324
326
  import { DECODED_EVENT_TYPES } from "@secondlayer/shared";
327
+ import {
328
+ generateIndexSchema,
329
+ INDEX_CODEGEN_TABLES
330
+ } from "@secondlayer/subgraphs";
325
331
  import { z as z4 } from "zod/v4";
326
332
  var INDEX_EVENT_TYPES = DECODED_EVENT_TYPES;
327
333
  var rangeFilters = {
@@ -397,6 +403,14 @@ function registerIndexTools(server, clientProvider = getClient) {
397
403
  return tx ? jsonResponse(tx) : notFound(`No pending tx for ${txId}`);
398
404
  });
399
405
  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()));
406
+ 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(", ")}.`, {
407
+ target: z4.enum(["prisma", "kysely", "drizzle", "json-schema"]).optional().describe("Output target (default kysely)"),
408
+ tables: z4.array(z4.string()).optional().describe("Subset of Index tables (default: all)"),
409
+ schemaName: z4.string().optional().describe("Postgres schema to qualify table names with")
410
+ }, async ({ target = "kysely", tables, schemaName }) => {
411
+ const out = generateIndexSchema(target, { tables, schemaName });
412
+ return { content: [{ type: "text", text: out }] };
413
+ });
400
414
  }
401
415
 
402
416
  // src/tools/scaffold.ts
@@ -475,6 +489,48 @@ function registerStreamsTools(server, clientProvider = getClient) {
475
489
  cursor: z6.string().optional().describe("Opaque cursor from a prior response"),
476
490
  limit: z6.number().optional().describe("Max events for this page")
477
491
  }, async (params) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.events.list(params))));
492
+ 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).", {
493
+ fromCursor: z6.string().optional().describe("Resume cursor from a prior consume (omit to start at genesis)"),
494
+ types: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to include"),
495
+ notTypes: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to exclude"),
496
+ contractId: z6.string().optional().describe("Filter by contract id"),
497
+ sender: z6.string().optional().describe("Filter by sender principal"),
498
+ recipient: z6.string().optional().describe("Filter by recipient principal"),
499
+ assetIdentifier: z6.string().optional().describe("Filter by asset identifier"),
500
+ batchSize: z6.number().optional().describe("Events per page (1–1000, default 100)"),
501
+ maxPages: z6.number().optional().describe("Max pages to walk this call (1–20, default 5)"),
502
+ finalizedOnly: z6.boolean().optional().describe("Only emit finalized events; skip reorg handling")
503
+ }, async (params) => withStreamsAuthHint(async () => {
504
+ const maxPages = Math.min(Math.max(params.maxPages ?? 5, 1), 20);
505
+ const batchSize = Math.min(Math.max(params.batchSize ?? 100, 1), 1000);
506
+ const events = [];
507
+ const reorgs = [];
508
+ const result = await clientProvider().streams.events.consume({
509
+ fromCursor: params.fromCursor ?? null,
510
+ mode: "bounded",
511
+ finalizedOnly: params.finalizedOnly,
512
+ types: params.types,
513
+ notTypes: params.notTypes,
514
+ contractId: params.contractId,
515
+ sender: params.sender,
516
+ recipient: params.recipient,
517
+ assetIdentifier: params.assetIdentifier,
518
+ batchSize,
519
+ maxPages,
520
+ onBatch: (batch) => {
521
+ events.push(...batch);
522
+ },
523
+ onReorg: (reorg) => {
524
+ reorgs.push(reorg);
525
+ }
526
+ });
527
+ return jsonResponse({
528
+ events,
529
+ reorgs,
530
+ cursor: result.cursor,
531
+ pages: result.pages
532
+ });
533
+ }));
478
534
  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))));
479
535
  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).", {
480
536
  heightOrHash: z6.string().describe("Block height (digits) or block hash (0x… string)")
@@ -489,6 +545,11 @@ function registerStreamsTools(server, clientProvider = getClient) {
489
545
 
490
546
  // src/tools/subgraphs.ts
491
547
  import { bundleSubgraphCode } from "@secondlayer/bundler";
548
+ import {
549
+ generateDrizzleSchema,
550
+ generateKyselySchema,
551
+ generatePrismaSchema
552
+ } from "@secondlayer/subgraphs";
492
553
  import { z as z7 } from "zod/v4";
493
554
  function registerSubgraphTools(server, clientProvider = getClient) {
494
555
  defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only.", {}, async () => {
@@ -565,6 +626,21 @@ function registerSubgraphTools(server, clientProvider = getClient) {
565
626
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
566
627
  };
567
628
  });
629
+ defineTool(server, "subgraphs_aggregate", `Compute scalar aggregates over a subgraph table's filtered rows. Supports count, countDistinct, and sum/min/max (numeric columns only — uint/int plus the system _block_height). Filters use the same grammar as subgraphs_query (e.g. {"amount.gte": "1000"}). sum/min/max come back as lossless strings; counts as numbers. With no aggregate requested, returns the row count.`, {
630
+ name: z7.string().describe("Subgraph name"),
631
+ table: z7.string().describe("Table name"),
632
+ filters: z7.record(z7.string(), z7.string()).optional().describe('Column filters — plain values or with operators (e.g. {"amount.gte": "1000", "sender": "SP..."})'),
633
+ count: z7.boolean().optional().describe("Include COUNT(*) of matching rows"),
634
+ countDistinct: z7.array(z7.string()).optional().describe("Columns to count distinct values of"),
635
+ sum: z7.array(z7.string()).optional().describe("Numeric columns to sum (lossless string result)"),
636
+ min: z7.array(z7.string()).optional().describe("Numeric columns to take the minimum of"),
637
+ max: z7.array(z7.string()).optional().describe("Numeric columns to take the maximum of")
638
+ }, async ({ name, table, filters, count, countDistinct, sum, min, max }) => {
639
+ const result = await clientProvider().subgraphs.queryTableAggregate(name, table, { filters, count, countDistinct, sum, min, max });
640
+ return {
641
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
642
+ };
643
+ });
568
644
  defineTool(server, "subgraphs_reindex", "Reindex a subgraph from a specific block range. Returns an operationId — poll subgraphs_operation to track progress to completion.", {
569
645
  name: z7.string().describe("Subgraph name"),
570
646
  fromBlock: z7.number().optional().describe("Start block (defaults to beginning)"),
@@ -616,6 +692,35 @@ function registerSubgraphTools(server, clientProvider = getClient) {
616
692
  content: [{ type: "text", text: JSON.stringify(source, null, 2) }]
617
693
  };
618
694
  });
695
+ 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.", {
696
+ code: z7.string().optional().describe("defineSubgraph() source (mutually exclusive with name)"),
697
+ name: z7.string().optional().describe("Deployed subgraph name (mutually exclusive with code)"),
698
+ target: z7.enum(["prisma", "drizzle", "kysely"]).optional().describe("ORM target (default prisma)"),
699
+ schemaName: z7.string().optional().describe("Postgres schema name (defaults to subgraph_<name>)")
700
+ }, async ({ code, name, target = "prisma", schemaName }) => {
701
+ if (code && name || !code && !name) {
702
+ throw new Error("Provide exactly one of `code` or `name`.");
703
+ }
704
+ let source = code;
705
+ if (name) {
706
+ const stored = await clientProvider().subgraphs.getSource(name);
707
+ if (!stored.sourceCode) {
708
+ throw new Error(`Subgraph "${name}" has no captured source (deployed before source capture). Redeploy it, or pass its source as \`code\`.`);
709
+ }
710
+ source = stored.sourceCode;
711
+ }
712
+ const bundled = await bundleSubgraphCode(source);
713
+ const def = {
714
+ name: bundled.name,
715
+ version: bundled.version,
716
+ description: bundled.description,
717
+ sources: bundled.sources,
718
+ schema: bundled.schema,
719
+ handlers: {}
720
+ };
721
+ const out = target === "drizzle" ? generateDrizzleSchema(def, { schemaName }) : target === "kysely" ? generateKyselySchema(def, { schemaName }) : generatePrismaSchema(def, { schemaName });
722
+ return { content: [{ type: "text", text: out }] };
723
+ });
619
724
  }
620
725
 
621
726
  // src/tools/subscriptions.ts
@@ -717,6 +822,12 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
717
822
  content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
718
823
  };
719
824
  });
825
+ 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 }) => {
826
+ const res = await clientProvider().subscriptions.test(id);
827
+ return {
828
+ content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
829
+ };
830
+ });
720
831
  defineTool(server, "subscriptions_replay", "Replay a block range for a subscription. Replays run at 10% of batch capacity — use sparingly.", {
721
832
  id: z8.string(),
722
833
  fromBlock: z8.number().int().nonnegative(),
@@ -778,5 +889,5 @@ var server = createServer();
778
889
  var transport = new StdioServerTransport;
779
890
  await server.connect(transport);
780
891
 
781
- //# debugId=6CC81A386AFC65E864756E2164756E21
892
+ //# debugId=43FD93326BB3318D64756E2164756E21
782
893
  //# sourceMappingURL=bin.js.map