@secondlayer/mcp 3.10.0 → 4.0.1

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
@@ -5,7 +5,7 @@ import { fileURLToPath } from "node:url";
5
5
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
6
 
7
7
  // src/resources.ts
8
- import { CHAIN_TRIGGER_FIELDS, DECODED_EVENT_TYPES } from "@secondlayer/shared";
8
+ import { CHAIN_TRIGGER_FIELDS } from "@secondlayer/shared";
9
9
  import { TRAIT_STANDARDS } from "@secondlayer/stacks/clarity";
10
10
  import { TYPE_MAP } from "@secondlayer/subgraphs/schema";
11
11
 
@@ -193,14 +193,13 @@ var COLUMN_TYPES = [
193
193
  }
194
194
  ];
195
195
  var PRODUCT_BLURBS = {
196
- index: "decoded L2 events, contract calls, blocks, transactions, stacking, mempool",
197
- streams: "raw canonical chain event firehose",
198
- contracts: "trait-based contract discovery",
196
+ index: "decoded events, transfers, contract calls, blocks, transactions",
197
+ streams: "bulk parquet dumps of the raw chain event firehose",
198
+ contracts: "trait-based contract discovery and ABIs",
199
199
  subgraphs: "author/deploy/query custom indexes",
200
200
  subscriptions: "webhook delivery on subgraph rows or raw chain events",
201
- account: "identity, plan, billing, usage, spend caps, and API keys",
202
- project: "create/manage projects and view their team",
203
- scaffold: "generate typed contract clients from a deployment or ABI"
201
+ account: "identity and self-provisioned API keys",
202
+ scaffold: "generate deploy-ready subgraph code from a deployed contract"
204
203
  };
205
204
  var PRODUCT_ORDER = [
206
205
  "index",
@@ -209,7 +208,6 @@ var PRODUCT_ORDER = [
209
208
  "subgraphs",
210
209
  "subscriptions",
211
210
  "account",
212
- "project",
213
211
  "scaffold"
214
212
  ];
215
213
  function buildCapabilities() {
@@ -316,29 +314,6 @@ function registerResources(server) {
316
314
  }
317
315
  ]
318
316
  }));
