@secondlayer/mcp 3.7.0 → 3.9.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
@@ -58,11 +58,14 @@ async function apiRequest(method, path, body) {
58
58
 
59
59
  // src/lib/format.ts
60
60
  function formatSubgraphSummary(s) {
61
+ const apiUrl = process.env.SECONDLAYER_API_URL || "https://api.secondlayer.tools";
61
62
  return {
62
63
  name: s.name,
63
64
  status: s.status,
64
65
  tables: Array.isArray(s.tables) ? s.tables : Object.keys(s.tables),
65
- lastProcessedBlock: s.lastProcessedBlock
66
+ lastProcessedBlock: s.lastProcessedBlock,
67
+ ...s.visibility ? { visibility: s.visibility } : {},
68
+ ...s.visibility === "public" ? { publicUrl: `${apiUrl}/v1/subgraphs/${s.name}` } : {}
66
69
  };
67
70
  }
68
71
  function withCap(items, cap) {
@@ -214,7 +217,7 @@ var READ_AUTH_TIERS = {
214
217
  datasets: "open — no API key required",
215
218
  index: "anonymous reads allowed; free-tier API keys are rejected (Build+ required)",
216
219
  streams: "API key required (SL_API_KEY) — keyless calls return 401",
217
- subgraphs: "reads public during open beta; writes require an API key"
220
+ subgraphs: "public subgraphs anon-readable at /v1/subgraphs/<name>/<table> ({ rows, next_cursor, tip } cursor envelope); private subgraphs need the owning account's API key (anon → 404); writes require an API key"
218
221
  };
219
222
  async function buildContext(deps = { clientProvider: getClient }) {
220
223
  const unavailable = "unavailable: set SL_API_KEY";
@@ -368,6 +371,7 @@ function registerAccountTools(server) {
368
371
  }
369
372
 
370
373
  // src/tools/contracts.ts
374
+ import { generateContractInterface } from "@secondlayer/scaffold";
371
375
  import { z as z2 } from "zod/v4";
372
376
  function registerContractTools(server, clientProvider = getClient) {
373
377
  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.', {
@@ -377,6 +381,47 @@ function registerContractTools(server, clientProvider = getClient) {
377
381
  limit: z2.number().optional().describe("Page size, 1–500 (default 100)"),
378
382
  cursor: z2.string().optional().describe("Opaque cursor from a prior response's next_cursor")
379
383
  }, async (params) => jsonResponse(await clientProvider().contracts.list(params)));
384
+ 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.", {
385
+ contractId: z2.string().describe("Fully qualified contract id (e.g. SP….amm-pool-v2-01)")
386
+ }, async ({ contractId }) => {
387
+ const contract = await clientProvider().contracts.get(contractId, {
388
+ includeAbi: true
389
+ });
390
+ return contract ? jsonResponse(contract) : jsonResponse({
391
+ error: {
392
+ type: "not_found",
393
+ status: 404,
394
+ message: `Contract not in registry: ${contractId}`
395
+ }
396
+ }, true);
397
+ });
398
+ 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.", {
399
+ contractId: z2.string().describe("Fully qualified contract id (e.g. SP….amm-pool-v2-01)")
400
+ }, async ({ contractId }) => {
401
+ const c = await clientProvider().contracts.get(contractId, {
402
+ includeAbi: true
403
+ });
404
+ if (!c || !c.abi) {
405
+ return jsonResponse({
406
+ error: {
407
+ type: "not_found",
408
+ status: 404,
409
+ message: `Contract not in registry: ${contractId}`
410
+ }
411
+ }, true);
412
+ }
413
+ const [address, contractName] = contractId.split(".");
414
+ const name = (contractName ?? contractId).replace(/[^a-zA-Z0-9]/g, "_");
415
+ const code = generateContractInterface([
416
+ {
417
+ name,
418
+ address,
419
+ contractName: contractName ?? "",
420
+ abi: c.abi
421
+ }
422
+ ]);
423
+ return { content: [{ type: "text", text: code }] };
424
+ });
380
425
  }
381
426
 
382
427
  // src/tools/datasets.ts
@@ -467,6 +512,10 @@ function registerIndexTools(server, clientProvider = getClient) {
467
512
  const tx = await clientProvider().index.transactions.get(txId);
468
513
  return tx ? jsonResponse(tx) : notFound(`No transaction for ${txId}`);
469
514
  });
515
+ 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: z4.string().describe("Transaction id (0x… hash)") }, async ({ txId }) => {
516
+ const proof = await clientProvider().index.transactions.getProof(txId);
517
+ return proof ? jsonResponse(proof) : notFound(`No proof available for ${txId}`);
518
+ });
470
519
  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).", {
471
520
  ...heightFilters,
472
521
  functionName: z4.string().optional().describe("Filter by PoX function name"),
@@ -531,25 +580,32 @@ function registerProjectTools(server, clientProvider = getClient) {
531
580
  }
532
581
 
533
582
  // src/tools/scaffold.ts
534
- import { generateSubgraphCode } from "@secondlayer/scaffold";
583
+ import {
584
+ generateSubgraphCode,
585
+ generateTraitSubgraph
586
+ } from "@secondlayer/scaffold";
587
+ import { TRAIT_STANDARDS as TRAIT_STANDARDS2 } from "@secondlayer/stacks/clarity";
535
588
  import { z as z6 } from "zod/v4";
