@secondlayer/mcp 3.0.0 → 3.1.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-http.js +316 -118
- package/dist/bin-http.js.map +11 -7
- package/dist/bin.js +316 -118
- package/dist/bin.js.map +11 -7
- package/dist/index.js +316 -118
- package/dist/index.js.map +11 -7
- package/package.json +2 -2
package/dist/bin-http.js
CHANGED
|
@@ -12,6 +12,69 @@ import { dirname, join } from "node:path";
|
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
14
|
|
|
15
|
+
// src/lib/client.ts
|
|
16
|
+
import { SecondLayer } from "@secondlayer/sdk";
|
|
17
|
+
var instance = null;
|
|
18
|
+
function readApiKey() {
|
|
19
|
+
return process.env.SL_API_KEY;
|
|
20
|
+
}
|
|
21
|
+
function getClient() {
|
|
22
|
+
if (!instance) {
|
|
23
|
+
const apiKey = readApiKey();
|
|
24
|
+
const baseUrl = process.env.SECONDLAYER_API_URL;
|
|
25
|
+
instance = new SecondLayer({
|
|
26
|
+
...apiKey ? { apiKey } : {},
|
|
27
|
+
origin: "mcp",
|
|
28
|
+
...baseUrl ? { baseUrl } : {}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return instance;
|
|
32
|
+
}
|
|
33
|
+
var keyHint = " — set SL_API_KEY to an sk-sl_ API key from " + "https://secondlayer.tools/platform/api-keys for write and account operations";
|
|
34
|
+
async function apiRequest(method, path, body) {
|
|
35
|
+
const apiKey = readApiKey();
|
|
36
|
+
const baseUrl = process.env.SECONDLAYER_API_URL || "https://api.secondlayer.tools";
|
|
37
|
+
const res = await fetch(`${baseUrl}${path}`, {
|
|
38
|
+
method,
|
|
39
|
+
headers: {
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
|
|
42
|
+
},
|
|
43
|
+
body: body ? JSON.stringify(body) : undefined
|
|
44
|
+
});
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
const text = await res.text().catch(() => "");
|
|
47
|
+
const needsKey = !apiKey && (res.status === 401 || res.status === 403);
|
|
48
|
+
throw Object.assign(new Error((text || `HTTP ${res.status}`) + (needsKey ? keyHint : "")), { status: res.status });
|
|
49
|
+
}
|
|
50
|
+
if (res.status === 204)
|
|
51
|
+
return;
|
|
52
|
+
return res.json();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/lib/format.ts
|
|
56
|
+
function formatSubgraphSummary(s) {
|
|
57
|
+
return {
|
|
58
|
+
name: s.name,
|
|
59
|
+
status: s.status,
|
|
60
|
+
tables: Array.isArray(s.tables) ? s.tables : Object.keys(s.tables),
|
|
61
|
+
lastProcessedBlock: s.lastProcessedBlock
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function withCap(items, cap) {
|
|
65
|
+
return {
|
|
66
|
+
items: items.slice(0, cap),
|
|
67
|
+
truncated: items.length > cap,
|
|
68
|
+
total: items.length
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function jsonResponse(data, isError) {
|
|
72
|
+
return {
|
|
73
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
74
|
+
...isError && { isError: true }
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
15
78
|
// src/resources.ts
|
|
16
79
|
var FILTERS_REFERENCE = [
|
|
17
80
|
{
|
|
@@ -67,7 +130,54 @@ var COLUMN_TYPES = [
|
|
|
67
130
|
description: "Column options: nullable allows NULL, indexed creates a B-tree index, search enables full-text search"
|
|
68
131
|
}
|
|
69
132
|
];
|
|
133
|
+
var CAPABILITIES = {
|
|
134
|
+
products: [
|
|
135
|
+
"datasets — public foundation datasets (datasets_list, datasets_query)",
|
|
136
|
+
"index — decoded L2 events + contract calls (index_ft_transfers, index_nft_transfers, index_events, index_contract_calls)",
|
|
137
|
+
"streams — raw chain event firehose (streams_tip, streams_events)",
|
|
138
|
+
"contracts — trait-based contract discovery (contracts_find)",
|
|
139
|
+
"subgraphs — author/deploy/query custom indexes (subgraphs_deploy, subgraphs_query, subgraphs_list, subgraphs_get, subgraphs_reindex, subgraphs_delete)",
|
|
140
|
+
"subscriptions — webhook delivery of subgraph rows (subscriptions_create, subscriptions_list, subscriptions_update, …)",
|
|
141
|
+
"account — identity + plan (account_whoami)"
|
|
142
|
+
],
|
|
143
|
+
discoverFirst: "Call datasets_list / contracts_find to learn what exists before querying."
|
|
144
|
+
};
|
|
145
|
+
var READ_AUTH_TIERS = {
|
|
146
|
+
datasets: "open — no API key required",
|
|
147
|
+
index: "anonymous reads allowed; free-tier API keys are rejected (Build+ required)",
|
|
148
|
+
streams: "API key required (SL_API_KEY) — keyless calls return 401",
|
|
149
|
+
subgraphs: "reads public during open beta; writes require an API key"
|
|
150
|
+
};
|
|
151
|
+
async function buildContext(deps = {
|
|
152
|
+
clientProvider: getClient,
|
|
153
|
+
accountRequest: () => apiRequest("GET", "/api/accounts/me")
|
|
154
|
+
}) {
|
|
155
|
+
const unavailable = "unavailable: set SL_API_KEY";
|
|
156
|
+
const subgraphs = await deps.clientProvider().subgraphs.list().then((r) => r.data.map(formatSubgraphSummary)).catch(() => unavailable);
|
|
157
|
+
const subscriptions = await deps.clientProvider().subscriptions.list().then((r) => ({
|
|
158
|
+
count: r.data.length,
|
|
159
|
+
statuses: r.data.map((s) => s.status)
|
|
160
|
+
})).catch(() => unavailable);
|
|
161
|
+
const account = await deps.accountRequest().catch(() => unavailable);
|
|
162
|
+
return {
|
|
163
|
+
authState: { apiKeySet: Boolean(process.env.SL_API_KEY) },
|
|
164
|
+
whatExists: { subgraphs, subscriptions, account },
|
|
165
|
+
whatYouCanDo: CAPABILITIES,
|
|
166
|
+
readAuthTiers: READ_AUTH_TIERS
|
|
167
|
+
};
|
|
168
|
+
}
|
|
70
169
|
function registerResources(server) {
|
|
170
|
+
server.resource("context", "secondlayer://context", {
|
|
171
|
+
description: "Live agent context — what exists (your subgraphs, subscriptions, account), what you can do, and read-auth tiers. Read this first."
|
|
172
|
+
}, async () => ({
|
|
173
|
+
contents: [
|
|
174
|
+
{
|
|
175
|
+
uri: "secondlayer://context",
|
|
176
|
+
mimeType: "application/json",
|
|
177
|
+
text: JSON.stringify(await buildContext(), null, 2)
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
}));
|
|
71
181
|
server.resource("filters", "secondlayer://filters", { description: "Event filter types and their available fields" }, async () => ({
|
|
72
182
|
contents: [
|
|
73
183
|
{
|
|
@@ -88,68 +198,8 @@ function registerResources(server) {
|
|
|
88
198
|
}));
|
|
89
199
|
}
|
|
90
200
|
|
|
91
|
-
// src/
|
|
92
|
-
import {
|
|
93
|
-
var instance = null;
|
|
94
|
-
function readApiKey() {
|
|
95
|
-
return process.env.SL_API_KEY;
|
|
96
|
-
}
|
|
97
|
-
function getClient() {
|
|
98
|
-
if (!instance) {
|
|
99
|
-
const apiKey = readApiKey();
|
|
100
|
-
const baseUrl = process.env.SECONDLAYER_API_URL;
|
|
101
|
-
instance = new SecondLayer({
|
|
102
|
-
...apiKey ? { apiKey } : {},
|
|
103
|
-
origin: "mcp",
|
|
104
|
-
...baseUrl ? { baseUrl } : {}
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
return instance;
|
|
108
|
-
}
|
|
109
|
-
var keyHint = " — set SL_API_KEY to an sk-sl_ API key from " + "https://secondlayer.tools/platform/api-keys for write and account operations";
|
|
110
|
-
async function apiRequest(method, path, body) {
|
|
111
|
-
const apiKey = readApiKey();
|
|
112
|
-
const baseUrl = process.env.SECONDLAYER_API_URL || "https://api.secondlayer.tools";
|
|
113
|
-
const res = await fetch(`${baseUrl}${path}`, {
|
|
114
|
-
method,
|
|
115
|
-
headers: {
|
|
116
|
-
"Content-Type": "application/json",
|
|
117
|
-
...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
|
|
118
|
-
},
|
|
119
|
-
body: body ? JSON.stringify(body) : undefined
|
|
120
|
-
});
|
|
121
|
-
if (!res.ok) {
|
|
122
|
-
const text = await res.text().catch(() => "");
|
|
123
|
-
const needsKey = !apiKey && (res.status === 401 || res.status === 403);
|
|
124
|
-
throw Object.assign(new Error((text || `HTTP ${res.status}`) + (needsKey ? keyHint : "")), { status: res.status });
|
|
125
|
-
}
|
|
126
|
-
if (res.status === 204)
|
|
127
|
-
return;
|
|
128
|
-
return res.json();
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// src/lib/format.ts
|
|
132
|
-
function formatSubgraphSummary(s) {
|
|
133
|
-
return {
|
|
134
|
-
name: s.name,
|
|
135
|
-
status: s.status,
|
|
136
|
-
tables: Array.isArray(s.tables) ? s.tables : Object.keys(s.tables),
|
|
137
|
-
lastProcessedBlock: s.lastProcessedBlock
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
function withCap(items, cap) {
|
|
141
|
-
return {
|
|
142
|
-
items: items.slice(0, cap),
|
|
143
|
-
truncated: items.length > cap,
|
|
144
|
-
total: items.length
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
function jsonResponse(data, isError) {
|
|
148
|
-
return {
|
|
149
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
150
|
-
...isError && { isError: true }
|
|
151
|
-
};
|
|
152
|
-
}
|
|
201
|
+
// src/tools/account.ts
|
|
202
|
+
import { z } from "zod/v4";
|
|
153
203
|
|
|
154
204
|
// src/lib/tool.ts
|
|
155
205
|
function defineTool(server, name, description, schema, handler) {
|
|
@@ -180,11 +230,113 @@ function registerAccountTools(server) {
|
|
|
180
230
|
const result = await apiRequest("GET", "/api/accounts/me");
|
|
181
231
|
return jsonResponse(result);
|
|
182
232
|
});
|
|
233
|
+
defineTool(server, "account_update", "Update the authenticated account's profile. Requires an API key.", {
|
|
234
|
+
displayName: z.string().optional().describe("Display name"),
|
|
235
|
+
bio: z.string().optional().describe("Profile bio"),
|
|
236
|
+
slug: z.string().optional().describe("Account URL slug")
|
|
237
|
+
}, async ({ displayName, bio, slug }) => {
|
|
238
|
+
const body = {};
|
|
239
|
+
if (displayName !== undefined)
|
|
240
|
+
body.display_name = displayName;
|
|
241
|
+
if (bio !== undefined)
|
|
242
|
+
body.bio = bio;
|
|
243
|
+
if (slug !== undefined)
|
|
244
|
+
body.slug = slug;
|
|
245
|
+
const result = await apiRequest("PATCH", "/api/accounts/me", body);
|
|
246
|
+
return jsonResponse(result);
|
|
247
|
+
});
|
|
248
|
+
defineTool(server, "account_billing", "Show the account's plan and subscription/billing status. Requires an API key.", {}, async () => {
|
|
249
|
+
const result = await apiRequest("GET", "/api/billing/status");
|
|
250
|
+
return jsonResponse(result);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// src/tools/contracts.ts
|
|
255
|
+
import { z as z2 } from "zod/v4";
|
|
256
|
+
function registerContractTools(server, clientProvider = getClient) {
|
|
257
|
+
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.', {
|
|
258
|
+
trait: z2.string().describe('Required. Trait to match (e.g. "sip-010").'),
|
|
259
|
+
conformance: z2.enum(["declared", "inferred", "any"]).optional().describe("declared = parsed from source, inferred = ABI shape-match, any = either (default)"),
|
|
260
|
+
include: z2.literal("abi").optional().describe(`Set to "abi" to include each contract's full ABI`),
|
|
261
|
+
limit: z2.number().optional().describe("Page size, 1–500 (default 100)"),
|
|
262
|
+
cursor: z2.string().optional().describe("Opaque cursor from a prior response's next_cursor")
|
|
263
|
+
}, async (params) => jsonResponse(await clientProvider().contracts.list(params)));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/tools/datasets.ts
|
|
267
|
+
import { z as z3 } from "zod/v4";
|
|
268
|
+
function registerDatasetTools(server, clientProvider = getClient) {
|
|
269
|
+
defineTool(server, "datasets_list", "List the Foundation Datasets catalog with freshness — the discovery endpoint for what dataset slugs exist and how current each is. Reads are public.", {}, async () => {
|
|
270
|
+
const catalog = await clientProvider().datasets.listDatasets();
|
|
271
|
+
return jsonResponse(catalog);
|
|
272
|
+
});
|
|
273
|
+
defineTool(server, "datasets_query", 'Query a cursor-paginated Foundation Dataset by slug (e.g. "stx-transfers", "sbtc/events", "pox-4/calls", "bns/events"). Filters are passed through as documented query params (e.g. {"sender": "SP...", "from_block": "150000"}). Returns { rows, next_cursor, tip }. Call datasets_list first to discover slugs.', {
|
|
274
|
+
slug: z3.string().describe("Dataset slug from datasets_list (e.g. stx-transfers)"),
|
|
275
|
+
filters: z3.record(z3.string(), z3.string()).optional().describe("Documented per-dataset query params (snake_case values)"),
|
|
276
|
+
limit: z3.number().optional().describe("Max rows for this page"),
|
|
277
|
+
cursor: z3.string().optional().describe("Opaque cursor from a prior response's next_cursor")
|
|
278
|
+
}, async ({ slug, filters, limit, cursor }) => {
|
|
279
|
+
const params = { ...filters ?? {} };
|
|
280
|
+
if (limit !== undefined)
|
|
281
|
+
params.limit = limit;
|
|
282
|
+
if (cursor !== undefined)
|
|
283
|
+
params.cursor = cursor;
|
|
284
|
+
const result = await clientProvider().datasets.query(slug, params);
|
|
285
|
+
return jsonResponse(result);
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/tools/index.ts
|
|
290
|
+
import { z as z4 } from "zod/v4";
|
|
291
|
+
var INDEX_EVENT_TYPES = [
|
|
292
|
+
"ft_transfer",
|
|
293
|
+
"nft_transfer",
|
|
294
|
+
"stx_transfer",
|
|
295
|
+
"stx_mint",
|
|
296
|
+
"stx_burn",
|
|
297
|
+
"stx_lock",
|
|
298
|
+
"ft_mint",
|
|
299
|
+
"ft_burn",
|
|
300
|
+
"nft_mint",
|
|
301
|
+
"nft_burn",
|
|
302
|
+
"print"
|
|
303
|
+
];
|
|
304
|
+
var rangeFilters = {
|
|
305
|
+
contractId: z4.string().optional().describe("Filter by contract id"),
|
|
306
|
+
fromHeight: z4.number().optional().describe("Start block height (inclusive)"),
|
|
307
|
+
toHeight: z4.number().optional().describe("End block height (inclusive)"),
|
|
308
|
+
cursor: z4.string().optional().describe("Opaque cursor from a prior response's next_cursor"),
|
|
309
|
+
limit: z4.number().optional().describe("Max rows for this page")
|
|
310
|
+
};
|
|
311
|
+
function registerIndexTools(server, clientProvider = getClient) {
|
|
312
|
+
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).", {
|
|
313
|
+
...rangeFilters,
|
|
314
|
+
sender: z4.string().optional().describe("Filter by sender principal"),
|
|
315
|
+
recipient: z4.string().optional().describe("Filter by recipient principal")
|
|
316
|
+
}, async (params) => jsonResponse(await clientProvider().index.ftTransfers.list(params)));
|
|
317
|
+
defineTool(server, "index_nft_transfers", "List decoded SIP-009 non-fungible-token transfers from the Index. Anonymous reads allowed (free-tier keys rejected).", {
|
|
318
|
+
...rangeFilters,
|
|
319
|
+
sender: z4.string().optional().describe("Filter by sender principal"),
|
|
320
|
+
recipient: z4.string().optional().describe("Filter by recipient principal"),
|
|
321
|
+
assetIdentifier: z4.string().optional().describe("Filter by asset identifier (contract::asset)")
|
|
322
|
+
}, async (params) => jsonResponse(await clientProvider().index.nftTransfers.list(params)));
|
|
323
|
+
defineTool(server, "index_events", "List decoded chain events from the Index by event type. Use this for event types without a dedicated tool (stx_*, ft_mint/burn, nft_mint/burn, print). For ft/nft transfers prefer index_ft_transfers / index_nft_transfers.", {
|
|
324
|
+
eventType: z4.enum(INDEX_EVENT_TYPES).describe("Required. Decoded event type to list."),
|
|
325
|
+
...rangeFilters,
|
|
326
|
+
sender: z4.string().optional().describe("Filter by sender principal"),
|
|
327
|
+
recipient: z4.string().optional().describe("Filter by recipient principal"),
|
|
328
|
+
assetIdentifier: z4.string().optional().describe("Filter by asset identifier where applicable")
|
|
329
|
+
}, async (params) => jsonResponse(await clientProvider().index.events.list(params)));
|
|
330
|
+
defineTool(server, "index_contract_calls", "List decoded contract calls from the Index (function name, args, result). Note: contract-call cursors are a SEPARATE keyspace from event cursors — they are not interchangeable.", {
|
|
331
|
+
...rangeFilters,
|
|
332
|
+
functionName: z4.string().optional().describe("Filter by called function name"),
|
|
333
|
+
sender: z4.string().optional().describe("Filter by caller principal")
|
|
334
|
+
}, async (params) => jsonResponse(await clientProvider().index.contractCalls.list(params)));
|
|
183
335
|
}
|
|
184
336
|
|
|
185
337
|
// src/tools/scaffold.ts
|
|
186
338
|
import { generateSubgraphCode } from "@secondlayer/scaffold";
|
|
187
|
-
import { z } from "zod/v4";
|
|
339
|
+
import { z as z5 } from "zod/v4";
|
|
188
340
|
var API_BASE = process.env.SECONDLAYER_API_URL || "https://api.secondlayer.tools";
|
|
189
341
|
async function fetchAbi(contractId) {
|
|
190
342
|
const res = await fetch(`${API_BASE}/api/node/contracts/${contractId}/abi`, {
|
|
@@ -203,17 +355,17 @@ async function fetchAbi(contractId) {
|
|
|
203
355
|
}
|
|
204
356
|
function registerScaffoldTools(server) {
|
|
205
357
|
defineTool(server, "scaffold_from_contract", "Generate a subgraph scaffold from a deployed Stacks contract. Fetches the ABI automatically.", {
|
|
206
|
-
contractId:
|
|
207
|
-
subgraphName:
|
|
358
|
+
contractId: z5.string().describe("Fully qualified contract ID (e.g. SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01)"),
|
|
359
|
+
subgraphName: z5.string().optional().describe("Override the subgraph name (defaults to contract name)")
|
|
208
360
|
}, async ({ contractId, subgraphName }) => {
|
|
209
361
|
const { functions, maps } = await fetchAbi(contractId);
|
|
210
362
|
const code = generateSubgraphCode(contractId, functions, subgraphName, maps);
|
|
211
363
|
return { content: [{ type: "text", text: code }] };
|
|
212
364
|
});
|
|
213
365
|
defineTool(server, "scaffold_from_abi", "Generate a subgraph scaffold from a provided ABI JSON. Use when you already have the ABI.", {
|
|
214
|
-
abi:
|
|
215
|
-
contractId:
|
|
216
|
-
subgraphName:
|
|
366
|
+
abi: z5.string().describe("ABI JSON string (the full contract ABI object)"),
|
|
367
|
+
contractId: z5.string().describe("Fully qualified contract ID"),
|
|
368
|
+
subgraphName: z5.string().optional().describe("Override the subgraph name")
|
|
217
369
|
}, async ({ abi, contractId, subgraphName }) => {
|
|
218
370
|
let parsed;
|
|
219
371
|
try {
|
|
@@ -229,9 +381,51 @@ function registerScaffoldTools(server) {
|
|
|
229
381
|
});
|
|
230
382
|
}
|
|
231
383
|
|
|
384
|
+
// src/tools/streams.ts
|
|
385
|
+
import { AuthError } from "@secondlayer/sdk";
|
|
386
|
+
import { z as z6 } from "zod/v4";
|
|
387
|
+
var STREAMS_EVENT_TYPES = [
|
|
388
|
+
"stx_transfer",
|
|
389
|
+
"stx_mint",
|
|
390
|
+
"stx_burn",
|
|
391
|
+
"stx_lock",
|
|
392
|
+
"ft_transfer",
|
|
393
|
+
"ft_mint",
|
|
394
|
+
"ft_burn",
|
|
395
|
+
"nft_transfer",
|
|
396
|
+
"nft_mint",
|
|
397
|
+
"nft_burn",
|
|
398
|
+
"print"
|
|
399
|
+
];
|
|
400
|
+
async function withStreamsAuthHint(fn) {
|
|
401
|
+
try {
|
|
402
|
+
return await fn();
|
|
403
|
+
} catch (err) {
|
|
404
|
+
if (err instanceof AuthError) {
|
|
405
|
+
throw Object.assign(new Error(err.message + keyHint), { status: 401 });
|
|
406
|
+
}
|
|
407
|
+
throw err;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function registerStreamsTools(server, clientProvider = getClient) {
|
|
411
|
+
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())));
|
|
412
|
+
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.", {
|
|
413
|
+
types: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to include"),
|
|
414
|
+
notTypes: z6.array(z6.enum(STREAMS_EVENT_TYPES)).optional().describe("Event types to exclude (applied after types)"),
|
|
415
|
+
contractId: z6.string().optional().describe("Filter by contract id"),
|
|
416
|
+
sender: z6.string().optional().describe("Filter by sender principal"),
|
|
417
|
+
recipient: z6.string().optional().describe("Filter by recipient principal"),
|
|
418
|
+
assetIdentifier: z6.string().optional().describe("Filter by asset identifier"),
|
|
419
|
+
fromBlock: z6.number().optional().describe("Start block (inclusive)"),
|
|
420
|
+
toBlock: z6.number().optional().describe("End block (inclusive)"),
|
|
421
|
+
cursor: z6.string().optional().describe("Opaque cursor from a prior response"),
|
|
422
|
+
limit: z6.number().optional().describe("Max events for this page")
|
|
423
|
+
}, async (params) => withStreamsAuthHint(async () => jsonResponse(await clientProvider().streams.events.list(params))));
|
|
424
|
+
}
|
|
425
|
+
|
|
232
426
|
// src/tools/subgraphs.ts
|
|
233
427
|
import { bundleSubgraphCode } from "@secondlayer/bundler";
|
|
234
|
-
import { z as
|
|
428
|
+
import { z as z7 } from "zod/v4";
|
|
235
429
|
function registerSubgraphTools(server, clientProvider = getClient) {
|
|
236
430
|
defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only.", {}, async () => {
|
|
237
431
|
const { data } = await clientProvider().subgraphs.list();
|
|
@@ -244,16 +438,16 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
244
438
|
]
|
|
245
439
|
};
|
|
246
440
|
});
|
|
247
|
-
defineTool(server, "subgraphs_get", "Get full details of a subgraph including schema, health, and table columns.", { name:
|
|
441
|
+
defineTool(server, "subgraphs_get", "Get full details of a subgraph including schema, health, and table columns.", { name: z7.string().describe("Subgraph name") }, async ({ name }) => {
|
|
248
442
|
const detail = await clientProvider().subgraphs.get(name);
|
|
249
443
|
return {
|
|
250
444
|
content: [{ type: "text", text: JSON.stringify(detail, null, 2) }]
|
|
251
445
|
};
|
|
252
446
|
});
|
|
253
447
|
defineTool(server, "subgraphs_spec", "Get generated API documentation for a subgraph. Defaults to compact agent schema; supports OpenAPI JSON and Markdown.", {
|
|
254
|
-
name:
|
|
255
|
-
format:
|
|
256
|
-
serverUrl:
|
|
448
|
+
name: z7.string().describe("Subgraph name"),
|
|
449
|
+
format: z7.enum(["agent", "openapi", "markdown"]).optional().describe("Spec format to return. Defaults to agent."),
|
|
450
|
+
serverUrl: z7.string().optional().describe("Override the server URL embedded in generated docs.")
|
|
257
451
|
}, async ({ name, format = "agent", serverUrl }) => {
|
|
258
452
|
const options = serverUrl ? { serverUrl } : undefined;
|
|
259
453
|
const spec = format === "openapi" ? await clientProvider().subgraphs.openapi(name, options) : format === "markdown" ? await clientProvider().subgraphs.markdown(name, options) : await clientProvider().subgraphs.schema(name, options);
|
|
@@ -267,15 +461,15 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
267
461
|
};
|
|
268
462
|
});
|
|
269
463
|
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.', {
|
|
270
|
-
name:
|
|
271
|
-
table:
|
|
272
|
-
filters:
|
|
273
|
-
sort:
|
|
274
|
-
order:
|
|
275
|
-
limit:
|
|
276
|
-
offset:
|
|
277
|
-
fields:
|
|
278
|
-
count:
|
|
464
|
+
name: z7.string().describe("Subgraph name"),
|
|
465
|
+
table: z7.string().describe("Table name"),
|
|
466
|
+
filters: z7.record(z7.string(), z7.string()).optional().describe('Column filters — plain values or with operators (e.g. {"amount.gte": "1000", "sender": "SP..."})'),
|
|
467
|
+
sort: z7.string().optional().describe("Column to sort by"),
|
|
468
|
+
order: z7.enum(["asc", "desc"]).optional().describe("Sort order"),
|
|
469
|
+
limit: z7.number().max(200).optional().describe("Max rows (default 50, max 200)"),
|
|
470
|
+
offset: z7.number().optional().describe("Offset for pagination"),
|
|
471
|
+
fields: z7.string().optional().describe('Comma-separated column list to return (e.g. "sender,amount")'),
|
|
472
|
+
count: z7.boolean().optional().describe("If true, return row count instead of rows")
|
|
279
473
|
}, async ({
|
|
280
474
|
name,
|
|
281
475
|
table,
|
|
@@ -308,9 +502,9 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
308
502
|
};
|
|
309
503
|
});
|
|
310
504
|
defineTool(server, "subgraphs_reindex", "Reindex a subgraph from a specific block range.", {
|
|
311
|
-
name:
|
|
312
|
-
fromBlock:
|
|
313
|
-
toBlock:
|
|
505
|
+
name: z7.string().describe("Subgraph name"),
|
|
506
|
+
fromBlock: z7.number().optional().describe("Start block (defaults to beginning)"),
|
|
507
|
+
toBlock: z7.number().optional().describe("End block (defaults to latest)")
|
|
314
508
|
}, async ({ name, fromBlock, toBlock }) => {
|
|
315
509
|
const result = await clientProvider().subgraphs.reindex(name, {
|
|
316
510
|
fromBlock,
|
|
@@ -320,13 +514,13 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
320
514
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
321
515
|
};
|
|
322
516
|
});
|
|
323
|
-
defineTool(server, "subgraphs_delete", "Delete a subgraph permanently.", { name:
|
|
517
|
+
defineTool(server, "subgraphs_delete", "Delete a subgraph permanently.", { name: z7.string().describe("Subgraph name") }, async ({ name }) => {
|
|
324
518
|
const result = await clientProvider().subgraphs.delete(name);
|
|
325
519
|
return { content: [{ type: "text", text: result.message }] };
|
|
326
520
|
});
|
|
327
521
|
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. Call `subgraphs_reindex` separately if you need a forced reindex.", {
|
|
328
|
-
code:
|
|
329
|
-
startBlock:
|
|
522
|
+
code: z7.string().describe("TypeScript source code containing a defineSubgraph() call"),
|
|
523
|
+
startBlock: z7.number().int().nonnegative().optional().describe("Override the definition startBlock for this deploy")
|
|
330
524
|
}, async ({ code, startBlock }) => {
|
|
331
525
|
const bundled = await bundleSubgraphCode(code);
|
|
332
526
|
const result = await clientProvider().subgraphs.deploy({
|
|
@@ -343,7 +537,7 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
343
537
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
344
538
|
};
|
|
345
539
|
});
|
|
346
|
-
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:
|
|
540
|
+
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 }) => {
|
|
347
541
|
const source = await clientProvider().subgraphs.getSource(name);
|
|
348
542
|
return {
|
|
349
543
|
content: [{ type: "text", text: JSON.stringify(source, null, 2) }]
|
|
@@ -352,7 +546,7 @@ function registerSubgraphTools(server, clientProvider = getClient) {
|
|
|
352
546
|
}
|
|
353
547
|
|
|
354
548
|
// src/tools/subscriptions.ts
|
|
355
|
-
import { z as
|
|
549
|
+
import { z as z8 } from "zod/v4";
|
|
356
550
|
function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
357
551
|
defineTool(server, "subscriptions_list", "List all subscriptions for the current account. Returns summary fields (no secrets).", {}, async () => {
|
|
358
552
|
const { data } = await clientProvider().subscriptions.list();
|
|
@@ -360,18 +554,18 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
360
554
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
361
555
|
};
|
|
362
556
|
});
|
|
363
|
-
defineTool(server, "subscriptions_get", "Get full detail for a subscription (filter, auth, retry config, circuit state).", { id:
|
|
557
|
+
defineTool(server, "subscriptions_get", "Get full detail for a subscription (filter, auth, retry config, circuit state).", { id: z8.string().describe("Subscription id") }, async ({ id }) => {
|
|
364
558
|
const detail = await clientProvider().subscriptions.get(id);
|
|
365
559
|
return {
|
|
366
560
|
content: [{ type: "text", text: JSON.stringify(detail, null, 2) }]
|
|
367
561
|
};
|
|
368
562
|
});
|
|
369
563
|
defineTool(server, "subscriptions_create", "Create a subscription. Returns `signingSecret` ONCE — forward it to the user so they can wire it into their receiver.", {
|
|
370
|
-
name:
|
|
371
|
-
subgraphName:
|
|
372
|
-
tableName:
|
|
373
|
-
url:
|
|
374
|
-
format:
|
|
564
|
+
name: z8.string().describe("Human-readable name, unique per account"),
|
|
565
|
+
subgraphName: z8.string().describe("Subgraph to subscribe to"),
|
|
566
|
+
tableName: z8.string().describe("Table within the subgraph"),
|
|
567
|
+
url: z8.string().describe("Webhook URL"),
|
|
568
|
+
format: z8.enum([
|
|
375
569
|
"standard-webhooks",
|
|
376
570
|
"inngest",
|
|
377
571
|
"trigger",
|
|
@@ -379,8 +573,8 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
379
573
|
"cloudevents",
|
|
380
574
|
"raw"
|
|
381
575
|
]).optional().describe("Wire format (default standard-webhooks)"),
|
|
382
|
-
runtime:
|
|
383
|
-
filter:
|
|
576
|
+
runtime: z8.enum(["inngest", "trigger", "cloudflare", "node"]).optional().describe("Receiver runtime label (display only)"),
|
|
577
|
+
filter: z8.record(z8.string(), z8.unknown()).optional().describe('Scalar filter DSL, e.g. {"amount": {"gte": 100}, "sender": "SP..."}')
|
|
384
578
|
}, async (input) => {
|
|
385
579
|
const res = await clientProvider().subscriptions.create(input);
|
|
386
580
|
return {
|
|
@@ -388,10 +582,10 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
388
582
|
};
|
|
389
583
|
});
|
|
390
584
|
defineTool(server, "subscriptions_update", "Patch a subscription (url, filter, format, runtime, retry, timeout, concurrency).", {
|
|
391
|
-
id:
|
|
392
|
-
url:
|
|
393
|
-
filter:
|
|
394
|
-
format:
|
|
585
|
+
id: z8.string(),
|
|
586
|
+
url: z8.string().optional(),
|
|
587
|
+
filter: z8.record(z8.string(), z8.unknown()).optional(),
|
|
588
|
+
format: z8.enum([
|
|
395
589
|
"standard-webhooks",
|
|
396
590
|
"inngest",
|
|
397
591
|
"trigger",
|
|
@@ -399,44 +593,44 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
399
593
|
"cloudevents",
|
|
400
594
|
"raw"
|
|
401
595
|
]).optional(),
|
|
402
|
-
runtime:
|
|
403
|
-
maxRetries:
|
|
404
|
-
timeoutMs:
|
|
405
|
-
concurrency:
|
|
596
|
+
runtime: z8.enum(["inngest", "trigger", "cloudflare", "node"]).nullable().optional(),
|
|
597
|
+
maxRetries: z8.number().int().min(0).optional(),
|
|
598
|
+
timeoutMs: z8.number().int().min(100).optional(),
|
|
599
|
+
concurrency: z8.number().int().min(1).optional()
|
|
406
600
|
}, async ({ id, ...patch }) => {
|
|
407
601
|
const res = await clientProvider().subscriptions.update(id, patch);
|
|
408
602
|
return {
|
|
409
603
|
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
410
604
|
};
|
|
411
605
|
});
|
|
412
|
-
defineTool(server, "subscriptions_pause", "Pause a subscription. Pending rows remain queued until resumed.", { id:
|
|
606
|
+
defineTool(server, "subscriptions_pause", "Pause a subscription. Pending rows remain queued until resumed.", { id: z8.string() }, async ({ id }) => {
|
|
413
607
|
const res = await clientProvider().subscriptions.pause(id);
|
|
414
608
|
return {
|
|
415
609
|
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
416
610
|
};
|
|
417
611
|
});
|
|
418
|
-
defineTool(server, "subscriptions_resume", "Resume a paused or circuit-open subscription and reset circuit failures.", { id:
|
|
612
|
+
defineTool(server, "subscriptions_resume", "Resume a paused or circuit-open subscription and reset circuit failures.", { id: z8.string() }, async ({ id }) => {
|
|
419
613
|
const res = await clientProvider().subscriptions.resume(id);
|
|
420
614
|
return {
|
|
421
615
|
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
422
616
|
};
|
|
423
617
|
});
|
|
424
|
-
defineTool(server, "subscriptions_delete", "Delete a subscription. Pending outbox rows are cascade-deleted.", { id:
|
|
618
|
+
defineTool(server, "subscriptions_delete", "Delete a subscription. Pending outbox rows are cascade-deleted.", { id: z8.string() }, async ({ id }) => {
|
|
425
619
|
const res = await clientProvider().subscriptions.delete(id);
|
|
426
620
|
return {
|
|
427
621
|
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
428
622
|
};
|
|
429
623
|
});
|
|
430
|
-
defineTool(server, "subscriptions_rotate_secret", "Rotate a subscription signing secret. Returns the new plaintext secret once.", { id:
|
|
624
|
+
defineTool(server, "subscriptions_rotate_secret", "Rotate a subscription signing secret. Returns the new plaintext secret once.", { id: z8.string() }, async ({ id }) => {
|
|
431
625
|
const res = await clientProvider().subscriptions.rotateSecret(id);
|
|
432
626
|
return {
|
|
433
627
|
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
434
628
|
};
|
|
435
629
|
});
|
|
436
630
|
defineTool(server, "subscriptions_replay", "Replay a block range for a subscription. Replays run at 10% of batch capacity — use sparingly.", {
|
|
437
|
-
id:
|
|
438
|
-
fromBlock:
|
|
439
|
-
toBlock:
|
|
631
|
+
id: z8.string(),
|
|
632
|
+
fromBlock: z8.number().int().nonnegative(),
|
|
633
|
+
toBlock: z8.number().int().nonnegative()
|
|
440
634
|
}, async ({ id, fromBlock, toBlock }) => {
|
|
441
635
|
const res = await clientProvider().subscriptions.replay(id, {
|
|
442
636
|
fromBlock,
|
|
@@ -446,22 +640,22 @@ function registerSubscriptionTools(server, clientProvider = getClient) {
|
|
|
446
640
|
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
447
641
|
};
|
|
448
642
|
});
|
|
449
|
-
defineTool(server, "subscriptions_dead", "Return the last 100 dead-letter outbox rows for a subscription.", { id:
|
|
643
|
+
defineTool(server, "subscriptions_dead", "Return the last 100 dead-letter outbox rows for a subscription.", { id: z8.string() }, async ({ id }) => {
|
|
450
644
|
const { data } = await clientProvider().subscriptions.dead(id);
|
|
451
645
|
return {
|
|
452
646
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
453
647
|
};
|
|
454
648
|
});
|
|
455
649
|
defineTool(server, "subscriptions_requeue_dead", "Requeue one dead-letter outbox row for delivery retry.", {
|
|
456
|
-
id:
|
|
457
|
-
outboxId:
|
|
650
|
+
id: z8.string(),
|
|
651
|
+
outboxId: z8.string()
|
|
458
652
|
}, async ({ id, outboxId }) => {
|
|
459
653
|
const res = await clientProvider().subscriptions.requeueDead(id, outboxId);
|
|
460
654
|
return {
|
|
461
655
|
content: [{ type: "text", text: JSON.stringify(res, null, 2) }]
|
|
462
656
|
};
|
|
463
657
|
});
|
|
464
|
-
defineTool(server, "subscriptions_recent_deliveries", "Return the last 100 delivery attempts (attempt #, status code, duration, truncated response).", { id:
|
|
658
|
+
defineTool(server, "subscriptions_recent_deliveries", "Return the last 100 delivery attempts (attempt #, status code, duration, truncated response).", { id: z8.string() }, async ({ id }) => {
|
|
465
659
|
const { data } = await clientProvider().subscriptions.recentDeliveries(id);
|
|
466
660
|
return {
|
|
467
661
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
@@ -481,6 +675,10 @@ function createServer() {
|
|
|
481
675
|
registerSubgraphTools(server);
|
|
482
676
|
registerSubscriptionTools(server);
|
|
483
677
|
registerAccountTools(server);
|
|
678
|
+
registerDatasetTools(server);
|
|
679
|
+
registerIndexTools(server);
|
|
680
|
+
registerStreamsTools(server);
|
|
681
|
+
registerContractTools(server);
|
|
484
682
|
registerResources(server);
|
|
485
683
|
return server;
|
|
486
684
|
}
|
|
@@ -571,5 +769,5 @@ httpServer.listen(port, () => {
|
|
|
571
769
|
console.error("Warning: SECONDLAYER_MCP_SECRET not set, authentication disabled");
|
|
572
770
|
});
|
|
573
771
|
|
|
574
|
-
//# debugId=
|
|
772
|
+
//# debugId=D41ACEAFF3E3A4CC64756E2164756E21
|
|
575
773
|
//# sourceMappingURL=bin-http.js.map
|