@secondlayer/mcp 3.1.1 → 3.3.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
@@ -72,6 +72,34 @@ function jsonResponse(data, isError) {
72
72
  };
73
73
  }
74
74
 
75
+ // src/lib/tool.ts
76
+ var registeredToolNames = new Set;
77
+ function getRegisteredToolNames() {
78
+ return [...registeredToolNames];
79
+ }
80
+ function defineTool(server, name, description, schema, handler) {
81
+ const wrappedHandler = async (args) => {
82
+ try {
83
+ return await handler(args);
84
+ } catch (err) {
85
+ const message = err instanceof Error ? err.message : String(err);
86
+ const status = err instanceof Error && "status" in err ? err.status : 0;
87
+ const type = status === 401 ? "unauthorized" : status === 404 ? "not_found" : status === 429 ? "rate_limited" : status >= 500 ? "server_error" : "error";
88
+ return {
89
+ content: [
90
+ {
91
+ type: "text",
92
+ text: JSON.stringify({ error: { type, status, message } })
93
+ }
94
+ ],
95
+ isError: true
96
+ };
97
+ }
98
+ };
99
+ registeredToolNames.add(name);
100
+ server.tool(name, description, schema, wrappedHandler);
101
+ }
102
+
75
103
  // src/resources.ts
76
104
  var FILTERS_REFERENCE = [
77
105
  {
@@ -127,39 +155,69 @@ var COLUMN_TYPES = [
127
155
  description: "Column options: nullable allows NULL, indexed creates a B-tree index, search enables full-text search"
128
156
  }
129
157
  ];
130
- var CAPABILITIES = {
131
- products: [
132
- "datasets public foundation datasets (datasets_list, datasets_query)",
133
- "index decoded L2 events + contract calls (index_ft_transfers, index_nft_transfers, index_events, index_contract_calls)",
134
- "streams raw chain event firehose (streams_tip, streams_events)",
135
- "contracts trait-based contract discovery (contracts_find)",
136
- "subgraphs author/deploy/query custom indexes (subgraphs_deploy, subgraphs_query, subgraphs_list, subgraphs_get, subgraphs_reindex, subgraphs_delete)",
137
- "subscriptions — webhook delivery of subgraph rows (subscriptions_create, subscriptions_list, subscriptions_update, …)",
138
- "account identity + plan (account_whoami)"
139
- ],
140
- discoverFirst: "Call datasets_list / contracts_find to learn what exists before querying."
158
+ var PRODUCT_BLURBS = {
159
+ datasets: "public foundation datasets",
160
+ index: "decoded L2 events, contract calls, blocks, transactions, stacking, mempool",
161
+ streams: "raw canonical chain event firehose",
162
+ contracts: "trait-based contract discovery",
163
+ subgraphs: "author/deploy/query custom indexes",
164
+ subscriptions: "webhook delivery on subgraph rows or raw chain events",
165
+ account: "identity, plan, billing, and API keys",
166
+ scaffold: "generate typed contract clients from a deployment or ABI"
141
167
  };
168
+ var PRODUCT_ORDER = [
169
+ "datasets",
170
+ "index",
171
+ "streams",
172
+ "contracts",
173
+ "subgraphs",
174
+ "subscriptions",
175
+ "account",
176
+ "scaffold"
177
+ ];
178
+ function buildCapabilities() {
179
+ const byPrefix = new Map;
180
+ for (const name of getRegisteredToolNames()) {
181
+ const prefix = name.slice(0, name.indexOf("_"));
182
+ const tools = byPrefix.get(prefix) ?? [];
183
+ tools.push(name);
184
+ byPrefix.set(prefix, tools);
185
+ }
186
+ const order = [
187
+ ...PRODUCT_ORDER.filter((p) => byPrefix.has(p)),
188
+ ...[...byPrefix.keys()].filter((p) => !PRODUCT_ORDER.includes(p))
189
+ ];
190
+ const products = order.map((p) => {
191
+ const tools = byPrefix.get(p) ?? [];
192
+ const blurb = PRODUCT_BLURBS[p];
193
+ return blurb ? `${p} — ${blurb} (${tools.join(", ")})` : `${p} (${tools.join(", ")})`;
194
+ });
195
+ return {
196
+ products,
197
+ discoverFirst: "Call datasets_list / contracts_find to learn what exists before querying."
198
+ };
199
+ }
142
200
  var READ_AUTH_TIERS = {
143
201
  datasets: "open — no API key required",
144
202
  index: "anonymous reads allowed; free-tier API keys are rejected (Build+ required)",
145
203
  streams: "API key required (SL_API_KEY) — keyless calls return 401",
146
204
  subgraphs: "reads public during open beta; writes require an API key"
147
205
  };
148
- async function buildContext(deps = {
149
- clientProvider: getClient,
150
- accountRequest: () => apiRequest("GET", "/api/accounts/me")
151
- }) {
206
+ async function buildContext(deps = { clientProvider: getClient }) {
152
207
  const unavailable = "unavailable: set SL_API_KEY";
153
- const subgraphs = await deps.clientProvider().subgraphs.list().then((r) => r.data.map(formatSubgraphSummary)).catch(() => unavailable);
154
- const subscriptions = await deps.clientProvider().subscriptions.list().then((r) => ({
155
- count: r.data.length,
156
- statuses: r.data.map((s) => s.status)
157
- })).catch(() => unavailable);
158
- const account = await deps.accountRequest().catch(() => unavailable);
208
+ const orNull = (v) => v == null ? unavailable : v;
209
+ const snap = await deps.clientProvider().context().catch(() => null);
159
210
  return {
160
211
  authState: { apiKeySet: Boolean(process.env.SL_API_KEY) },
161
- whatExists: { subgraphs, subscriptions, account },
162
- whatYouCanDo: CAPABILITIES,
212
+ whatExists: {
213
+ account: orNull(snap?.account),
214
+ streamsTip: orNull(snap?.streamsTip),
215
+ indexTip: orNull(snap?.indexTip),
216
+ subgraphs: snap?.subgraphs ? snap.subgraphs.map(formatSubgraphSummary) : unavailable,
217
+ subscriptions: orNull(snap?.subscriptions),
218
+ activeOperations: orNull(snap?.activeOperations)
219
+ },
220
+ whatYouCanDo: buildCapabilities(),
163
221
  readAuthTiers: READ_AUTH_TIERS
164
222
  };
165
223
  }
@@ -197,31 +255,6 @@ function registerResources(server) {
197
255
 
198
256
  // src/tools/account.ts
199
257
  import { z } from "zod/v4";
200
-
201
- // src/lib/tool.ts
202
- function defineTool(server, name, description, schema, handler) {
203
- const wrappedHandler = async (args) => {
204
- try {
205
- return await handler(args);
206
- } catch (err) {
207
- const message = err instanceof Error ? err.message : String(err);
208
- const status = err instanceof Error && "status" in err ? err.status : 0;
209
- const type = status === 401 ? "unauthorized" : status === 404 ? "not_found" : status === 429 ? "rate_limited" : status >= 500 ? "server_error" : "error";
210
- return {
211
- content: [
212
- {
213
- type: "text",
214
- text: JSON.stringify({ error: { type, status, message } })
215
- }
216
- ],
217
- isError: true
218
- };
219
- }
220
- };
221
- server.tool(name, description, schema, wrappedHandler);
222
- }
223
-
224
- // src/tools/account.ts
225
258
  function registerAccountTools(server) {
226
259
  defineTool(server, "account_whoami", "Show the authenticated account's email and plan.", {}, async () => {
227
260
  const result = await apiRequest("GET", "/api/accounts/me");
@@ -246,6 +279,10 @@ function registerAccountTools(server) {
246
279
  const result = await apiRequest("GET", "/api/billing/status");
247
280
  return jsonResponse(result);
248
281
  });
282
+ 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.", {
283
+ product: z.enum(["streams", "index"]).optional().describe("Key scope (default streams)"),
284
+ name: z.string().optional().describe("Optional label for the key")
285
+ }, async ({ product, name }) => jsonResponse(await getClient().apiKeys.create({ product, name })));
249
286
  }
250
287
 
251
288
  // src/tools/contracts.ts
@@ -284,20 +321,9 @@ function registerDatasetTools(server, clientProvider = getClient) {
284
321
  }
285
322
 
286
323
  // src/tools/index.ts
324
+ import { DECODED_EVENT_TYPES } from "@secondlayer/shared";
287
325
  import { z as z4 } from "zod/v4";
288
- var INDEX_EVENT_TYPES = [
289
- "ft_transfer",
290
- "nft_transfer",
291
- "stx_transfer",
292
- "stx_mint",
293
- "stx_burn",
294
- "stx_lock",
295
- "ft_mint",
296
- "ft_burn",
297
- "nft_mint",
298
- "nft_burn",
299
- "print"
300
- ];
326
+ var INDEX_EVENT_TYPES = DECODED_EVENT_TYPES;
301
327
  var rangeFilters = {
302
328
  contractId: z4.string().optional().describe("Filter by contract id"),
303
329
  fromHeight: z4.number().optional().describe("Start block height (inclusive)"),
@@ -305,6 +331,13 @@ var rangeFilters = {
305
331
  cursor: z4.string().optional().describe("Opaque cursor from a prior response's next_cursor"),
306
332
  limit: z4.number().optional().describe("Max rows for this page")
307
333
  };
334
+ var heightFilters = {
335
+ fromHeight: rangeFilters.fromHeight,
336
+ toHeight: rangeFilters.toHeight,
337
+ cursor: rangeFilters.cursor,
338
+ limit: rangeFilters.limit
339
+ };
340
+ var notFound = (message) => jsonResponse({ error: { type: "not_found", status: 404, message } }, true);
308
341
  function registerIndexTools(server, clientProvider = getClient) {
309
342
  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).", {
310
343
  ...rangeFilters,
@@ -329,6 +362,41 @@ function registerIndexTools(server, clientProvider = getClient) {
329
362
  functionName: z4.string().optional().describe("Filter by called function name"),
330
363
  sender: z4.string().optional().describe("Filter by caller principal")
331
364
  }, async (params) => jsonResponse(await clientProvider().index.contractCalls.list(params)));
365
+ 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)));
366
+ 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)));
367
+ defineTool(server, "index_block", "Get a single block from the Index by height or block hash. Returns not_found if unknown.", {
368
+ ref: z4.string().describe("Block height (digits) or block hash (0x… string)")
369
+ }, async ({ ref }) => {
370
+ const block = await clientProvider().index.blocks.get(/^\d+$/.test(ref) ? Number(ref) : ref);
371
+ return block ? jsonResponse(block) : notFound(`No block for ref ${ref}`);
372
+ });
373
+ defineTool(server, "index_transactions", "List decoded transactions from the Index. Filter by type, sender, or contract. Anonymous reads allowed (free-tier keys rejected).", {
374
+ ...rangeFilters,
375
+ type: z4.string().optional().describe("Filter by transaction type"),
376
+ sender: z4.string().optional().describe("Filter by sender principal")
377
+ }, async (params) => jsonResponse(await clientProvider().index.transactions.list(params)));
378
+ defineTool(server, "index_transaction", "Get a single transaction from the Index by tx_id. Returns not_found if unknown.", { txId: z4.string().describe("Transaction id (0x… hash)") }, async ({ txId }) => {
379
+ const tx = await clientProvider().index.transactions.get(txId);
380
+ return tx ? jsonResponse(tx) : notFound(`No transaction for ${txId}`);
381
+ });
382
+ 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).", {
383
+ ...heightFilters,
384
+ functionName: z4.string().optional().describe("Filter by PoX function name"),
385
+ stacker: z4.string().optional().describe("Filter by stacker principal"),
386
+ caller: z4.string().optional().describe("Filter by caller principal")
387
+ }, async (params) => jsonResponse(await clientProvider().index.stacking.list(params)));
388
+ 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).", {
389
+ sender: z4.string().optional().describe("Filter by sender principal"),
390
+ type: z4.string().optional().describe("Filter by transaction type"),
391
+ contractId: z4.string().optional().describe("Filter to pending calls to a single contract"),
392
+ cursor: z4.string().optional().describe("Opaque cursor from a prior response's next_cursor"),
393
+ limit: z4.number().optional().describe("Max rows for this page")
394
+ }, async (params) => jsonResponse(await clientProvider().index.mempool.list(params)));
395
+ 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: z4.string().describe("Transaction id (0x… hash)") }, async ({ txId }) => {
396
+ const tx = await clientProvider().index.mempool.get(txId);
397
+ return tx ? jsonResponse(tx) : notFound(`No pending tx for ${txId}`);
398
+ });
399
+ 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()));
332
400
  }
