@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 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: "nft_transfer",
129
- fields: ["sender", "recipient", "assetIdentifier", "tokenId"]
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: "uint",
140
- sqlType: "bigint",
141
- description: "Unsigned integer (Clarity uint)"
127
+ type: "ft_burn",
128
+ fields: ["sender", "assetIdentifier", "minAmount", "trait"]
142
129
  },
143
130
  {
144
- type: "int",
145
- sqlType: "bigint",
146
- description: "Signed integer (Clarity int)"
131
+ type: "nft_transfer",
132
+ fields: ["sender", "recipient", "assetIdentifier", "trait"]
147
133
  },
148
- { type: "text", sqlType: "text", description: "UTF-8 string" },
134
+ { type: "nft_mint", fields: ["recipient", "assetIdentifier", "trait"] },
135
+ { type: "nft_burn", fields: ["sender", "assetIdentifier", "trait"] },
149
136
  {
150
- type: "principal",
151
- sqlType: "text",
152
- description: "Stacks address (standard or contract)"
137
+ type: "contract_call",
138
+ fields: ["contractId", "functionName", "caller", "trait", "abi"]
153
139
  },
154
- { type: "bool", sqlType: "boolean", description: "Boolean value" },
155
- { type: "json", sqlType: "jsonb", description: "Arbitrary JSON data" },
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=587F4DDF9A78B13764756E2164756E21
961
+ //# debugId=435B327DCA8D3DB864756E2164756E21
866
962
  //# sourceMappingURL=bin-http.js.map