@secondlayer/mcp 3.3.0 → 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.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 () => {
@@ -616,6 +677,35 @@ function registerSubgraphTools(server, clientProvider = getClient) {
616
677
  content: [{ type: "text", text: JSON.stringify(source, null, 2) }]
617
678
  };
618
679
  });
680
+ 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.", {
681
+ code: z7.string().optional().describe("defineSubgraph() source (mutually exclusive with name)"),
682
+ name: z7.string().optional().describe("Deployed subgraph name (mutually exclusive with code)"),
683
+ target: z7.enum(["prisma", "drizzle", "kysely"]).optional().describe("ORM target (default prisma)"),
684
+ schemaName: z7.string().optional().describe("Postgres schema name (defaults to subgraph_<name>)")
685
+ }, async ({ code, name, target = "prisma", schemaName }) => {
686
+ if (code && name || !code && !name) {
687
+ throw new Error("Provide exactly one of `code` or `name`.");
688
+ }
689
+ let source = code;
690
+ if (name) {
691
+ const stored = await clientProvider().subgraphs.getSource(name);
692
+ if (!stored.sourceCode) {
693
+ throw new Error(`Subgraph "${name}" has no captured source (deployed before source capture). Redeploy it, or pass its source as \`code\`.`);
694
+ }
695
+ source = stored.sourceCode;
696
+ }
697
+ const bundled = await bundleSubgraphCode(source);
698
+ const def = {
699
+ name: bundled.name,
700
+ version: bundled.version,
701
+ description: bundled.description,
702
+ sources: bundled.sources,
703
+ schema: bundled.schema,
704
+ handlers: {}
705
+ };
706
+ const out = target === "drizzle" ? generateDrizzleSchema(def, { schemaName }) : target === "kysely" ? generateKyselySchema(def, { schemaName }) : generatePrismaSchema(def, { schemaName });
707
+ return { content: [{ type: "text", text: out }] };
708
+ });
619
709
  }
620
710
 
621
711
  // src/tools/subscriptions.ts
@@ -717,6 +807,12 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
717
807
  content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
718
808
  };
719
809
  });
810
+ 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 }) => {
811
+ const res = await clientProvider().subscriptions.test(id);
812
+ return {
813
+ content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
814
+ };
815
+ });
720
816
  defineTool(server, "subscriptions_replay", "Replay a block range for a subscription. Replays run at 10% of batch capacity — use sparingly.", {
721
817
  id: z8.string(),
722
818
  fromBlock: z8.number().int().nonnegative(),
@@ -778,5 +874,5 @@ var server = createServer();
778
874
  var transport = new StdioServerTransport;
779
875
  await server.connect(transport);
780
876
 
781
- //# debugId=6CC81A386AFC65E864756E2164756E21
877
+ //# debugId=8433E24CA57D4D1D64756E2164756E21
782
878
  //# sourceMappingURL=bin.js.map