536
- var API_BASE = process.env.SECONDLAYER_API_URL || "https://api.secondlayer.tools";
537
589
  async function fetchAbi(contractId) {
538
- const res = await fetch(`${API_BASE}/api/node/contracts/${contractId}/abi`, {
539
- signal: AbortSignal.timeout(1e4)
590
+ const contract = await getClient().contracts.get(contractId, {
591
+ includeAbi: true
540
592
  });
541
- if (!res.ok) {
542
- if (res.status === 404)
543
- throw new Error(`Contract not found: ${contractId}`);
544
- throw new Error(`Failed to fetch ABI: HTTP ${res.status}`);
593
+ if (!contract)
594
+ throw new Error(`Contract not found: ${contractId}`);
595
+ const abi = contract.abi;
596
+ if (!abi) {
597
+ throw new Error(`No ABI available for ${contractId} (abi_status: ${contract.abi_status})`);
545
598
  }
546
- const abi = await res.json();
547
- return {
548
- functions: abi.functions ?? [],
549
- maps: abi.maps ?? []
550
- };
599
+ return { functions: abi.functions ?? [], maps: abi.maps ?? [] };
551
600
  }
552
601
  function registerScaffoldTools(server) {
602
+ 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.", {
603
+ trait: z6.enum(TRAIT_STANDARDS2).describe("SIP standard to index (sip-009 | sip-010 | sip-013)"),
604
+ subgraphName: z6.string().optional().describe("Override the subgraph name (defaults to <trait>-transfers)")
605
+ }, async ({ trait, subgraphName }) => {
606
+ const code = generateTraitSubgraph({ trait, name: subgraphName });
607
+ return { content: [{ type: "text", text: code }] };
608
+ });
553
609
  defineTool(server, "scaffold_from_contract", "Generate a subgraph scaffold from a deployed Stacks contract. Fetches the ABI automatically.", {
554
610
  contractId: z6.string().describe("Fully qualified contract ID (e.g. SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01)"),
555
611
  subgraphName: z6.string().optional().describe("Override the subgraph name (defaults to contract name)")
@@ -671,7 +727,7 @@ import {
671
727
  } from "@secondlayer/subgraphs";
672
728
  import { z as z8 } from "zod/v4";
673
729
  function registerSubgraphTools(server, clientProvider = getClient) {
674
- defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only.", {}, async () => {
730
+ defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only, including visibility — public subgraphs are anon-readable at /v1/subgraphs/<name>.", {}, async () => {
675
731
  const { data } = await clientProvider().subgraphs.list();
676
732
  return {
677
733
  content: [
@@ -704,7 +760,7 @@ function registerSubgraphTools(server, clientProvider = getClient) {
704
760
  ]
705
761
  };
706
762
  });
707
- 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>"}.', {
763
+ 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.', {
708
764
  name: z8.string().describe("Subgraph name"),
709
765
  table: z8.string().describe("Table name"),
710
766
  filters: z8.record(z8.string(), z8.string()).optional().describe('Column filters — plain values or with operators (e.g. {"amount.gte": "1000", "sender": "SP..."})'),
@@ -820,12 +876,13 @@ function registerSubgraphTools(server, clientProvider = getClient) {
820
876
  const result = await clientProvider().subgraphs.delete(name);
821
877
  return { content: [{ type: "text", text: result.message }] };
822
878
  });
823
- 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. Call `subgraphs_reindex` separately if you need a forced reindex.", {
879
+ 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.", {
824
880
  code: z8.string().describe("TypeScript source code containing a defineSubgraph() call"),
825
881
  startBlock: z8.number().int().nonnegative().optional().describe("Override the definition startBlock for this deploy"),
826
882
  databaseUrl: z8.string().optional().describe("BYO data plane: Postgres connection string to host the subgraph's schema and rows in your own database"),
827
- dryRun: z8.boolean().optional().describe("Validate and preview the deploy (schema/DDL, BYO connection) without writing changes")
828
- }, async ({ code, startBlock, databaseUrl, dryRun }) => {
883
+ dryRun: z8.boolean().optional().describe("Validate and preview the deploy (schema/DDL, BYO connection) without writing changes"),
884
+ visibility: z8.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.")
885
+ }, async ({ code, startBlock, databaseUrl, dryRun, visibility }) => {
829
886
  const bundled = await bundleSubgraphCode(code);
830
887
  try {
831
888
  const result = await clientProvider().subgraphs.deploy({
@@ -838,7 +895,8 @@ function registerSubgraphTools(server, clientProvider = getClient) {
838
895
  sourceCode: code,
839
896
  ...startBlock !== undefined ? { startBlock } : {},
840
897
  ...databaseUrl !== undefined ? { databaseUrl } : {},
841
- ...dryRun !== undefined ? { dryRun } : {}
898
+ ...dryRun !== undefined ? { dryRun } : {},
899
+ ...visibility !== undefined ? { visibility } : {}
842
900
  });
843
901
  return {
844
902
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
@@ -857,6 +915,18 @@ function registerSubgraphTools(server, clientProvider = getClient) {
857
915
  throw err;
858
916
  }
859
917
  });
918
+ 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: z8.string().describe("Subgraph name") }, async ({ name }) => {
919
+ const result = await clientProvider().subgraphs.publish(name);
920
+ return {
921
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
922
+ };
923
+ });
924
+ 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: z8.string().describe("Subgraph name") }, async ({ name }) => {
925
+ const result = await clientProvider().subgraphs.unpublish(name);
926
+ return {
927
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
928
+ };
929
+ });
860
930
  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: z8.string().describe("Subgraph name") }, async ({ name }) => {
861
931
  const source = await clientProvider().subgraphs.getSource(name);
862
932
  return {
@@ -1066,5 +1136,5 @@ var server = createServer();
1066
1136
  var transport = new StdioServerTransport;
1067
1137
  await server.connect(transport);
1068
1138
 
1069
- //# debugId=A418D04474BCA8ED64756E2164756E21
1139
+ //# debugId=9CFDE0E3EC20CFB264756E2164756E21
1070
1140
  //# sourceMappingURL=bin.js.map