@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/index.js CHANGED
@@ -53,11 +53,14 @@ async function apiRequest(method, path, body) {
53
53
 
54
54
  // src/lib/format.ts
55
55
  function formatSubgraphSummary(s) {
56
+ const apiUrl = process.env.SECONDLAYER_API_URL || "https://api.secondlayer.tools";
56
57
  return {
57
58
  name: s.name,
58
59
  status: s.status,
59
60
  tables: Array.isArray(s.tables) ? s.tables : Object.keys(s.tables),
60
- lastProcessedBlock: s.lastProcessedBlock
61
+ lastProcessedBlock: s.lastProcessedBlock,
62
+ ...s.visibility ? { visibility: s.visibility } : {},
63
+ ...s.visibility === "public" ? { publicUrl: `${apiUrl}/v1/subgraphs/${s.name}` } : {}
61
64
  };
62
65
  }
63
66
  function withCap(items, cap) {
@@ -209,7 +212,7 @@ var READ_AUTH_TIERS = {
209
212
  datasets: "open — no API key required",
210
213
  index: "anonymous reads allowed; free-tier API keys are rejected (Build+ required)",
211
214
  streams: "API key required (SL_API_KEY) — keyless calls return 401",
212
- subgraphs: "reads public during open beta; writes require an API key"
215
+ 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"
213
216
  };
214
217
  async function buildContext(deps = { clientProvider: getClient }) {
215
218
  const unavailable = "unavailable: set SL_API_KEY";
@@ -363,6 +366,7 @@ function registerAccountTools(server) {
363
366
  }
364
367
 
365
368
  // src/tools/contracts.ts
369
+ import { generateContractInterface } from "@secondlayer/scaffold";
366
370
  import { z as z2 } from "zod/v4";
367
371
  function registerContractTools(server, clientProvider = getClient) {
368
372
  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.', {
@@ -372,6 +376,47 @@ function registerContractTools(server, clientProvider = getClient) {
372
376
  limit: z2.number().optional().describe("Page size, 1–500 (default 100)"),
373
377
  cursor: z2.string().optional().describe("Opaque cursor from a prior response's next_cursor")
374
378
  }, async (params) => jsonResponse(await clientProvider().contracts.list(params)));
379
+ 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.", {
380
+ contractId: z2.string().describe("Fully qualified contract id (e.g. SP….amm-pool-v2-01)")
381
+ }, async ({ contractId }) => {
382
+ const contract = await clientProvider().contracts.get(contractId, {
383
+ includeAbi: true
384
+ });
385
+ return contract ? jsonResponse(contract) : jsonResponse({
386
+ error: {
387
+ type: "not_found",
388
+ status: 404,
389
+ message: `Contract not in registry: ${contractId}`
390
+ }
391
+ }, true);
392
+ });
393
+ 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.", {
394
+ contractId: z2.string().describe("Fully qualified contract id (e.g. SP….amm-pool-v2-01)")
395
+ }, async ({ contractId }) => {
396
+ const c = await clientProvider().contracts.get(contractId, {
397
+ includeAbi: true
398
+ });
399
+ if (!c || !c.abi) {
400
+ return jsonResponse({
401
+ error: {
402
+ type: "not_found",
403
+ status: 404,
404
+ message: `Contract not in registry: ${contractId}`
405
+ }
406
+ }, true);
407
+ }
408
+ const [address, contractName] = contractId.split(".");
409
+ const name = (contractName ?? contractId).replace(/[^a-zA-Z0-9]/g, "_");
410
+ const code = generateContractInterface([
411
+ {
412
+ name,
413
+ address,
414
+ contractName: contractName ?? "",
415
+ abi: c.abi
416
+ }
417
+ ]);
418
+ return { content: [{ type: "text", text: code }] };
419
+ });
375
420
  }
376
421
 
377
422
  // src/tools/datasets.ts
@@ -462,6 +507,10 @@ function registerIndexTools(server, clientProvider = getClient) {
462
507
  const tx = await clientProvider().index.transactions.get(txId);
463
508
  return tx ? jsonResponse(tx) : notFound(`No transaction for ${txId}`);
464
509
  });
510
+ 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 }) => {
511
+ const proof = await clientProvider().index.transactions.getProof(txId);
512
+ return proof ? jsonResponse(proof) : notFound(`No proof available for ${txId}`);
513
+ });
465
514
  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).", {
466
515
  ...heightFilters,
467
516
  functionName: z4.string().optional().describe("Filter by PoX function name"),
@@ -526,25 +575,32 @@ function registerProjectTools(server, clientProvider = getClient) {
526
575
  }
527
576
 
528
577
  // src/tools/scaffold.ts
529
- import { generateSubgraphCode } from "@secondlayer/scaffold";
578
+ import {
579
+ generateSubgraphCode,
580
+ generateTraitSubgraph
581
+ } from "@secondlayer/scaffold";
582
+ import { TRAIT_STANDARDS as TRAIT_STANDARDS2 } from "@secondlayer/stacks/clarity";
530
583
  import { z as z6 } from "zod/v4";