333
401
 
334
402
  // src/tools/scaffold.ts
@@ -380,20 +448,9 @@ function registerScaffoldTools(server) {
380
448
 
381
449
  // src/tools/streams.ts
382
450
  import { AuthError } from "@secondlayer/sdk";
451
+ import { DECODED_EVENT_TYPES as DECODED_EVENT_TYPES2 } from "@secondlayer/shared";
383
452
  import { z as z6 } from "zod/v4";
384
- var STREAMS_EVENT_TYPES = [
385
- "stx_transfer",
386
- "stx_mint",
387
- "stx_burn",
388
- "stx_lock",
389
- "ft_transfer",
390
- "ft_mint",
391
- "ft_burn",
392
- "nft_transfer",
393
- "nft_mint",
394
- "nft_burn",
395
- "print"
396
- ];
453
+ var STREAMS_EVENT_TYPES = DECODED_EVENT_TYPES2;
397
454
  async function withStreamsAuthHint(fn) {
398
455
  try {
399
456
  return await fn();
@@ -413,11 +470,21 @@ function registerStreamsTools(server, clientProvider = getClient) {
413
470
  sender: z6.string().optional().describe("Filter by sender principal"),
414
471
  recipient: z6.string().optional().describe("Filter by recipient principal"),
415
472
  assetIdentifier: z6.string().optional().describe("Filter by asset identifier"),
416
- fromBlock: z6.number().optional().describe("Start block (inclusive)"),
417
- toBlock: z6.number().optional().describe("End block (inclusive)"),
473
+ fromHeight: z6.number().optional().describe("Start block height (inclusive)"),
474
+ toHeight: z6.number().optional().describe("End block height (inclusive)"),
418
475
  cursor: z6.string().optional().describe("Opaque cursor from a prior response"),
419
476
  limit: z6.number().optional().describe("Max events for this page")
420
477
  }, async (params) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.events.list(params))));
