@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/index.js CHANGED
@@ -4,6 +4,9 @@ import { dirname, join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
6
 
7
+ // src/resources.ts
8
+ import { TYPE_MAP } from "@secondlayer/subgraphs/schema";
9
+
7
10
  // src/lib/client.ts
8
11
  import { SecondLayer } from "@secondlayer/sdk";
9
12
  var instance = null;
@@ -106,45 +109,44 @@ var FILTERS_REFERENCE = [
106
109
  { type: "stx_lock", fields: ["lockedAddress", "minAmount"] },
107
110
  {
108
111
  type: "ft_transfer",
109
- fields: [
110
- "sender",
111
- "recipient",
112
- "assetIdentifier",
113
- "minAmount",
114
- "maxAmount"
115
- ]
112
+ fields: ["sender", "recipient", "assetIdentifier", "minAmount", "trait"]
116
113
  },
117
- { type: "ft_mint", fields: ["recipient", "assetIdentifier", "minAmount"] },
118
- { type: "ft_burn", fields: ["sender", "assetIdentifier", "minAmount"] },
119
114
  {
120
- type: "nft_transfer",
121
- fields: ["sender", "recipient", "assetIdentifier", "tokenId"]
115
+ type: "ft_mint",
116
+ fields: ["recipient", "assetIdentifier", "minAmount", "trait"]
122
117
  },
123
- { type: "nft_mint", fields: ["recipient", "assetIdentifier", "tokenId"] },
124
- { type: "nft_burn", fields: ["sender", "assetIdentifier", "tokenId"] },
125
- { type: "contract_call", fields: ["contract", "function"] },
126
- { type: "contract_deploy", fields: ["contract"] },
127
- { type: "print_event", fields: ["contract", "event", "contains"] }
128
- ];
129
- var COLUMN_TYPES = [
130
118
  {
131
- type: "uint",
132
- sqlType: "bigint",
133
- description: "Unsigned integer (Clarity uint)"
119
+ type: "ft_burn",
120
+ fields: ["sender", "assetIdentifier", "minAmount", "trait"]
134
121
  },
135
122
  {
136
- type: "int",
137
- sqlType: "bigint",
138
- description: "Signed integer (Clarity int)"
123
+ type: "nft_transfer",
124
+ fields: ["sender", "recipient", "assetIdentifier", "trait"]
139
125
  },
140
- { type: "text", sqlType: "text", description: "UTF-8 string" },
126
+ { type: "nft_mint", fields: ["recipient", "assetIdentifier", "trait"] },
127
+ { type: "nft_burn", fields: ["sender", "assetIdentifier", "trait"] },
141
128
  {
142
- type: "principal",
143
- sqlType: "text",
144
- description: "Stacks address (standard or contract)"
129
+ type: "contract_call",
130
+ fields: ["contractId", "functionName", "caller", "trait", "abi"]
145
131
  },
146
- { type: "bool", sqlType: "boolean", description: "Boolean value" },
147
- { type: "json", sqlType: "jsonb", description: "Arbitrary JSON data" },
132
+ { type: "contract_deploy", fields: ["deployer", "contractName"] },
133
+ { type: "print_event", fields: ["contractId", "topic", "trait"] }
134
+ ];
135
+ var COLUMN_TYPE_DESCRIPTIONS = {
136
+ text: "UTF-8 string",
137
+ uint: "Unsigned integer (Clarity uint) — NUMERIC for lossless 128-bit range",
138
+ int: "Signed integer (Clarity int) — NUMERIC for lossless 128-bit range",
139
+ principal: "Stacks address (standard or contract)",
140
+ boolean: "Boolean value",
141
+ timestamp: "Timestamp with time zone",
142
+ jsonb: "Arbitrary JSON data"
143
+ };
144
+ var COLUMN_TYPES = [
145
+ ...Object.keys(TYPE_MAP).map((type) => ({
146
+ type,
147
+ sqlType: TYPE_MAP[type],
148
+ description: COLUMN_TYPE_DESCRIPTIONS[type]
149
+ })),
148
150
  {
149
151
  options: ["nullable", "indexed", "search"],
150
152
  description: "Column options: nullable allows NULL, indexed creates a B-tree index, search enables full-text search"
@@ -317,6 +319,10 @@ function registerDatasetTools(server, clientProvider = getClient) {
317
319
 
318
320
  // src/tools/index.ts
319
321
  import { DECODED_EVENT_TYPES } from "@secondlayer/shared";
322
+ import {
323
+ generateIndexSchema,
324
+ INDEX_CODEGEN_TABLES
325
+ } from "@secondlayer/subgraphs";
320
326
  import { z as z4 } from "zod/v4";
321
327
  var INDEX_EVENT_TYPES = DECODED_EVENT_TYPES;
322
328
  var rangeFilters = {
@@ -392,6 +398,14 @@ function registerIndexTools(server, clientProvider = getClient) {
392
398
  return tx ? jsonResponse(tx) : notFound(`No pending tx for ${txId}`);
393
399
  });
394
400
  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()));
401
+ 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(", ")}.`, {
402
+ target: z4.enum(["prisma", "kysely", "drizzle", "json-schema"]).optional().describe("Output target (default kysely)"),
403
+ tables: z4.array(z4.string()).optional().describe("Subset of Index tables (default: all)"),
404
+ schemaName: z4.string().optional().describe("Postgres schema to qualify table names with")
405
+ }, async ({ target = "kysely", tables, schemaName }) => {
406
+ const out = generateIndexSchema(target, { tables, schemaName });
407
+ return { content: [{ type: "text", text: out }] };
408
+ });
395
409
  }
396
410
 
397
411
  // src/tools/scaffold.ts
@@ -470,6 +484,48 @@ function registerStreamsTools(server, clientProvider = getClient) {
470
484
  cursor: z6.string().optional().describe("Opaque cursor from a prior response"),
471
485
  limit: z6.number().optional().describe("Max events for this page")
472
486
  }, async (params) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.events.list(params))));
487
+ 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).", {
488
+ fromCursor: z6.string().optional().describe("Resume cursor from a prior consume (omit to start at genesis)"),
489
+ types: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to include"),
490
+ notTypes: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to exclude"),
491
+ contractId: z6.string().optional().describe("Filter by contract id"),
492
+ sender: z6.string().optional().describe("Filter by sender principal"),
493
+ recipient: z6.string().optional().describe("Filter by recipient principal"),
494
+ assetIdentifier: z6.string().optional().describe("Filter by asset identifier"),
495
+ batchSize: z6.number().optional().describe("Events per page (1–1000, default 100)"),
496
+ maxPages: z6.number().optional().describe("Max pages to walk this call (1–20, default 5)"),
497
+ finalizedOnly: z6.boolean().optional().describe("Only emit finalized events; skip reorg handling")
498
+ }, async (params) => withStreamsAuthHint(async () => {
499
+ const maxPages = Math.min(Math.max(params.maxPages ?? 5, 1), 20);
500
+ const batchSize = Math.min(Math.max(params.batchSize ?? 100, 1), 1000);
501
+ const events = [];
502
+ const reorgs = [];
503
+ const result = await clientProvider().streams.events.consume({
504
+ fromCursor: params.fromCursor ?? null,
505
+ mode: "bounded",
506
+ finalizedOnly: params.finalizedOnly,
507
+ types: params.types,
508
+ notTypes: params.notTypes,
509
+ contractId: params.contractId,
510
+ sender: params.sender,
511
+ recipient: params.recipient,
512
+ assetIdentifier: params.assetIdentifier,
513
+ batchSize,
514
+ maxPages,
515
+ onBatch: (batch) => {
516
+ events.push(...batch);
517
+ },
518
+ onReorg: (reorg) => {
519
+ reorgs.push(reorg);
520
+ }
521
+ });
522
+ return jsonResponse({
523
+ events,
524
+ reorgs,
525
+ cursor: result.cursor,
526
+ pages: result.pages
527
+ });
528
+ }));
473
529
  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))));
474
530
  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).", {
475
531
  heightOrHash: z6.string().describe("Block height (digits) or block hash (0x… string)")
@@ -484,6 +540,11 @@ function registerStreamsTools(server, clientProvider = getClient) {
484
540
 
485
541
  // src/tools/subgraphs.ts
486
542
  import { bundleSubgraphCode } from "@secondlayer/bundler";
543
+ import {
544
+ generateDrizzleSchema,
545
+ generateKyselySchema,
546
+ generatePrismaSchema
547
+ } from "@secondlayer/subgraphs";
487
548
  import { z as z7 } from "zod/v4";
488
549
  function registerSubgraphTools(server, clientProvider = getClient) {
489
550
  defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only.", {}, async () => {
@@ -611,6 +672,35 @@ function registerSubgraphTools(server, clientProvider = getClient) {
611
672
  content: [{ type: "text", text: JSON.stringify(source, null, 2) }]
612
673
  };
613
674
  });
675
+ 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.", {
676
+ code: z7.string().optional().describe("defineSubgraph() source (mutually exclusive with name)"),
677
+ name: z7.string().optional().describe("Deployed subgraph name (mutually exclusive with code)"),
678
+ target: z7.enum(["prisma", "drizzle", "kysely"]).optional().describe("ORM target (default prisma)"),
679
+ schemaName: z7.string().optional().describe("Postgres schema name (defaults to subgraph_<name>)")
680
+ }, async ({ code, name, target = "prisma", schemaName }) => {
681
+ if (code && name || !code && !name) {
682
+ throw new Error("Provide exactly one of `code` or `name`.");
683
+ }
684
+ let source = code;
685
+ if (name) {
686
+ const stored = await clientProvider().subgraphs.getSource(name);
687
+ if (!stored.sourceCode) {
688
+ throw new Error(`Subgraph "${name}" has no captured source (deployed before source capture). Redeploy it, or pass its source as \`code\`.`);
689
+ }
690
+ source = stored.sourceCode;
691
+ }
692
+ const bundled = await bundleSubgraphCode(source);
693
+ const def = {
694
+ name: bundled.name,
695
+ version: bundled.version,
696
+ description: bundled.description,
697
+ sources: bundled.sources,
698
+ schema: bundled.schema,
699
+ handlers: {}
700
+ };
701
+ const out = target === "drizzle" ? generateDrizzleSchema(def, { schemaName }) : target === "kysely" ? generateKyselySchema(def, { schemaName }) : generatePrismaSchema(def, { schemaName });
702
+ return { content: [{ type: "text", text: out }] };
703
+ });
614
704
  }
615
705
 
616
706
  // src/tools/subscriptions.ts
@@ -712,6 +802,12 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
712
802
  content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
713
803
  };
714
804
  });
805
+ 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 }) => {
806
+ const res = await clientProvider().subscriptions.test(id);
807
+ return {
808
+ content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
809
+ };
810
+ });
715
811
  defineTool(server, "subscriptions_replay", "Replay a block range for a subscription. Replays run at 10% of batch capacity — use sparingly.", {
716
812
  id: z8.string(),
717
813
  fromBlock: z8.number().int().nonnegative(),
@@ -771,5 +867,5 @@ export {
771
867
  createServer
772
868
  };
773
869
 
774
- //# debugId=49F00B0EF2B27D3964756E2164756E21
870
+ //# debugId=F809CEC718C9FE6564756E2164756E21
775
871
  //# sourceMappingURL=index.js.map