@secondlayer/mcp 7.4.1 → 7.5.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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Gives your coding agent direct access to the Stacks data on your own instance —
4
4
  Index (decoded rows), Subgraphs (tables you define, served from your instance),
5
5
  and Streams (raw inputs). Exposes the golden-path tools only: Index reads, the
6
- subgraph lifecycle, subscriptions, contract discovery/scaffolding, instance
6
+ subgraph lifecycle, webhooks, contract discovery/scaffolding, instance
7
7
  status, archive verify/bootstrap, and hosted credits/quote. Everything else
8
8
  (single-record lookups, mempool, stacking, proofs, live Streams reads) is
9
9
  available over REST `/v1` + OpenAPI. There is no `consume` tool.
@@ -16,7 +16,7 @@ bun add @secondlayer/mcp
16
16
 
17
17
  ## Auth
18
18
 
19
- Most reads are public: `index_*` and `contracts_find` work with no key. Subgraph tools need an `INSTANCE_TOKEN` past loopback; separately, **public** subgraphs are anon-readable over HTTP at `GET /v1/subgraphs/<name>/<table>` (`{ rows, next_cursor, tip }` cursor envelope), while private ones need the instance token (anon → 404). `streams_dumps` needs no key: the dumps manifest is public; the tool only needs `SL_STREAMS_DUMPS_URL` configured. Every other `streams_*` tool is key-mandatory (keyless → 401). Writes (deploy, reindex, delete, subscriptions) need a key: set `INSTANCE_TOKEN` from `secondlayer init`. Hosted credits/quote use `SECONDLAYER_API_KEY` (`sk-sl_*`). Read `secondlayer://context` first: it reports auth state and read-auth tiers.
19
+ Most reads are public: `index_*` and `contracts_find` work with no key. Subgraph tools need an `INSTANCE_TOKEN` past loopback; separately, **public** subgraphs are anon-readable over HTTP at `GET /v1/subgraphs/<name>/<table>` (`{ rows, next_cursor, tip }` cursor envelope), while private ones need the instance token (anon → 404). `streams_dumps` needs no key: the dumps manifest is public; the tool only needs `SL_STREAMS_DUMPS_URL` configured. Every other `streams_*` tool is key-mandatory (keyless → 401). Writes (deploy, reindex, delete, webhooks) need a key: set `INSTANCE_TOKEN` from `secondlayer init`. Hosted credits/quote use `SECONDLAYER_API_KEY` (`sk-sl_*`). Read `secondlayer://context` first: it reports auth state and read-auth tiers.
20
20
 
21
21
  ## Quick Start — Stdio (IDE)
22
22
 
@@ -65,7 +65,7 @@ bunx -p @secondlayer/mcp secondlayer-mcp-http
65
65
  | --- | --- |
66
66
  | **Index** (9) | `index_events`, `index_ft_transfers`, `index_nft_transfers`, `index_contract_calls`, `index_blocks`, `index_transactions`, `index_print_schema`, `index_discover`, `batch_query` |
67
67
  | **Subgraphs** (12) | `subgraphs_list`, `subgraphs_status`, `subgraphs_spec`, `subgraphs_scaffold`, `subgraphs_deploy`, `subgraphs_delete`, `subgraphs_query`, `subgraphs_backfill`, `subgraphs_reindex`, `subgraphs_stop`, `subgraphs_operations`, `subgraphs_gaps` |
68
- | **Subscriptions** (13) | `subscriptions_create`, `subscriptions_list`, `subscriptions_get`, `subscriptions_update`, `subscriptions_delete`, `subscriptions_test`, `subscriptions_pause`, `subscriptions_resume`, `subscriptions_rotate_secret`, `subscriptions_deliveries`, `subscriptions_dead`, `subscriptions_requeue`, `subscriptions_replay` |
68
+ | **Webhooks** (13) | `webhooks_create`, `webhooks_list`, `webhooks_get`, `webhooks_update`, `webhooks_delete`, `webhooks_test`, `webhooks_pause`, `webhooks_resume`, `webhooks_rotate_secret`, `webhooks_deliveries`, `webhooks_dead`, `webhooks_requeue`, `webhooks_replay` |
69
69
  | **Streams** (7) | `streams_tip`, `streams_events`, `streams_events_by_tx`, `streams_block_events`, `streams_canonical`, `streams_reorgs`, `streams_dumps` |
70
70
  | **Contracts** (2) | `contracts_find`, `contracts_get_abi` |
71
71
  | **Instance** (1) | `instance_status` |