478
+ defineTool(server, "streams_event_by_txid", "List all Streams events emitted by a single transaction. Streams requires an API key (SL_API_KEY).", { txId: z6.string().describe("Transaction id (0x… hash)") }, async ({ txId }) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.events.byTxId(txId))));
479
+ defineTool(server, "streams_block_events", "List all Streams events in a single block, by height or block hash. Streams requires an API key (SL_API_KEY).", {
480
+ heightOrHash: z6.string().describe("Block height (digits) or block hash (0x… string)")
481
+ }, async ({ heightOrHash }) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.blocks.events(/^\d+$/.test(heightOrHash) ? Number(heightOrHash) : heightOrHash))));
482
+ defineTool(server, "streams_reorgs", "List chain reorgs observed by Streams since a cursor. Streams requires an API key (SL_API_KEY).", {
483
+ since: z6.string().describe("Cursor to list reorgs since (block:index or ISO timestamp)"),
484
+ limit: z6.number().optional().describe("Max reorgs to return")
485
+ }, async (params) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.reorgs.list(params))));
486
+ 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))));
487
+ 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())));
421
488
  }
422
489
 
423
490
  // src/tools/subgraphs.ts
@@ -498,7 +565,7 @@ function registerSubgraphTools(server, clientProvider = getClient) {
498
565
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
499
566
  };