319
- server.resource("streams-filters", "secondlayer://streams-filters", {
320
- description: "Streams firehose vocabulary — the decoded event types and the filter fields accepted by streams_events / streams_consume."
321
- }, async () => ({
322
- contents: [
323
- {
324
- uri: "secondlayer://streams-filters",
325
- mimeType: "application/json",
326
- text: JSON.stringify({
327
- eventTypes: [...DECODED_EVENT_TYPES],
328
- filters: {
329
- types: "include only these event types (array)",
330
- notTypes: "exclude these event types (array)",
331
- contractId: "match a contract id",
332
- sender: "match the sender principal",
333
- recipient: "match the recipient principal",
334
- assetIdentifier: "match the asset identifier (contract::asset)",
335
- fromHeight: "start block height (inclusive)",
336
- toHeight: "end block height (inclusive)"
337
- }
338
- }, null, 2)
339
- }
340
- ]
341
- }));
342
317
  server.resource("chain-triggers", "secondlayer://chain-triggers", {
343
318
  description: "Chain-subscription trigger types and the filter fields each accepts (for subscriptions_create triggers)."
344
319
  }, async () => ({
@@ -359,50 +334,13 @@ function registerAccountTools(server) {
359
334
  const result = await apiRequest("GET", "/api/accounts/me");
360
335
  return jsonResponse(result);
361
336
  });
362
- defineTool(server, "account_update", "Update the authenticated account's profile. Requires an API key.", {
363
- displayName: z.string().optional().describe("Display name"),
364
- bio: z.string().optional().describe("Profile bio"),
365
- slug: z.string().optional().describe("Account URL slug")
366
- }, async ({ displayName, bio, slug }) => {
367
- const body = {};
368
- if (displayName !== undefined)
369
- body.display_name = displayName;
370
- if (bio !== undefined)
371
- body.bio = bio;
372
- if (slug !== undefined)
373
- body.slug = slug;
374
- const result = await apiRequest("PATCH", "/api/accounts/me", body);
375
- return jsonResponse(result);
376
- });
377
- defineTool(server, "account_billing", "Show the account's plan and subscription/billing status. Requires an API key. (Plan upgrade / Stripe portal / checkout are deliberately not agent tools — they are session-only human-payment flows; use account_set_caps to bound spend.)", {}, async () => {
378
- const result = await apiRequest("GET", "/api/billing/status");
379
- return jsonResponse(result);
380
- });
381
337
  defineTool(server, "account_create_key", "Mint a scoped streams/index read API key so the agent can self-provision access. Requires an account-level (owner) API key. The returned `key` is shown ONCE — forward it to the user to set as SL_API_KEY.", {
382
338
  product: z.enum(["streams", "index"]).optional().describe("Key scope (default streams)"),
383
339
  name: z.string().optional().describe("Optional label for the key")
384
340
  }, async ({ product, name }) => jsonResponse(await getClient().apiKeys.create({ product, name })));
385
- defineTool(server, "account_list_keys", "List the account's API keys (metadata only — prefix, name, status, product, tier, last used; never the plaintext). Requires an account-level (owner) API key.", {}, async () => jsonResponse(await getClient().apiKeys.list()));
386
- defineTool(server, "account_revoke_key", "Revoke an API key by id. Requests using that key stop working immediately. Requires an account-level (owner) API key.", { id: z.string().describe("Key id from account_list_keys") }, async ({ id }) => jsonResponse(await getClient().apiKeys.revoke(id)));
387
- defineTool(server, "account_usage", "Show the current billing period's usage snapshot: spend (with cap/projection), compute, storage, and per-project breakdown. Requires an API key.", {}, async () => jsonResponse(await apiRequest("GET", "/api/accounts/usage")));
388
- defineTool(server, "account_get_caps", "Show the account's spend caps and alert threshold. Requires an account-level (owner) API key.", {}, async () => jsonResponse(await apiRequest("GET", "/api/billing/caps")));
389
- defineTool(server, "account_set_caps", "Set the account's spend caps and alert threshold (no payment — just bounds spend). Requires an account-level (owner) API key.", {
390
- monthlyCapCents: z.number().int().nonnegative().optional().describe("Total monthly spend cap in cents"),
391
- computeCapCents: z.number().int().nonnegative().optional().describe("Compute spend cap in cents"),
392
- storageCapCents: z.number().int().nonnegative().optional().describe("Storage spend cap in cents"),
393
- alertThresholdPct: z.number().int().min(1).max(100).optional().describe("Percent of cap that triggers an alert (1-100)")
394
- }, async (caps) => {
395
- const body = {};
396
- for (const [k, v] of Object.entries(caps)) {
397
- if (v !== undefined)
398
- body[k] = v;
399
- }
400
- return jsonResponse(await apiRequest("PATCH", "/api/billing/caps", body));
401
- });
402
341
  }
403
342
 
404
343
  // src/tools/contracts.ts
405
- import { generateContractInterface } from "@secondlayer/scaffold";
406
344
  import { z as z2 } from "zod/v4";
407
345
  function registerContractTools(server, clientProvider = getClient) {
408
346
  defineTool(server, "contracts_find", 'Discover deployed Stacks contracts conforming to a trait (e.g. "sip-010", "sip-009", "sip-013"). The discovery endpoint for "which contracts implement X". Reads are public.', {
@@ -412,7 +350,7 @@ function registerContractTools(server, clientProvider = getClient) {
412
350
  limit: z2.number().optional().describe("Page size, 1–500 (default 100)"),
413
351
  cursor: z2.string().optional().describe("Opaque cursor from a prior response's next_cursor")
414
352
  }, async (params) => jsonResponse(await clientProvider().contracts.list(params)));
415
- defineTool(server, "get_contract_abi", "Fetch a single deployed contract's ABI from the registry (prod-safe). Returns the contract's metadata + full ABI (functions, maps, variables, fungible/non-fungible tokens). Returns not_found if the contract isn't in the registry. Feed the ABI into scaffold_from_abi or generate a typed client.", {
353
+ defineTool(server, "get_contract_abi", "Fetch a single deployed contract's ABI from the registry (prod-safe). Returns the contract's metadata + full ABI (functions, maps, variables, fungible/non-fungible tokens). Returns not_found if the contract isn't in the registry. Use it to inspect a contract before scaffold_from_contract.", {
416
354
  contractId: z2.string().describe("Fully qualified contract id (e.g. SP….amm-pool-v2-01)")
417
355
  }, async ({ contractId }) => {
418
356
  const contract = await clientProvider().contracts.get(contractId, {
@@ -426,43 +364,12 @@ function registerContractTools(server, clientProvider = getClient) {
426
364
  }
427
365
  }, true);
428
366
  });
429
- defineTool(server, "generate_contract_interface", "Generate a typed TypeScript contract-client interface (typed methods + map/var/constant readers) from a deployed contract's ABI (fetched from the registry). Returns not_found if the contract isn't in the registry.", {
430
- contractId: z2.string().describe("Fully qualified contract id (e.g. SP….amm-pool-v2-01)")
431
- }, async ({ contractId }) => {
432
- const c = await clientProvider().contracts.get(contractId, {
433
- includeAbi: true
434
- });
435
- if (!c || !c.abi) {
436
- return jsonResponse({
437
- error: {
438
- type: "not_found",
439
- status: 404,
440
- message: `Contract not in registry: ${contractId}`
441
- }
442
- }, true);
443
- }
444
- const [address, contractName] = contractId.split(".");
445
- const name = (contractName ?? contractId).replace(/[^a-zA-Z0-9]/g, "_");
446
- const code = generateContractInterface([
447
- {
448
- name,
449
- address,
450
- contractName: contractName ?? "",
451
- abi: c.abi
452
- }
453
- ]);
454
- return { content: [{ type: "text", text: code }] };
455
- });
456
367
  }
457
368
 
458
369
  // src/tools/index.ts
459
- import { DECODED_EVENT_TYPES as DECODED_EVENT_TYPES2 } from "@secondlayer/shared";
460
- import {
461
- INDEX_CODEGEN_TABLES,
462
- generateIndexSchema
463
- } from "@secondlayer/subgraphs";
370
+ import { DECODED_EVENT_TYPES } from "@secondlayer/shared";
464
371
  import { z as z3 } from "zod/v4";
465
- var INDEX_EVENT_TYPES = DECODED_EVENT_TYPES2;
372
+ var INDEX_EVENT_TYPES = DECODED_EVENT_TYPES;
466
373
  var rangeFilters = {
467
374
  contractId: z3.string().optional().describe("Filter by contract id"),
468
375
  fromHeight: z3.number().optional().describe("Start block height (inclusive)"),
@@ -476,9 +383,8 @@ var heightFilters = {
476
383
  cursor: rangeFilters.cursor,
477
384
  limit: rangeFilters.limit
478
385
  };
479
- var notFound = (message) => jsonResponse({ error: { type: "not_found", status: 404, message } }, true);
480
386
  function registerIndexTools(server, clientProvider = getClient) {
481
- defineTool(server, "index_ft_transfers", "List decoded SIP-010 fungible-token transfers from the Index (L2 decoded layer). Anonymous reads allowed (free-tier API keys are rejected — Build+ required).", {
387
+ defineTool(server, "index_ft_transfers", "List decoded SIP-010 fungible-token transfers from the Index (decoded layer). Anonymous reads allowed (free-tier API keys are rejected — Build+ required).", {
482
388
  ...rangeFilters,
483
389
  sender: z3.string().optional().describe("Filter by sender principal"),
484
390
  recipient: z3.string().optional().describe("Filter by recipient principal")
@@ -503,54 +409,13 @@ function registerIndexTools(server, clientProvider = getClient) {
503
409
  sender: z3.string().optional().describe("Filter by caller principal"),
504
410
  trait: z3.string().optional().describe("Match contracts conforming to a trait/standard (e.g. sip-010). Mutually exclusive with contractId.")
505
411
  }, async (params) => jsonResponse(await clientProvider().index.contractCalls.list(params)));
506
- defineTool(server, "index_canonical", "List the canonical Stacks block sequence from the Index (height + hash). Anonymous reads allowed (free-tier keys rejected).", { ...heightFilters }, async (params) => jsonResponse(await clientProvider().index.canonical.list(params)));
507
412
  defineTool(server, "index_blocks", "List decoded blocks from the Index. Anonymous reads allowed (free-tier keys rejected).", { ...heightFilters }, async (params) => jsonResponse(await clientProvider().index.blocks.list(params)));
508
- defineTool(server, "index_block", "Get a single block from the Index by height or block hash. Returns not_found if unknown.", {
509
- ref: z3.string().describe("Block height (digits) or block hash (0x… string)")
510
- }, async ({ ref }) => {
511
- const block = await clientProvider().index.blocks.get(/^\d+$/.test(ref) ? Number(ref) : ref);
512
- return block ? jsonResponse(block) : notFound(`No block for ref ${ref}`);
513
- });
514
413
  defineTool(server, "index_transactions", "List decoded transactions from the Index. Filter by type, sender, or contract. Anonymous reads allowed (free-tier keys rejected).", {
515
414
  ...rangeFilters,
516
415
  type: z3.string().optional().describe("Filter by transaction type"),
517
416
  sender: z3.string().optional().describe("Filter by sender principal")
518
417
  }, async (params) => jsonResponse(await clientProvider().index.transactions.list(params)));
519
- defineTool(server, "index_transaction", "Get a single transaction from the Index by tx_id. Returns not_found if unknown.", { txId: z3.string().describe("Transaction id (0x… hash)") }, async ({ txId }) => {
520
- const tx = await clientProvider().index.transactions.get(txId);
521
- return tx ? jsonResponse(tx) : notFound(`No transaction for ${txId}`);
522
- });
523
- defineTool(server, "index_transaction_proof", "Fetch a transaction's inclusion proof (raw tx + signed Nakamoto block header + merkle path) so it can be verified trustlessly client-side with the SDK's verifyTransactionProof. Returns not_found if the tx/block is unknown; a 503 means the proof can't be assembled on this deployment right now (PROOF_TX_SET_INCOMPLETE, or PROOF_NODE_UNAVAILABLE when the stacks-node header source isn't configured).", { txId: z3.string().describe("Transaction id (0x… hash)") }, async ({ txId }) => {
524
- const proof = await clientProvider().index.transactions.getProof(txId);
525
- return proof ? jsonResponse(proof) : notFound(`No proof available for ${txId}`);
526
- });
527
- defineTool(server, "index_stacking", "List decoded PoX-4 stacking actions from the Index (stack-stx, delegate-stx, etc.). Anonymous reads allowed (free-tier keys rejected).", {
528
- ...heightFilters,
529
- functionName: z3.string().optional().describe("Filter by PoX function name"),
530
- stacker: z3.string().optional().describe("Filter by stacker principal"),
531
- caller: z3.string().optional().describe("Filter by caller principal")
532
- }, async (params) => jsonResponse(await clientProvider().index.stacking.list(params)));
533
- defineTool(server, "index_mempool", "List pending (unconfirmed) transactions from the Index mempool. Sequence-cursor paginated (no height range). Anonymous reads allowed (free-tier keys rejected).", {
534
- sender: z3.string().optional().describe("Filter by sender principal"),
535
- type: z3.string().optional().describe("Filter by transaction type"),
536
- contractId: z3.string().optional().describe("Filter to pending calls to a single contract"),
537
- cursor: z3.string().optional().describe("Opaque cursor from a prior response's next_cursor"),
538
- limit: z3.number().optional().describe("Max rows for this page")
539
- }, async (params) => jsonResponse(await clientProvider().index.mempool.list(params)));
540
- defineTool(server, "index_mempool_tx", "Get a single pending transaction from the Index mempool by tx_id. Returns not_found once it is mined or dropped.", { txId: z3.string().describe("Transaction id (0x… hash)") }, async ({ txId }) => {
541
- const tx = await clientProvider().index.mempool.get(txId);
542
- return tx ? jsonResponse(tx) : notFound(`No pending tx for ${txId}`);
543
- });
544
418
  defineTool(server, "index_discover", "Discover the Index vocabulary: every event type and its columns, allowed/equality filters, and required-non-null fields (and which types accept `trait`). Read this before building Index queries instead of guessing filters. Anonymous read.", {}, async () => jsonResponse(await clientProvider().index.discover()));
545
- 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()));
546
- 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(", ")}.`, {
547
- target: z3.enum(["prisma", "kysely", "drizzle", "json-schema"]).optional().describe("Output target (default kysely)"),
548
- tables: z3.array(z3.string()).optional().describe("Subset of Index tables (default: all)"),
549
- schemaName: z3.string().optional().describe("Postgres schema to qualify table names with")
550
- }, async ({ target = "kysely", tables, schemaName }) => {
551
- const out = generateIndexSchema(target, { tables, schemaName });
552
- return { content: [{ type: "text", text: out }] };
553
- });
554
419
  defineTool(server, "batch_query", "Run up to 10 public /v1 reads in one round trip (POST /v1/batch). Each item keeps its own auth/quota/pay-per-call semantics; results return in order with per-item status. Paths must start with /v1/index, /v1/subgraphs, /v1/streams, or /v1/contracts.", {
555
420
  requests: z3.array(z3.object({
556
421
  path: z3.string().describe("Public /v1 read path"),
@@ -559,49 +424,11 @@ function registerIndexTools(server, clientProvider = getClient) {
559
424
  }, async (params) => jsonResponse(await clientProvider().batch(params.requests)));
560
425
  }
561
426
 
562
- // src/tools/projects.ts
563
- import { z as z4 } from "zod/v4";
564
- function registerProjectTools(server, clientProvider = getClient) {
565
- defineTool(server, "project_list", "List the account's projects. Requires an account-level (owner) API key.", {}, async () => jsonResponse(await clientProvider().projects.list()));
566
- defineTool(server, "project_get", "Get a single project by slug.", { slug: z4.string().describe("Project slug") }, async ({ slug }) => jsonResponse(await clientProvider().projects.get(slug)));
567
- defineTool(server, "project_create", "Create a project. The slug is derived from the name when omitted. Requires an account-level (owner) API key.", {
568
- name: z4.string().describe("Project name"),
569
- slug: z4.string().optional().describe("URL slug (auto-derived from name if omitted)"),
570
- network: z4.string().optional().describe("Network (e.g. mainnet, testnet)"),
571
- nodeRpc: z4.string().optional().describe("Custom node RPC URL")
572
- }, async ({ name, slug, network, nodeRpc }) => jsonResponse(await clientProvider().projects.create({
573
- name,
574
- slug,
575
- network,
576
- nodeRpc
577
- })));
578
- defineTool(server, "project_update", "Update a project. `slug` selects the project; `newSlug` renames it. Requires an account-level (owner) API key.", {
579
- slug: z4.string().describe("Slug of the project to update"),
580
- newSlug: z4.string().optional().describe("New slug (renames the project)"),
581
- name: z4.string().optional().describe("New display name"),
582
- network: z4.string().optional().describe("Network (e.g. mainnet, testnet)"),
583
- nodeRpc: z4.string().optional().describe("Custom node RPC URL"),
584
- settings: z4.record(z4.string(), z4.unknown()).optional().describe("Arbitrary project settings to merge")
585
- }, async ({ slug, newSlug, name, network, nodeRpc, settings }) => jsonResponse(await clientProvider().projects.update(slug, {
586
- ...newSlug !== undefined ? { slug: newSlug } : {},
587
- ...name !== undefined ? { name } : {},
588
- ...network !== undefined ? { network } : {},
589
- ...nodeRpc !== undefined ? { nodeRpc } : {},
590
- ...settings !== undefined ? { settings } : {}
591
- })));
592
- defineTool(server, "project_delete", "Delete a project permanently. The account's last remaining project cannot be deleted.", { slug: z4.string().describe("Project slug") }, async ({ slug }) => jsonResponse(await clientProvider().projects.delete(slug)));
593
- defineTool(server, "project_team_list", "List a project's team members and pending invitations.", { slug: z4.string().describe("Project slug") }, async ({ slug }) => jsonResponse(await clientProvider().projects.team(slug)));
594
- }
595
-
596
427
  // src/tools/scaffold.ts
597
- import {
598
- generateSubgraphCode,
599
- generateTraitSubgraph
600
- } from "@secondlayer/scaffold";
601
- import { TRAIT_STANDARDS as TRAIT_STANDARDS2 } from "@secondlayer/stacks/clarity";
602
- import { z as z5 } from "zod/v4";
603
- async function fetchAbi(contractId) {
604
- const contract = await getClient().contracts.get(contractId, {
428
+ import { generateSubgraphCode } from "@secondlayer/scaffold";
429
+ import { z as z4 } from "zod/v4";
430
+ async function fetchAbi(clientProvider, contractId) {
431
+ const contract = await clientProvider().contracts.get(contractId, {
605
432
  includeAbi: true
606
433
  });
607
434
  if (!contract)
@@ -612,134 +439,26 @@ async function fetchAbi(contractId) {
612
439
  }
613
440
  return { functions: abi.functions ?? [], maps: abi.maps ?? [] };
614
441
  }
615
- function registerScaffoldTools(server) {
616
- defineTool(server, "scaffold_from_trait", "Generate a deploy-ready subgraph scaffold that indexes EVERY contract conforming to a SIP trait (no specific contract needed) — sip-009 → an nft_transfer source, sip-010/sip-013 → ft_transfer. Use scaffold_from_contract/abi instead when targeting one contract. See the secondlayer://traits resource for valid trait ids.", {
617
- trait: z5.enum(TRAIT_STANDARDS2).describe("SIP standard to index (sip-009 | sip-010 | sip-013)"),
618
- subgraphName: z5.string().optional().describe("Override the subgraph name (defaults to <trait>-transfers)")
619
- }, async ({ trait, subgraphName }) => {
620
- const code = generateTraitSubgraph({ trait, name: subgraphName });
621
- return { content: [{ type: "text", text: code }] };
622
- });
442
+ function registerScaffoldTools(server, clientProvider = getClient) {
623
443
  defineTool(server, "scaffold_from_contract", "Generate a subgraph scaffold from a deployed Stacks contract. Fetches the ABI automatically.", {
624
- contractId: z5.string().describe("Fully qualified contract ID (e.g. SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01)"),
625
- subgraphName: z5.string().optional().describe("Override the subgraph name (defaults to contract name)")
444
+ contractId: z4.string().describe("Fully qualified contract ID (e.g. SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01)"),
445
+ subgraphName: z4.string().optional().describe("Override the subgraph name (defaults to contract name)")
626
446
  }, async ({ contractId, subgraphName }) => {
627
- const { functions, maps } = await fetchAbi(contractId);
447
+ const { functions, maps } = await fetchAbi(clientProvider, contractId);
628
448
  const code = generateSubgraphCode(contractId, functions, subgraphName, maps);
629
449
  return { content: [{ type: "text", text: code }] };
630
450
  });
631
- defineTool(server, "scaffold_from_abi", "Generate a subgraph scaffold from a provided ABI JSON. Use when you already have the ABI.", {
632
- abi: z5.string().describe("ABI JSON string (the full contract ABI object)"),
633
- contractId: z5.string().describe("Fully qualified contract ID"),
634
- subgraphName: z5.string().optional().describe("Override the subgraph name")
635
- }, async ({ abi, contractId, subgraphName }) => {
636
- let parsed;
637
- try {
638
- parsed = JSON.parse(abi);
639
- } catch {
640
- return {
641
- content: [{ type: "text", text: "Invalid ABI JSON" }],
642
- isError: true
643
- };
644
- }
645
- const code = generateSubgraphCode(contractId, parsed.functions ?? [], subgraphName, parsed.maps ?? []);
646
- return { content: [{ type: "text", text: code }] };
647
- });
648
451
  }
649
452
 
650
453
  // src/tools/streams.ts
651
- import { AuthError } from "@secondlayer/sdk";
652
- import { DECODED_EVENT_TYPES as DECODED_EVENT_TYPES3 } from "@secondlayer/shared";
653
- import { z as z6 } from "zod/v4";
654
- var STREAMS_EVENT_TYPES = DECODED_EVENT_TYPES3;
655
- async function withStreamsAuthHint(fn) {
656
- try {
657
- return await fn();
658
- } catch (err) {
659
- if (err instanceof AuthError) {
660
- throw Object.assign(new Error(err.message + keyHint), { status: 401 });
661
- }
662
- throw err;
663
- }
664
- }
665
454
  function registerStreamsTools(server, clientProvider = getClient) {
666
- defineTool(server, "streams_tip", "Get the current Streams chain tip (latest processed block + lag). Streams requires an API key (SL_API_KEY).", {}, async () => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.tip())));
667
- defineTool(server, "streams_events", "List raw chain events from the Streams firehose. Streams requires an API key (SL_API_KEY). Filter by event types, principals, contract, asset, or block range; page with cursor.", {
668
- types: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to include"),
669
- notTypes: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to exclude (applied after types)"),
670
- contractId: z6.string().optional().describe("Filter by contract id"),
671
- sender: z6.string().optional().describe("Filter by sender principal"),
672
- recipient: z6.string().optional().describe("Filter by recipient principal"),
673
- assetIdentifier: z6.string().optional().describe("Filter by asset identifier"),
674
- fromHeight: z6.number().optional().describe("Start block height (inclusive)"),
675
- toHeight: z6.number().optional().describe("End block height (inclusive)"),
676
- cursor: z6.string().optional().describe("Opaque cursor from a prior response"),
677
- limit: z6.number().optional().describe("Max events for this page")
678
- }, async (params) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.events.list(params))));
679
- 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).", {
680
- fromCursor: z6.string().optional().describe("Resume cursor from a prior consume (omit to start at genesis)"),
681
- types: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to include"),
682
- notTypes: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to exclude"),
683
- contractId: z6.string().optional().describe("Filter by contract id"),
684
- sender: z6.string().optional().describe("Filter by sender principal"),
685
- recipient: z6.string().optional().describe("Filter by recipient principal"),
686
- assetIdentifier: z6.string().optional().describe("Filter by asset identifier"),
687
- batchSize: z6.number().optional().describe("Events per page (1–1000, default 100)"),
688
- maxPages: z6.number().optional().describe("Max pages to walk this call (1–20, default 5)"),
689
- finalizedOnly: z6.boolean().optional().describe("Only emit finalized events; skip reorg handling")
690
- }, async (params) => withStreamsAuthHint(async () => {
691
- const maxPages = Math.min(Math.max(params.maxPages ?? 5, 1), 20);
692
- const batchSize = Math.min(Math.max(params.batchSize ?? 100, 1), 1000);
693
- const events = [];
694
- const reorgs = [];
695
- const result = await clientProvider().streams.events.consume({
696
- fromCursor: params.fromCursor ?? null,
697
- mode: "bounded",
698
- finalizedOnly: params.finalizedOnly,
699
- types: params.types,
700
- notTypes: params.notTypes,
701
- contractId: params.contractId,
702
- sender: params.sender,
703
- recipient: params.recipient,
704
- assetIdentifier: params.assetIdentifier,
705
- batchSize,
706
- maxPages,
707
- onBatch: (batch) => {
708
- events.push(...batch);
709
- },
710
- onReorg: (reorg) => {
711
- reorgs.push(reorg);
712
- }
713
- });
714
- return jsonResponse({
715
- events,
716
- reorgs,
717
- cursor: result.cursor,
718
- pages: result.pages
719
- });
720
- }));
721
- 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))));
722
- 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).", {
723
- heightOrHash: z6.string().describe("Block height (digits) or block hash (0x… string)")
724
- }, async ({ heightOrHash }) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.blocks.events(/^\d+$/.test(heightOrHash) ? Number(heightOrHash) : heightOrHash))));
725
- defineTool(server, "streams_reorgs", "List chain reorgs observed by Streams since a cursor. Streams requires an API key (SL_API_KEY).", {
726
- since: z6.string().describe("Cursor to list reorgs since (block:index or ISO timestamp)"),
727
- limit: z6.number().optional().describe("Max reorgs to return")
728
- }, async (params) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.reorgs.list(params))));
729
- defineTool(server, "streams_canonical", "Get the canonical block at a given height from Streams (height + hashes + is_canonical). Streams requires an API key (SL_API_KEY).", { height: z6.number().describe("Block height") }, async ({ height }) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.canonical(height))));
730
- defineTool(server, "streams_usage", "Your own Streams consumption (events today + this month) and tier limits (rate limit, retention). Streams requires an API key (SL_API_KEY).", {}, async () => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.usage())));
731
- defineTool(server, "streams_dumps", "List the Streams bulk parquet dumps manifest — coverage range, latest_finalized_cursor, and per-file metadata (block range, row count, size, sha256, signed URL). This is the cold backfill path for downloading all raw data; fetch the file URLs directly (e.g. with DuckDB). Requires the dumps base URL to be configured (SL_STREAMS_DUMPS_URL).", {}, async () => jsonResponse(await clientProvider().streams.dumps.list()));
455
+ defineTool(server, "streams_dumps", "List the Streams bulk parquet dumps manifest — coverage range, latest_finalized_cursor, and per-file metadata (block range, row count, size, sha256, signed URL). This is the cold backfill path for downloading all raw data; fetch the file URLs directly (e.g. with DuckDB). Requires the dumps base URL to be configured (SL_STREAMS_DUMPS_URL). Live Streams reads (tip, events, consume) are REST-only: see /v1/streams/* in the OpenAPI spec.", {}, async () => jsonResponse(await clientProvider().streams.dumps.list()));
732
456
  }
733
457
 
734
458
  // src/tools/subgraphs.ts
735
459
  import { bundleSubgraphCode } from "@secondlayer/bundler";
736
460
  import { ByoBreakingChangeError } from "@secondlayer/sdk";
737
- import {
738
- generateDrizzleSchema,
739
- generateKyselySchema,
740
- generatePrismaSchema
741
- } from "@secondlayer/subgraphs";
742
- import { z as z7 } from "zod/v4";
461
+ import { z as z5 } from "zod/v4";
743
462
  function registerSubgraphTools(server, clientProvider = getClient) {
744
463
  defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only, including visibility — public subgraphs are anon-readable at /v1/subgraphs/<name>.", {}, async () => {
745
464
  const { data } = await clientProvider().subgraphs.list();
@@ -752,38 +471,22 @@ function registerSubgraphTools(server, clientProvider = getClient) {
752
471
  ]
753
472
  };
754
473
  });
755
- defineTool(server, "subgraphs_get", "Get full details of a subgraph including schema, health, and table columns.", { name: z7.string().describe("Subgraph name") }, async ({ name }) => {
474
+ defineTool(server, "subgraphs_get", "Get full details of a subgraph including schema, health, and table columns.", { name: z5.string().describe("Subgraph name") }, async ({ name }) => {
756
475
  const detail = await clientProvider().subgraphs.get(name);
757
476
  return {
758
477
  content: [{ type: "text", text: JSON.stringify(detail, null, 2) }]
759
478
  };
760
479
  });
761
- defineTool(server, "subgraphs_spec", "Get generated API documentation for a subgraph. Defaults to compact agent schema; supports OpenAPI JSON and Markdown.", {
762
- name: z7.string().describe("Subgraph name"),
763
- format: z7.enum(["agent", "openapi", "markdown"]).optional().describe("Spec format to return. Defaults to agent."),
764
- serverUrl: z7.string().optional().describe("Override the server URL embedded in generated docs.")
765
- }, async ({ name, format = "agent", serverUrl }) => {
766
- const options = serverUrl ? { serverUrl } : undefined;
767
- const spec = format === "openapi" ? await clientProvider().subgraphs.openapi(name, options) : format === "markdown" ? await clientProvider().subgraphs.markdown(name, options) : await clientProvider().subgraphs.schema(name, options);
768
- return {
769
- content: [
770
- {
771
- type: "text",
772
- text: typeof spec === "string" ? spec : JSON.stringify(spec, null, 2)
773
- }
774
- ]
775
- };
776
- });
777
480
  defineTool(server, "subgraphs_query", 'Query rows from a subgraph table (max 200 rows). Filters support operators: "amount.gte": "1000", "sender.neq": "SP...", "name.like": "%token%". Available operators: eq, neq, gt, gte, lt, lte, like. To TAIL new rows (no streaming over MCP): sort=_id, order=desc for the latest, then poll forward with the filter {"_id.gt": "<last _id seen>"}, order=asc. Fetch one row by id with {"_id": "<id>"}. Public subgraphs are also keyless over HTTP at GET /v1/subgraphs/<name>/<table> — { rows, next_cursor, tip } envelope, resume with ?cursor=<next_cursor> + _order=asc|desc (no _offset/_sort on /v1); hand that URL to third parties.', {
778
- name: z7.string().describe("Subgraph name"),
779
- table: z7.string().describe("Table name"),
780
- filters: z7.record(z7.string(), z7.string()).optional().describe('Column filters — plain values or with operators (e.g. {"amount.gte": "1000", "sender": "SP..."})'),
781
- sort: z7.string().optional().describe("Column to sort by"),
782
- order: z7.enum(["asc", "desc"]).optional().describe("Sort order"),
783
- limit: z7.number().max(200).optional().describe("Max rows (default 50, max 200)"),
784
- offset: z7.number().optional().describe("Offset for pagination"),
785
- fields: z7.string().optional().describe('Comma-separated column list to return (e.g. "sender,amount")'),
786
- count: z7.boolean().optional().describe("If true, return row count instead of rows")
481
+ name: z5.string().describe("Subgraph name"),
482
+ table: z5.string().describe("Table name"),
483
+ filters: z5.record(z5.string(), z5.string()).optional().describe('Column filters — plain values or with operators (e.g. {"amount.gte": "1000", "sender": "SP..."})'),
484
+ sort: z5.string().optional().describe("Column to sort by"),
485
+ order: z5.enum(["asc", "desc"]).optional().describe("Sort order"),
486
+ limit: z5.number().max(200).optional().describe("Max rows (default 50, max 200)"),
487
+ offset: z5.number().optional().describe("Offset for pagination"),
488
+ fields: z5.string().optional().describe('Comma-separated column list to return (e.g. "sender,amount")'),
489
+ count: z5.boolean().optional().describe("If true, return row count instead of rows")
787
490
  }, async ({
788
491
  name,
789
492
  table,
@@ -815,25 +518,10 @@ function registerSubgraphTools(server, clientProvider = getClient) {
815
518
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
816
519
  };
817
520
  });
818
- 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.`, {
819
- name: z7.string().describe("Subgraph name"),
820
- table: z7.string().describe("Table name"),
821
- filters: z7.record(z7.string(), z7.string()).optional().describe('Column filters — plain values or with operators (e.g. {"amount.gte": "1000", "sender": "SP..."})'),
822
- count: z7.boolean().optional().describe("Include COUNT(*) of matching rows"),
823
- countDistinct: z7.array(z7.string()).optional().describe("Columns to count distinct values of"),
824
- sum: z7.array(z7.string()).optional().describe("Numeric columns to sum (lossless string result)"),
825
- min: z7.array(z7.string()).optional().describe("Numeric columns to take the minimum of"),
826
- max: z7.array(z7.string()).optional().describe("Numeric columns to take the maximum of")
827
- }, async ({ name, table, filters, count, countDistinct, sum, min, max }) => {
828
- const result = await clientProvider().subgraphs.queryTableAggregate(name, table, { filters, count, countDistinct, sum, min, max });
829
- return {
830
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
831
- };
832
- });
833
- defineTool(server, "subgraphs_reindex", "Reindex a subgraph from a specific block range. Returns an operationId — poll subgraphs_operation to track progress to completion.", {
834
- name: z7.string().describe("Subgraph name"),
835
- fromBlock: z7.number().optional().describe("Start block (defaults to beginning)"),
836
- toBlock: z7.number().optional().describe("End block (defaults to latest)")
521
+ defineTool(server, "subgraphs_reindex", "Reindex a subgraph from a specific block range. Returns an operationId — check subgraphs_get (health) or the REST operations endpoint to track progress to completion.", {
522
+ name: z5.string().describe("Subgraph name"),
523
+ fromBlock: z5.number().optional().describe("Start block (defaults to beginning)"),
524
+ toBlock: z5.number().optional().describe("End block (defaults to latest)")
837
525
  }, async ({ name, fromBlock, toBlock }) => {
838
526
  const result = await clientProvider().subgraphs.reindex(name, {
839
527
  fromBlock,
@@ -843,19 +531,10 @@ function registerSubgraphTools(server, clientProvider = getClient) {
843
531
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
844
532
  };
845
533
  });
846
- defineTool(server, "subgraphs_operation", "Check reindex/backfill progress. With operationId, returns that operation's status (poll until status is completed/failed/cancelled); without it, lists recent operations for the subgraph.", {
847
- name: z7.string().describe("Subgraph name"),
848
- operationId: z7.string().optional().describe("Operation id from reindex/backfill/stop; omit to list recent operations")
849
- }, async ({ name, operationId }) => {
850
- const result = operationId ? await clientProvider().subgraphs.getOperation(name, operationId) : await clientProvider().subgraphs.operations(name);
851
- return {
852
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
853
- };
854
- });
855
- defineTool(server, "subgraphs_backfill", "Backfill a subgraph over a block range. Non-destructive forward fill (does not drop existing data) — unlike subgraphs_reindex, and the only data-fill path for BYO subgraphs (reindex is blocked there). Both blocks required. Returns an operationId — poll subgraphs_operation to track progress.", {
856
- name: z7.string().describe("Subgraph name"),
857
- fromBlock: z7.number().int().nonnegative().describe("Start block (inclusive)"),
858
- toBlock: z7.number().int().nonnegative().describe("End block (inclusive)")
534
+ defineTool(server, "subgraphs_backfill", "Backfill a subgraph over a block range. Non-destructive forward fill (does not drop existing data) — unlike subgraphs_reindex, and the only data-fill path for BYO subgraphs (reindex is blocked there). Both blocks required. Returns an operationId — check subgraphs_get (health) or the REST operations endpoint to track progress.", {
535
+ name: z5.string().describe("Subgraph name"),
536
+ fromBlock: z5.number().int().nonnegative().describe("Start block (inclusive)"),
537
+ toBlock: z5.number().int().nonnegative().describe("End block (inclusive)")
859
538
  }, async ({ name, fromBlock, toBlock }) => {
860
539
  const result = await clientProvider().subgraphs.backfill(name, {
861
540
  fromBlock,
@@ -865,17 +544,17 @@ function registerSubgraphTools(server, clientProvider = getClient) {
865
544
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
866
545
  };
867
546
  });
868
- defineTool(server, "subgraphs_stop", "Cancel an in-flight reindex or backfill operation for a subgraph. Returns the stop request status; poll subgraphs_operation to confirm it reaches a terminal state.", { name: z7.string().describe("Subgraph name") }, async ({ name }) => {
547
+ defineTool(server, "subgraphs_stop", "Cancel an in-flight reindex or backfill operation for a subgraph. Returns the stop request status; check subgraphs_get (health) or the REST operations endpoint to confirm it reaches a terminal state.", { name: z5.string().describe("Subgraph name") }, async ({ name }) => {
869
548
  const result = await clientProvider().subgraphs.stop(name);
870
549
  return {
871
550
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
872
551
  };
873
552
  });
874
553
  defineTool(server, "subgraphs_gaps", "List indexing gaps (missing block ranges) for a subgraph. Each gap reports start/end/size, reason, and detected/resolved timestamps. Feed an unresolved gap's range into subgraphs_backfill to fill it. Defaults to unresolved gaps.", {
875
- name: z7.string().describe("Subgraph name"),
876
- limit: z7.number().int().positive().optional().describe("Max gaps to return"),
877
- offset: z7.number().int().nonnegative().optional().describe("Pagination offset"),
878
- resolved: z7.boolean().optional().describe("Filter by resolved state (omit for unresolved only)")
554
+ name: z5.string().describe("Subgraph name"),
555
+ limit: z5.number().int().positive().optional().describe("Max gaps to return"),
556
+ offset: z5.number().int().nonnegative().optional().describe("Pagination offset"),
557
+ resolved: z5.boolean().optional().describe("Filter by resolved state (omit for unresolved only)")
879
558
  }, async ({ name, limit, offset, resolved }) => {
880
559
  const result = await clientProvider().subgraphs.gaps(name, {
881
560
  limit,
@@ -886,16 +565,16 @@ function registerSubgraphTools(server, clientProvider = getClient) {
886
565
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
887
566
  };
888
567
  });
889
- defineTool(server, "subgraphs_delete", "Delete a subgraph permanently.", { name: z7.string().describe("Subgraph name") }, async ({ name }) => {
568
+ defineTool(server, "subgraphs_delete", "Delete a subgraph permanently.", { name: z5.string().describe("Subgraph name") }, async ({ name }) => {
890
569
  const result = await clientProvider().subgraphs.delete(name);
891
570
  return { content: [{ type: "text", text: result.message }] };
892
571
  });
893
572
  defineTool(server, "subgraphs_deploy", "Deploy a subgraph from TypeScript code. Pass the full defineSubgraph() source — it will be bundled, validated, and deployed. Optional startBlock overrides the source definition for this deploy. Set dryRun to validate and preview the schema/DDL without writing anything. Set databaseUrl to deploy to your own Postgres (BYO data plane) — the server verifies the connection first; with dryRun it returns the DDL + grant script. A breaking BYO schema change is refused and returns a migration plan (drop + rebuild DDL) instead of deploying. Visibility defaults: managed deploys are public (anon-readable at /v1/subgraphs/<name>, name claimed in the global public namespace), BYO deploys are private. Call `subgraphs_reindex` separately if you need a forced reindex.", {
894
- code: z7.string().describe("TypeScript source code containing a defineSubgraph() call"),
895
- startBlock: z7.number().int().nonnegative().optional().describe("Override the definition startBlock for this deploy"),
896
- databaseUrl: z7.string().optional().describe("BYO data plane: Postgres connection string to host the subgraph's schema and rows in your own database"),
897
- dryRun: z7.boolean().optional().describe("Validate and preview the deploy (schema/DDL, BYO connection) without writing changes"),
898
- visibility: z7.enum(["public", "private"]).optional().describe("Read visibility: public = anon /v1 reads + global name claim; private = owning account's key only. Defaults: managed → public, BYO → private.")
573
+ code: z5.string().describe("TypeScript source code containing a defineSubgraph() call"),
574
+ startBlock: z5.number().int().nonnegative().optional().describe("Override the definition startBlock for this deploy"),
575
+ databaseUrl: z5.string().optional().describe("BYO data plane: Postgres connection string to host the subgraph's schema and rows in your own database"),
576
+ dryRun: z5.boolean().optional().describe("Validate and preview the deploy (schema/DDL, BYO connection) without writing changes"),
577
+ visibility: z5.enum(["public", "private"]).optional().describe("Read visibility: public = anon /v1 reads + global name claim; private = owning account's key only. Defaults: managed → public, BYO → private.")
899
578
  }, async ({ code, startBlock, databaseUrl, dryRun, visibility }) => {
900
579
  const bundled = await bundleSubgraphCode(code);
901
580
  try {
@@ -929,58 +608,23 @@ function registerSubgraphTools(server, clientProvider = getClient) {
929
608
  throw err;
930
609
  }
931
610
  });
932
- defineTool(server, "subgraphs_publish", "Make a subgraph publicly readable at /v1/subgraphs/<name> — anyone (or any agent) can read it without a key. Claims the name in the global public namespace; fails with PUBLIC_NAME_TAKEN if another account holds it.", { name: z7.string().describe("Subgraph name") }, async ({ name }) => {
611
+ defineTool(server, "subgraphs_publish", "Make a subgraph publicly readable at /v1/subgraphs/<name> — anyone (or any agent) can read it without a key. Claims the name in the global public namespace; fails with PUBLIC_NAME_TAKEN if another account holds it.", { name: z5.string().describe("Subgraph name") }, async ({ name }) => {
933
612
  const result = await clientProvider().subgraphs.publish(name);
934
613
  return {
935
614
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
936
615
  };
937
616
  });
938
- defineTool(server, "subgraphs_unpublish", "Make a subgraph private again — /v1 reads then require the owning account's bearer key, and the global public name claim is released.", { name: z7.string().describe("Subgraph name") }, async ({ name }) => {
617
+ defineTool(server, "subgraphs_unpublish", "Make a subgraph private again — /v1 reads then require the owning account's bearer key, and the global public name claim is released.", { name: z5.string().describe("Subgraph name") }, async ({ name }) => {
939
618
  const result = await clientProvider().subgraphs.unpublish(name);
940
619
  return {
941
620
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
942
621
  };
943
622
  });
944
- defineTool(server, "subgraphs_read_source", "Fetch the deployed TypeScript source of a subgraph (plus its stored version). Returns a readOnly payload for subgraphs deployed before source capture — in that case the caller should redeploy via CLI before editing.", { name: z7.string().describe("Subgraph name") }, async ({ name }) => {
945
- const source = await clientProvider().subgraphs.getSource(name);
946
- return {
947
- content: [{ type: "text", text: JSON.stringify(source, null, 2) }]
948
- };
949
- });
950
- 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.", {
951
- code: z7.string().optional().describe("defineSubgraph() source (mutually exclusive with name)"),
952
- name: z7.string().optional().describe("Deployed subgraph name (mutually exclusive with code)"),
953
- target: z7.enum(["prisma", "drizzle", "kysely"]).optional().describe("ORM target (default prisma)"),
954
- schemaName: z7.string().optional().describe("Postgres schema name (defaults to subgraph_<name>)")
955
- }, async ({ code, name, target = "prisma", schemaName }) => {
956
- if (code && name || !code && !name) {
957
- throw new Error("Provide exactly one of `code` or `name`.");
958
- }
959
- let source = code;
960
- if (name) {
961
- const stored = await clientProvider().subgraphs.getSource(name);
962
- if (!stored.sourceCode) {
963
- throw new Error(`Subgraph "${name}" has no captured source (deployed before source capture). Redeploy it, or pass its source as \`code\`.`);
964
- }
965
- source = stored.sourceCode;
966
- }
967
- const bundled = await bundleSubgraphCode(source);
968
- const def = {
969
- name: bundled.name,
970
- version: bundled.version,
971
- description: bundled.description,
972
- sources: bundled.sources,
973
- schema: bundled.schema,
974
- handlers: {}
975
- };
976
- const out = target === "drizzle" ? generateDrizzleSchema(def, { schemaName }) : target === "kysely" ? generateKyselySchema(def, { schemaName }) : generatePrismaSchema(def, { schemaName });
977
- return { content: [{ type: "text", text: out }] };
978
- });
979
623
  }
980
624
 
981
625
  // src/tools/subscriptions.ts
982
626
  import { CHAIN_TRIGGER_TYPES } from "@secondlayer/shared";
983
- import { z as z8 } from "zod/v4";
627
+ import { z as z6 } from "zod/v4";
984
628
  function registerSubscriptionTools(server, clientProvider = getClient) {
985
629
  defineTool(server, "subscriptions_list", "List all subscriptions for the current account. Returns summary fields (no secrets).", {}, async () => {
986
630
  const { data } = await clientProvider().subscriptions.list();
@@ -988,34 +632,34 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
988
632
  content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
989
633
  };
990
634
  });
991
- defineTool(server, "subscriptions_get", "Get full detail for a subscription (filter, auth, retry config, circuit state).", { id: z8.string().describe("Subscription id") }, async ({ id }) => {
635
+ defineTool(server, "subscriptions_get", "Get full detail for a subscription (filter, auth, retry config, circuit state).", { id: z6.string().describe("Subscription id") }, async ({ id }) => {
992
636
  const detail = await clientProvider().subscriptions.get(id);
993
637
  return {
994
638
  content: [{ type: "text", text: JSON.stringify(detail, null, 2) }]
995
639
  };
996
640
  });
997
641
  defineTool(server, "subscriptions_create", "Create a subscription. Two kinds (mutually exclusive): a SUBGRAPH subscription fires on a subgraph table's rows (set subgraphName + tableName + optional filter); a CHAIN subscription fires on raw chain events with no subgraph (set triggers). Returns `signingSecret` ONCE — forward it to the user so they can wire it into their receiver.", {
998
- name: z8.string().describe("Human-readable name, unique per account"),
999
- subgraphName: z8.string().optional().describe("Subgraph to subscribe to (subgraph subscription)"),
1000
- tableName: z8.string().optional().describe("Table within the subgraph (subgraph subscription)"),
1001
- triggers: z8.array(z8.object({
1002
- type: z8.enum(CHAIN_TRIGGER_TYPES),
1003
- contractId: z8.string().optional(),
1004
- functionName: z8.string().optional(),
1005
- caller: z8.string().optional(),
1006
- sender: z8.string().optional(),
1007
- recipient: z8.string().optional(),
1008
- assetIdentifier: z8.string().optional(),
1009
- deployer: z8.string().optional(),
1010
- contractName: z8.string().optional(),
1011
- topic: z8.string().optional(),
1012
- lockedAddress: z8.string().optional(),
1013
- trait: z8.string().optional(),
1014
- minAmount: z8.union([z8.string(), z8.number()]).optional(),
1015
- maxAmount: z8.union([z8.string(), z8.number()]).optional()
642
+ name: z6.string().describe("Human-readable name, unique per account"),
643
+ subgraphName: z6.string().optional().describe("Subgraph to subscribe to (subgraph subscription)"),
644
+ tableName: z6.string().optional().describe("Table within the subgraph (subgraph subscription)"),
645
+ triggers: z6.array(z6.object({
646
+ type: z6.enum(CHAIN_TRIGGER_TYPES),
647
+ contractId: z6.string().optional(),
648
+ functionName: z6.string().optional(),
649
+ caller: z6.string().optional(),
650
+ sender: z6.string().optional(),
651
+ recipient: z6.string().optional(),
652
+ assetIdentifier: z6.string().optional(),
653
+ deployer: z6.string().optional(),
654
+ contractName: z6.string().optional(),
655
+ topic: z6.string().optional(),
656
+ lockedAddress: z6.string().optional(),
657
+ trait: z6.string().optional(),
658
+ minAmount: z6.union([z6.string(), z6.number()]).optional(),
659
+ maxAmount: z6.union([z6.string(), z6.number()]).optional()
1016
660
  })).optional().describe("Chain triggers (chain subscription) — provide INSTEAD of subgraphName/tableName. Each targets a raw chain event/tx; string fields accept `*` wildcards, `trait` scopes to a SIP/trait. Per-type accepted fields: see the secondlayer://chain-triggers resource. Forward-looking: starts at chain tip, no backfill."),
1017
- url: z8.string().describe("Webhook URL"),
1018
- format: z8.enum([
661
+ url: z6.string().describe("Webhook URL"),
662
+ format: z6.enum([
1019
663
  "standard-webhooks",
1020
664
  "inngest",
1021
665
  "trigger",
@@ -1023,9 +667,9 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
1023
667
  "cloudevents",
1024
668
  "raw"
1025
669
  ]).optional().describe("Wire format (default standard-webhooks)"),
1026
- runtime: z8.enum(["inngest", "trigger", "cloudflare", "node"]).optional().describe("Receiver runtime label (display only)"),
1027
- filter: z8.record(z8.string(), z8.unknown()).optional().describe('Scalar filter DSL, e.g. {"amount": {"gte": 100}, "sender": "SP..."}'),
1028
- authConfig: z8.record(z8.string(), z8.unknown()).optional().describe('Receiver auth sent with each delivery, e.g. {"type": "bearer", "token": "..."}')
670
+ runtime: z6.enum(["inngest", "trigger", "cloudflare", "node"]).optional().describe("Receiver runtime label (display only)"),
671
+ filter: z6.record(z6.string(), z6.unknown()).optional().describe('Scalar filter DSL, e.g. {"amount": {"gte": 100}, "sender": "SP..."}'),
672
+ authConfig: z6.record(z6.string(), z6.unknown()).optional().describe('Receiver auth sent with each delivery, e.g. {"type": "bearer", "token": "..."}')
1029
673
  }, async (input) => {
1030
674
  const res = await clientProvider().subscriptions.create(input);
1031
675
  return {
@@ -1033,12 +677,12 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
1033
677
  };
1034
678
  });
1035
679
  defineTool(server, "subscriptions_update", "Patch a subscription (name, url, filter, authConfig, format, runtime, retry, timeout, concurrency).", {
1036
- id: z8.string(),
1037
- name: z8.string().optional().describe("Rename the subscription"),
1038
- url: z8.string().optional(),
1039
- filter: z8.record(z8.string(), z8.unknown()).optional(),
1040
- authConfig: z8.record(z8.string(), z8.unknown()).optional().describe("Receiver auth sent with each delivery (bearer/etc.)"),
1041
- format: z8.enum([
680
+ id: z6.string(),
681
+ name: z6.string().optional().describe("Rename the subscription"),
682
+ url: z6.string().optional(),
683
+ filter: z6.record(z6.string(), z6.unknown()).optional(),
684
+ authConfig: z6.record(z6.string(), z6.unknown()).optional().describe("Receiver auth sent with each delivery (bearer/etc.)"),
685
+ format: z6.enum([
1042
686
  "standard-webhooks",
1043
687
  "inngest",
1044
688
  "trigger",
@@ -1046,51 +690,33 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
1046
690
  "cloudevents",
1047
691
  "raw"
1048
692
  ]).optional(),
1049
- runtime: z8.enum(["inngest", "trigger", "cloudflare", "node"]).nullable().optional(),
1050
- maxRetries: z8.number().int().min(0).optional(),
1051
- timeoutMs: z8.number().int().min(100).optional(),
1052
- concurrency: z8.number().int().min(1).optional()
693
+ runtime: z6.enum(["inngest", "trigger", "cloudflare", "node"]).nullable().optional(),
694
+ maxRetries: z6.number().int().min(0).optional(),
695
+ timeoutMs: z6.number().int().min(100).optional(),
696
+ concurrency: z6.number().int().min(1).optional()
1053
697
  }, async ({ id, ...patch }) => {
1054
698
  const res = await clientProvider().subscriptions.update(id, patch);
1055
699
  return {
1056
700
  content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
1057
701
  };
1058
702
  });
1059
- defineTool(server, "subscriptions_pause", "Pause a subscription. Pending rows remain queued until resumed.", { id: z8.string() }, async ({ id }) => {
1060
- const res = await clientProvider().subscriptions.pause(id);
1061
- return {
1062
- content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
1063
- };
1064
- });
1065
- defineTool(server, "subscriptions_resume", "Resume a paused or circuit-open subscription and reset circuit failures.", { id: z8.string() }, async ({ id }) => {
1066
- const res = await clientProvider().subscriptions.resume(id);
1067
- return {
1068
- content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
1069
- };
1070
- });
1071
- defineTool(server, "subscriptions_delete", "Delete a subscription. Pending outbox rows are cascade-deleted.", { id: z8.string() }, async ({ id }) => {
703
+ defineTool(server, "subscriptions_delete", "Delete a subscription. Pending outbox rows are cascade-deleted.", { id: z6.string() }, async ({ id }) => {
1072
704
  const res = await clientProvider().subscriptions.delete(id);
1073
705
  return {
1074
706
  content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
1075
707
  };
1076
708
  });
1077
- defineTool(server, "subscriptions_rotate_secret", "Rotate a subscription signing secret. Returns the new plaintext secret once.", { id: z8.string() }, async ({ id }) => {
1078
- const res = await clientProvider().subscriptions.rotateSecret(id);
1079
- return {
1080
- content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
1081
- };
1082
- });
1083
- 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 }) => {
709
+ 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. Returns {ok, statusCode, error, durationMs, deliveryId}.", { id: z6.string().describe("Subscription id") }, async ({ id }) => {
1084
710
  const res = await clientProvider().subscriptions.test(id);
1085
711
  return {
1086
712
  content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
1087
713
  };
1088
714
  });
1089
715
  defineTool(server, "subscriptions_replay", "Replay a block range for a subscription. Replays run at 10% of batch capacity — use sparingly. Pass `force` (a short idempotency suffix) to re-run a range that was already replayed.", {
1090
- id: z8.string(),
1091
- fromBlock: z8.number().int().nonnegative(),
1092
- toBlock: z8.number().int().nonnegative(),
1093
- force: z8.string().optional().describe("Idempotency suffix to force a duplicate replay of the range")
716
+ id: z6.string(),
717
+ fromBlock: z6.number().int().nonnegative(),
718
+ toBlock: z6.number().int().nonnegative(),
719
+ force: z6.string().optional().describe("Idempotency suffix to force a duplicate replay of the range")
1094
720
  }, async ({ id, fromBlock, toBlock, force }) => {
1095
721
  const res = await clientProvider().subscriptions.replay(id, {
1096
722
  fromBlock,
@@ -1101,27 +727,6 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
1101
727
  content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
1102
728
  };
1103
729
  });
1104
- defineTool(server, "subscriptions_dead", "Return the last 100 dead-letter outbox rows for a subscription.", { id: z8.string() }, async ({ id }) => {
1105
- const { data } = await clientProvider().subscriptions.dead(id);
1106
- return {
1107
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
1108
- };
1109
- });
1110
- defineTool(server, "subscriptions_requeue_dead", "Requeue one dead-letter outbox row for delivery retry.", {
1111
- id: z8.string(),
1112
- outboxId: z8.string()
1113
- }, async ({ id, outboxId }) => {
1114
- const res = await clientProvider().subscriptions.requeueDead(id, outboxId);
1115
- return {
1116
- content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
1117
- };
1118
- });
1119
- defineTool(server, "subscriptions_recent_deliveries", "Return the last 100 delivery attempts (attempt #, status code, duration, truncated response).", { id: z8.string() }, async ({ id }) => {
1120
- const { data } = await clientProvider().subscriptions.recentDeliveries(id);
1121
- return {
1122
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
1123
- };
1124
- });
1125
730
  }
1126
731
 
1127
732
  // src/server.ts
@@ -1136,7 +741,6 @@ function createServer() {
1136
741
  registerSubgraphTools(server);
1137
742
  registerSubscriptionTools(server);
1138
743
  registerAccountTools(server);
1139
- registerProjectTools(server);
1140
744
  registerIndexTools(server);
1141
745
  registerStreamsTools(server);
1142
746
  registerContractTools(server);
@@ -1147,5 +751,5 @@ export {
1147
751
  createServer
1148
752
  };
1149
753
 
1150
- //# debugId=C349C261EC92DE8964756E2164756E21
754
+ //# debugId=D0D0C7F86B1A466C64756E2164756E21
1151
755
  //# sourceMappingURL=index.js.map