@@ -75,7 +75,7 @@ bunx -p @secondlayer/mcp secondlayer-mcp-http
75
75
  | **Account** (2) | `account_whoami`, `account_create_key` (only when pointed at `https://api.secondlayer.tools`) |
76
76
 
77
77
  Verify after mutating: `subgraphs_operations` for deploy/reindex/backfill/stop,
78
- `subscriptions_deliveries` for create/test/replay. Empty index: `setup` or
78
+ `webhooks_deliveries` for create/test/replay. Empty index: `setup` or
79
79
  `archive_bootstrap`, poll `instance_status` until decoders are ok, then
80
80
  `archive_verify`, then `codegen_index_schema`.
81
81
 
@@ -88,10 +88,10 @@ Point the server at your instance with `SECONDLAYER_API_URL` (default
88
88
  `secondlayer init`. `account_*` tools appear only when the server is pointed at
89
89
  `https://api.secondlayer.tools`.
90
90
 
91
- ### `subscriptions_create` kinds
91
+ ### `webhooks_create` kinds
92
92
 
93
- Subscriptions are polymorphic. Pass `subgraphName` + `tableName` for a
94
- **subgraph** subscription, or a `triggers` array for a **chain** subscription —
93
+ Webhooks are polymorphic. Pass `subgraphName` + `tableName` for a
94
+ **subgraph** webhook, or a `triggers` array for a **chain** webhook —
95
95
  a webhook on raw chain events (contract / event / function / trait) with no
96
96
  subgraph (e.g. `[{ "type": "contract_call", "contractId": "SP....amm",
97
97
  "functionName": "swap-*" }]`).
@@ -107,11 +107,11 @@ subgraph (e.g. `[{ "type": "contract_call", "contractId": "SP....amm",
107
107
 
108
108
  | URI | Description |
109
109
  | --- | --- |
110
- | `secondlayer://context` | Live state — what exists (your subgraphs, subscriptions, account), what you can do, and read-auth tiers. Read first. |
110
+ | `secondlayer://context` | Live state — what exists (your subgraphs, webhooks, account), what you can do, and read-auth tiers. Read first. |
111
111
  | `secondlayer://filters` | Subgraph source filter types and their fields |
112
112
  | `secondlayer://column-types` | Column type mappings and options |
113
113
  | `secondlayer://traits` | SIP trait standards (valid `trait` values) |
114
- | `secondlayer://chain-triggers` | Chain-subscription trigger types and fields |
114
+ | `secondlayer://chain-triggers` | Chain-webhook trigger types and fields |
115
115
 
116
116
  ## Error Handling
117
117
 
package/dist/bin-http.js CHANGED
@@ -190,7 +190,7 @@ var PRODUCT_BLURBS = {
190
190
  streams: "raw chain event firehose — cursor-paginated reads, tip/canonical/reorg checks, bulk parquet dumps",
191
191
  contracts: "trait-based contract discovery and ABIs",
192
192
  subgraphs: "author/scaffold/deploy/query custom indexes",
193
- subscriptions: "webhook delivery on subgraph rows or raw chain events",
193
+ webhooks: "a signed POST to a URL you run, on subgraph rows or raw chain events",
194
194
  account: "identity and self-provisioned API keys",
195
195
  codegen: "ORM schemas for the tables you read",
196
196
  instance: "decoder health and empty-index diagnosis",
@@ -203,7 +203,7 @@ var PRODUCT_ORDER = [
203
203
  "streams",
204
204
  "contracts",
205
205
  "subgraphs",
206
- "subscriptions",
206
+ "webhooks",
207
207
  "account",
208
208
  "codegen",
209
209
  "instance",
@@ -259,7 +259,7 @@ async function buildContext(deps = { clientProvider: getClient }) {
259
259
  streamsTip: orNull(snap?.streamsTip),
260
260
  indexTip: orNull(snap?.indexTip),
261
261
  subgraphs: snap?.subgraphs.value ? snap.subgraphs.value.map(formatSubgraphSummary) : orNull(snap?.subgraphs),
262
- subscriptions: orNull(snap?.subscriptions),
262
+ webhooks: orNull(snap?.webhooks),
263
263
  activeOperations: orNull(snap?.activeOperations),
264
264
  instance: instance2
265
265
  },
@@ -270,7 +270,7 @@ async function buildContext(deps = { clientProvider: getClient }) {
270
270
  }
271
271
  function registerResources(server) {
272
272
  server.resource("context", "secondlayer://context", {
273
- description: "Live agent context — what exists (your subgraphs, subscriptions, account), what you can do, and read-auth tiers. Read this first."
273
+ description: "Live agent context — what exists (your subgraphs, webhooks, account), what you can do, and read-auth tiers. Read this first."
274
274
  }, async () => ({
275
275
  contents: [
276
276
  {
@@ -315,7 +315,7 @@ function registerResources(server) {
315
315
  ]
316
316
  }));
317
317
  server.resource("chain-triggers", "secondlayer://chain-triggers", {
318
- description: "Chain-subscription trigger types and the filter fields each accepts (for subscriptions_create triggers)."
318
+ description: "Chain-webhook trigger types and the filter fields each accepts (for webhooks_create triggers)."
319
319
  }, async () => ({
320
320
  contents: [
321
321
  {
@@ -1077,22 +1077,22 @@ function registerSubgraphTools(server, clientProvider = getClient) {
1077
1077
  });
1078
1078
  }
1079
1079
 
1080
- // src/tools/subscriptions.ts
1080
+ // src/tools/webhooks.ts
1081
1081
  import { CHAIN_TRIGGER_TYPES } from "@secondlayer/shared";
1082
1082
  import { z as z10 } from "zod";
1083
- function registerSubscriptionTools(server, clientProvider = getClient) {
1084
- defineTool(server, "subscriptions_list", "List all subscriptions for the current account. Returns summary fields (no secrets).", {}, async () => {
1085
- const { data } = await clientProvider().subscriptions.list();
1083
+ function registerWebhookTools(server, clientProvider = getClient) {
1084
+ defineTool(server, "webhooks_list", "List all webhooks for the current account. Returns summary fields (no secrets).", {}, async () => {
1085
+ const { data } = await clientProvider().webhooks.list();
1086
1086
  return jsonResponse(data);
1087
1087
  });
1088
- defineTool(server, "subscriptions_get", "Get full detail for a subscription (filter, auth, retry config, circuit state).", { id: z10.string().describe("Subscription id") }, async ({ id }) => {
1089
- const detail = await clientProvider().subscriptions.get(id);
1088
+ defineTool(server, "webhooks_get", "Get full detail for a webhook (filter, auth, retry config, circuit state).", { id: z10.string().describe("Webhook id") }, async ({ id }) => {
1089
+ const detail = await clientProvider().webhooks.get(id);
1090
1090
  return jsonResponse(detail);
1091
1091
  });
1092
- 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.", {
1092
+ defineTool(server, "webhooks_create", "Create a webhook. Two kinds (mutually exclusive): a SUBGRAPH webhook fires on a subgraph table's rows (set subgraphName + tableName + optional filter); a CHAIN webhook 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.", {
1093
1093
  name: z10.string().describe("Human-readable name, unique per account"),
1094
- subgraphName: z10.string().optional().describe("Subgraph to subscribe to (subgraph subscription)"),
1095
- tableName: z10.string().optional().describe("Table within the subgraph (subgraph subscription)"),
1094
+ subgraphName: z10.string().optional().describe("Subgraph to subscribe to (subgraph webhook)"),
1095
+ tableName: z10.string().optional().describe("Table within the subgraph (subgraph webhook)"),
1096
1096
  triggers: z10.array(z10.object({
1097
1097
  type: z10.enum(CHAIN_TRIGGER_TYPES),
1098
1098
  contractId: z10.string().optional(),
@@ -1108,7 +1108,7 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
1108
1108
  trait: z10.string().optional(),
1109
1109
  minAmount: z10.union([z10.string(), z10.number()]).optional(),
1110
1110
  maxAmount: z10.union([z10.string(), z10.number()]).optional()
1111
- })).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."),
1111
+ })).optional().describe("Chain triggers (chain webhook) — 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."),
1112
1112
  url: z10.string().describe("Webhook URL"),
1113
1113
  format: z10.enum([
1114
1114
  "standard-webhooks",
@@ -1122,12 +1122,12 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
1122
1122
  filter: z10.record(z10.string(), z10.unknown()).optional().describe('Scalar filter DSL, e.g. {"amount": {"gte": 100}, "sender": "SP..."}'),
1123
1123
  authConfig: z10.record(z10.string(), z10.unknown()).optional().describe('Receiver auth sent with each delivery, e.g. {"type": "bearer", "token": "..."}')
1124
1124
  }, async (input) => {
1125
- const res = await clientProvider().subscriptions.create(input);
1125
+ const res = await clientProvider().webhooks.create(input);
1126
1126
  return jsonResponse(res);
1127
1127
  });
1128
- defineTool(server, "subscriptions_update", "Patch a subscription (name, url, filter, authConfig, format, runtime, retry, timeout, concurrency).", {
1128
+ defineTool(server, "webhooks_update", "Patch a webhook (name, url, filter, authConfig, format, runtime, retry, timeout, concurrency).", {
1129
1129
  id: z10.string(),
1130
- name: z10.string().optional().describe("Rename the subscription"),
1130
+ name: z10.string().optional().describe("Rename the webhook"),
1131
1131
  url: z10.string().optional(),
1132
1132
  filter: z10.record(z10.string(), z10.unknown()).optional(),
1133
1133
  authConfig: z10.record(z10.string(), z10.unknown()).optional().describe("Receiver auth sent with each delivery (bearer/etc.)"),
@@ -1144,33 +1144,33 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
1144
1144
  timeoutMs: z10.number().int().min(100).optional(),
1145
1145
  concurrency: z10.number().int().min(1).optional()
1146
1146
  }, async ({ id, ...patch }) => {
1147
- const res = await clientProvider().subscriptions.update(id, patch);
1147
+ const res = await clientProvider().webhooks.update(id, patch);
1148
1148
  return jsonResponse(res);
1149
1149
  });
1150
- defineTool(server, "subscriptions_delete", "Delete a subscription. Pending outbox rows are cascade-deleted.", { id: z10.string() }, async ({ id }) => {
1151
- const res = await clientProvider().subscriptions.delete(id);
1150
+ defineTool(server, "webhooks_delete", "Delete a webhook. Pending outbox rows are cascade-deleted.", { id: z10.string() }, async ({ id }) => {
1151
+ const res = await clientProvider().webhooks.delete(id);
1152
1152
  return jsonResponse(res);
1153
1153
  });
1154
- 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: z10.string().describe("Subscription id") }, async ({ id }) => {
1155
- const res = await clientProvider().subscriptions.test(id);
1154
+ defineTool(server, "webhooks_test", "Send a one-off test webhook to a webhook's URL (built for its format, SSRF-guarded). Logged as a delivery row. Returns {ok, statusCode, error, durationMs, deliveryId}.", { id: z10.string().describe("Webhook id") }, async ({ id }) => {
1155
+ const res = await clientProvider().webhooks.test(id);
1156
1156
  return jsonResponse(res);
1157
1157
  });
1158
- defineTool(server, "subscriptions_pause", "Pause a subscription: stops delivery attempts and stops queueing new ones. Nothing is deleted — resume picks the subscription back up from the tip. Verify with subscriptions_get (status).", { id: z10.string().describe("Subscription id") }, async ({ id }) => jsonResponse(await clientProvider().subscriptions.pause(id)));
1159
- defineTool(server, "subscriptions_resume", "Resume a paused subscription (also clears a tripped circuit breaker). Deliveries restart from the current tip — use subscriptions_replay for the blocks missed while paused. Verify with subscriptions_get (status).", { id: z10.string().describe("Subscription id") }, async ({ id }) => jsonResponse(await clientProvider().subscriptions.resume(id)));
1160
- defineTool(server, "subscriptions_rotate_secret", "Rotate a subscription's webhook signing secret. Returns the NEW `signingSecret` ONCE — forward it to the user; deliveries signed with the old secret stop verifying as soon as this returns, so the receiver must be updated.", { id: z10.string().describe("Subscription id") }, async ({ id }) => jsonResponse(await clientProvider().subscriptions.rotateSecret(id)));
1161
- defineTool(server, "subscriptions_deliveries", "List recent delivery attempts for a subscription — status code, attempt count, error, duration, timestamps. THIS IS THE VERIFY CALL: after subscriptions_create, subscriptions_test, or subscriptions_replay, read it to confirm the webhook actually landed (and to see the receiver's response when it didn't) instead of trusting the enqueue result.", { id: z10.string().describe("Subscription id") }, async ({ id }) => jsonResponse(await clientProvider().subscriptions.deliveries(id)));
1162
- defineTool(server, "subscriptions_dead", "List a subscription's dead-letter queue — deliveries that exhausted their retries, with the outbox id, payload, and last error. Diagnose the receiver first (subscriptions_get for the URL/auth, subscriptions_test for a live probe), then requeue rows with subscriptions_requeue.", { id: z10.string().describe("Subscription id") }, async ({ id }) => jsonResponse(await clientProvider().subscriptions.dead(id)));
1163
- defineTool(server, "subscriptions_requeue", "Requeue ONE dead-lettered delivery for another attempt, by its outbox id from subscriptions_dead. Fix the receiver first — a requeue against a still-broken endpoint just dies again. Verify with subscriptions_deliveries.", {
1164
- id: z10.string().describe("Subscription id"),
1165
- outboxId: z10.string().describe("Outbox id of the dead delivery (from subscriptions_dead)")
1166
- }, async ({ id, outboxId }) => jsonResponse(await clientProvider().subscriptions.requeue(id, outboxId)));
1167
- 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.", {
1158
+ defineTool(server, "webhooks_pause", "Pause a webhook: stops delivery attempts and stops queueing new ones. Nothing is deleted — resume picks the webhook back up from the tip. Verify with webhooks_get (status).", { id: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.pause(id)));
1159
+ defineTool(server, "webhooks_resume", "Resume a paused webhook (also clears a tripped circuit breaker). Deliveries restart from the current tip — use webhooks_replay for the blocks missed while paused. Verify with webhooks_get (status).", { id: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.resume(id)));
1160
+ defineTool(server, "webhooks_rotate_secret", "Rotate a webhook's signing secret. Returns the NEW `signingSecret` ONCE — forward it to the user; deliveries signed with the old secret stop verifying as soon as this returns, so the receiver must be updated.", { id: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.rotateSecret(id)));
1161
+ defineTool(server, "webhooks_deliveries", "List recent delivery attempts for a webhook — status code, attempt count, error, duration, timestamps. THIS IS THE VERIFY CALL: after webhooks_create, webhooks_test, or webhooks_replay, read it to confirm the webhook actually landed (and to see the receiver's response when it didn't) instead of trusting the enqueue result.", { id: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.deliveries(id)));
1162
+ defineTool(server, "webhooks_dead", "List a webhook's dead-letter queue — deliveries that exhausted their retries, with the outbox id, payload, and last error. Diagnose the receiver first (webhooks_get for the URL/auth, webhooks_test for a live probe), then requeue rows with webhooks_requeue.", { id: z10.string().describe("Webhook id") }, async ({ id }) => jsonResponse(await clientProvider().webhooks.dead(id)));
1163
+ defineTool(server, "webhooks_requeue", "Requeue ONE dead-lettered delivery for another attempt, by its outbox id from webhooks_dead. Fix the receiver first — a requeue against a still-broken endpoint just dies again. Verify with webhooks_deliveries.", {
1164
+ id: z10.string().describe("Webhook id"),
1165
+ outboxId: z10.string().describe("Outbox id of the dead delivery (from webhooks_dead)")
1166
+ }, async ({ id, outboxId }) => jsonResponse(await clientProvider().webhooks.requeue(id, outboxId)));
1167
+ defineTool(server, "webhooks_replay", "Replay a block range for a webhook. Replays run at 10% of batch capacity — use sparingly. Pass `force` (a short idempotency suffix) to re-run a range that was already replayed.", {
1168
1168
  id: z10.string(),
1169
1169
  fromBlock: z10.number().int().nonnegative(),
1170
1170
  toBlock: z10.number().int().nonnegative(),
1171
1171
  force: z10.string().optional().describe("Idempotency suffix to force a duplicate replay of the range")
1172
1172
  }, async ({ id, fromBlock, toBlock, force }) => {
1173
- const res = await clientProvider().subscriptions.replay(id, {
1173
+ const res = await clientProvider().webhooks.replay(id, {
1174
1174
  fromBlock,
1175
1175
  toBlock,
1176
1176
  ...force !== undefined ? { force } : {}
@@ -1189,7 +1189,7 @@ function createServer() {
1189
1189
  });
1190
1190
  registerScaffoldTools(server);
1191
1191
  registerSubgraphTools(server);
1192
- registerSubscriptionTools(server);
1192
+ registerWebhookTools(server);
1193
1193
  registerIndexTools(server);
1194
1194
  registerStreamsTools(server);
1195
1195
  registerContractTools(server);
@@ -1291,5 +1291,5 @@ httpServer.listen(port, () => {
1291
1291
  console.error("Warning: SECONDLAYER_MCP_SECRET not set, authentication disabled");
1292
1292
  });
1293
1293
 
1294
- //# debugId=0C640F6B0FB7B21064756E2164756E21
1294
+ //# debugId=3E6A09350537D5E764756E2164756E21
1295
1295
  //# sourceMappingURL=bin-http.js.map