500
567
  });
501
- defineTool(server, "subgraphs_reindex", "Reindex a subgraph from a specific block range.", {
568
+ defineTool(server, "subgraphs_reindex", "Reindex a subgraph from a specific block range. Returns an operationId — poll subgraphs_operation to track progress to completion.", {
502
569
  name: z7.string().describe("Subgraph name"),
503
570
  fromBlock: z7.number().optional().describe("Start block (defaults to beginning)"),
504
571
  toBlock: z7.number().optional().describe("End block (defaults to latest)")
@@ -511,6 +578,15 @@ function registerSubgraphTools(server, clientProvider = getClient) {
511
578
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
512
579
  };
513
580
  });
581
+ 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.", {
582
+ name: z7.string().describe("Subgraph name"),
583
+ operationId: z7.string().optional().describe("Operation id from reindex/backfill/stop; omit to list recent operations")
584
+ }, async ({ name, operationId }) => {
585
+ const result = operationId ? await clientProvider().subgraphs.getOperation(name, operationId) : await clientProvider().subgraphs.operations(name);
586
+ return {
587
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
588
+ };
589
+ });
514
590
  defineTool(server, "subgraphs_delete", "Delete a subgraph permanently.", { name: z7.string().describe("Subgraph name") }, async ({ name }) => {
515
591
  const result = await clientProvider().subgraphs.delete(name);
516
592
  return { content: [{ type: "text", text: result.message }] };
@@ -543,6 +619,7 @@ function registerSubgraphTools(server, clientProvider = getClient) {
543
619
  }
544
620
 
545
621
  // src/tools/subscriptions.ts
622
+ import { CHAIN_TRIGGER_TYPES } from "@secondlayer/shared";
546
623
  import { z as z8 } from "zod/v4";
547
624
  function registerSubscriptionTools(server, clientProvider = getClient) {
548
625
  defineTool(server, "subscriptions_list", "List all subscriptions for the current account. Returns summary fields (no secrets).", {}, async () => {
@@ -557,10 +634,26 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
557
634
  content: [{ type: "text", text: JSON.stringify(detail, null, 2) }]
558
635
  };
559
636
  });
560
- defineTool(server, "subscriptions_create", "Create a subscription. Returns `signingSecret` ONCE — forward it to the user so they can wire it into their receiver.", {
637
+ 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.", {
561
638
  name: z8.string().describe("Human-readable name, unique per account"),
562
- subgraphName: z8.string().describe("Subgraph to subscribe to"),
563
- tableName: z8.string().describe("Table within the subgraph"),
639
+ subgraphName: z8.string().optional().describe("Subgraph to subscribe to (subgraph subscription)"),
640
+ tableName: z8.string().optional().describe("Table within the subgraph (subgraph subscription)"),
641
+ triggers: z8.array(z8.object({
642
+ type: z8.enum(CHAIN_TRIGGER_TYPES),
643
+ contractId: z8.string().optional(),
644
+ functionName: z8.string().optional(),
645
+ caller: z8.string().optional(),
646
+ sender: z8.string().optional(),
647
+ recipient: z8.string().optional(),
648
+ assetIdentifier: z8.string().optional(),
649
+ deployer: z8.string().optional(),
650
+ contractName: z8.string().optional(),
651
+ topic: z8.string().optional(),
652
+ lockedAddress: z8.string().optional(),
653
+ trait: z8.string().optional(),
654
+ minAmount: z8.union([z8.string(), z8.number()]).optional(),
655
+ maxAmount: z8.union([z8.string(), z8.number()]).optional()
656
+ })).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. Forward-looking: starts at chain tip, no backfill."),
564
657
  url: z8.string().describe("Webhook URL"),
565
658
  format: z8.enum([
566
659
  "standard-webhooks",
@@ -685,5 +778,5 @@ var server = createServer();
685
778
  var transport = new StdioServerTransport;
686
779
  await server.connect(transport);
687
780
 
688
- //# debugId=911BD1E066921C1164756E2164756E21
781
+ //# debugId=6CC81A386AFC65E864756E2164756E21
689
782
  //# sourceMappingURL=bin.js.map