531
- var API_BASE = process.env.SECONDLAYER_API_URL || "https://api.secondlayer.tools";
532
584
  async function fetchAbi(contractId) {
533
- const res = await fetch(`${API_BASE}/api/node/contracts/${contractId}/abi`, {
534
- signal: AbortSignal.timeout(1e4)
585
+ const contract = await getClient().contracts.get(contractId, {
586
+ includeAbi: true
535
587
  });
536
- if (!res.ok) {
537
- if (res.status === 404)
538
- throw new Error(`Contract not found: ${contractId}`);
539
- throw new Error(`Failed to fetch ABI: HTTP ${res.status}`);
588
+ if (!contract)
589
+ throw new Error(`Contract not found: ${contractId}`);
590
+ const abi = contract.abi;
591
+ if (!abi) {
592
+ throw new Error(`No ABI available for ${contractId} (abi_status: ${contract.abi_status})`);
540
593
  }
541
- const abi = await res.json();
542
- return {
543
- functions: abi.functions ?? [],
544
- maps: abi.maps ?? []
545
- };
594
+ return { functions: abi.functions ?? [], maps: abi.maps ?? [] };
546
595
  }
547
596
  function registerScaffoldTools(server) {
597
+ 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.", {
598
+ trait: z6.enum(TRAIT_STANDARDS2).describe("SIP standard to index (sip-009 | sip-010 | sip-013)"),
599
+ subgraphName: z6.string().optional().describe("Override the subgraph name (defaults to <trait>-transfers)")
600
+ }, async ({ trait, subgraphName }) => {
601
+ const code = generateTraitSubgraph({ trait, name: subgraphName });
602
+ return { content: [{ type: "text", text: code }] };
603
+ });
548
604
  defineTool(server, "scaffold_from_contract", "Generate a subgraph scaffold from a deployed Stacks contract. Fetches the ABI automatically.", {
549
605
  contractId: z6.string().describe("Fully qualified contract ID (e.g. SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01)"),
550
606
  subgraphName: z6.string().optional().describe("Override the subgraph name (defaults to contract name)")
@@ -666,7 +722,7 @@ import {
666
722
  } from "@secondlayer/subgraphs";
667
723
  import { z as z8 } from "zod/v4";
668
724
  function registerSubgraphTools(server, clientProvider = getClient) {
669
- defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only.", {}, async () => {
725
+ defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only, including visibility — public subgraphs are anon-readable at /v1/subgraphs/<name>.", {}, async () => {
670
726
  const { data } = await clientProvider().subgraphs.list();
671
727
  return {
672
728
  content: [
@@ -699,7 +755,7 @@ function registerSubgraphTools(server, clientProvider = getClient) {
699
755
  ]
700
756
  };
701
757
  });
702
- 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>"}.', {
758
+ 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.', {
703
759
  name: z8.string().describe("Subgraph name"),
704
760
  table: z8.string().describe("Table name"),
705
761
  filters: z8.record(z8.string(), z8.string()).optional().describe('Column filters — plain values or with operators (e.g. {"amount.gte": "1000", "sender": "SP..."})'),
@@ -815,12 +871,13 @@ function registerSubgraphTools(server, clientProvider = getClient) {
815
871
  const result = await clientProvider().subgraphs.delete(name);
816
872
  return { content: [{ type: "text", text: result.message }] };
817
873
  });
818
- 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.", {
874
+ 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.", {
819
875
  code: z8.string().describe("TypeScript source code containing a defineSubgraph() call"),
820
876
  startBlock: z8.number().int().nonnegative().optional().describe("Override the definition startBlock for this deploy"),
821
877
  databaseUrl: z8.string().optional().describe("BYO data plane: Postgres connection string to host the subgraph's schema and rows in your own database"),
822
- dryRun: z8.boolean().optional().describe("Validate and preview the deploy (schema/DDL, BYO connection) without writing changes")
823
- }, async ({ code, startBlock, databaseUrl, dryRun }) => {
878
+ dryRun: z8.boolean().optional().describe("Validate and preview the deploy (schema/DDL, BYO connection) without writing changes"),
879
+ 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.")
880
+ }, async ({ code, startBlock, databaseUrl, dryRun, visibility }) => {
824
881
  const bundled = await bundleSubgraphCode(code);
825
882
  try {
826
883
  const result = await clientProvider().subgraphs.deploy({
@@ -833,7 +890,8 @@ function registerSubgraphTools(server, clientProvider = getClient) {
833
890
  sourceCode: code,
834
891
  ...startBlock !== undefined ? { startBlock } : {},
835
892
  ...databaseUrl !== undefined ? { databaseUrl } : {},
836
- ...dryRun !== undefined ? { dryRun } : {}
893
+ ...dryRun !== undefined ? { dryRun } : {},
894
+ ...visibility !== undefined ? { visibility } : {}
837
895
  });
838
896
  return {
839
897
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
@@ -852,6 +910,18 @@ function registerSubgraphTools(server, clientProvider = getClient) {
852
910
  throw err;
853
911
  }
854
912
  });
913
+ 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 }) => {
914
+ const result = await clientProvider().subgraphs.publish(name);
915
+ return {
916
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
917
+ };
918
+ });
919
+ 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 }) => {
920
+ const result = await clientProvider().subgraphs.unpublish(name);
921
+ return {
922
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
923
+ };
924
+ });
855
925
  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 }) => {
856
926
  const source = await clientProvider().subgraphs.getSource(name);
857
927
  return {
@@ -1059,5 +1129,5 @@ export {
1059
1129
  createServer
1060
1130
  };
1061
1131
 
1062
- //# debugId=B30584194FF8002264756E2164756E21
1132
+ //# debugId=00B1F28CFDA24A4764756E2164756E21
1063
1133
  //# sourceMappingURL=index.js.map