@secondlayer/mcp 0.3.2 → 0.3.3

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
@@ -4,9 +4,85 @@ import { dirname, join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
6
 
7
- // src/tools/templates.ts
7
+ // src/resources.ts
8
+ import { templates } from "@secondlayer/subgraphs/templates";
9
+
10
+ // src/tools/streams.ts
8
11
  import { z } from "zod/v4";
9
- import { templates, getTemplateById, getTemplatesByCategory } from "@secondlayer/subgraphs/templates";
12
+
13
+ // src/lib/client.ts
14
+ import { SecondLayer } from "@secondlayer/sdk";
15
+ var instance = null;
16
+ function getClient() {
17
+ if (!instance) {
18
+ const apiKey = process.env.SECONDLAYER_API_KEY;
19
+ if (!apiKey) {
20
+ throw new Error("SECONDLAYER_API_KEY environment variable is required. " + "Get your key at https://app.secondlayer.tools/settings/api-keys");
21
+ }
22
+ instance = new SecondLayer({ apiKey });
23
+ }
24
+ return instance;
25
+ }
26
+ async function apiRequest(method, path, body) {
27
+ const apiKey = process.env.SECONDLAYER_API_KEY;
28
+ if (!apiKey)
29
+ throw new Error("SECONDLAYER_API_KEY required");
30
+ const baseUrl = process.env.SECONDLAYER_API_URL || "https://api.secondlayer.tools";
31
+ const res = await fetch(`${baseUrl}${path}`, {
32
+ method,
33
+ headers: {
34
+ "Content-Type": "application/json",
35
+ Authorization: `Bearer ${apiKey}`
36
+ },
37
+ body: body ? JSON.stringify(body) : undefined
38
+ });
39
+ if (!res.ok) {
40
+ const text = await res.text().catch(() => "");
41
+ throw Object.assign(new Error(text || `HTTP ${res.status}`), {
42
+ status: res.status
43
+ });
44
+ }
45
+ if (res.status === 204)
46
+ return;
47
+ return res.json();
48
+ }
49
+
50
+ // src/lib/format.ts
51
+ function formatStreamSummary(s) {
52
+ return {
53
+ id: s.id,
54
+ name: s.name,
55
+ status: s.status,
56
+ endpointUrl: s.endpointUrl,
57
+ totalDeliveries: s.totalDeliveries,
58
+ failedDeliveries: s.failedDeliveries
59
+ };
60
+ }
61
+ function formatSubgraphSummary(s) {
62
+ return {
63
+ name: s.name,
64
+ status: s.status,
65
+ tables: Array.isArray(s.tables) ? s.tables : Object.keys(s.tables),
66
+ lastProcessedBlock: s.lastProcessedBlock
67
+ };
68
+ }
69
+ function formatDeliverySummary(d) {
70
+ return {
71
+ id: d.id,
72
+ blockHeight: d.blockHeight,
73
+ status: d.status,
74
+ statusCode: d.statusCode,
75
+ attempts: d.attempts,
76
+ createdAt: d.createdAt
77
+ };
78
+ }
79
+ function withCap(items, cap) {
80
+ return {
81
+ items: items.slice(0, cap),
82
+ truncated: items.length > cap,
83
+ total: items.length
84
+ };
85
+ }
10
86
 
11
87
  // src/lib/tool.ts
12
88
  function defineTool(server, name, description, schema, handler) {
@@ -18,7 +94,12 @@ function defineTool(server, name, description, schema, handler) {
18
94
  const status = err instanceof Error && "status" in err ? err.status : 0;
19
95
  const type = status === 401 ? "unauthorized" : status === 404 ? "not_found" : status === 429 ? "rate_limited" : status >= 500 ? "server_error" : "error";
20
96
  return {
21
- content: [{ type: "text", text: JSON.stringify({ error: { type, status, message } }) }],
97
+ content: [
98
+ {
99
+ type: "text",
100
+ text: JSON.stringify({ error: { type, status, message } })
101
+ }
102
+ ],
22
103
  isError: true
23
104
  };
24
105
  }
@@ -26,32 +107,330 @@ function defineTool(server, name, description, schema, handler) {
26
107
  server.tool(name, description, schema, wrappedHandler);
27
108
  }
28
109
 
29
- // src/tools/templates.ts
30
- function registerTemplateTools(server) {
31
- defineTool(server, "templates_list", "List available subgraph templates. Returns metadata only — use templates_get for full code.", { category: z.enum(["defi", "nft", "token", "infrastructure"]).optional().describe("Filter by category") }, async ({ category }) => {
32
- const list = category ? getTemplatesByCategory(category) : templates;
110
+ // src/tools/streams.ts
111
+ var FilterSchema = z.discriminatedUnion("type", [
112
+ z.object({
113
+ type: z.literal("stx_transfer"),
114
+ sender: z.string().optional(),
115
+ recipient: z.string().optional(),
116
+ minAmount: z.number().optional(),
117
+ maxAmount: z.number().optional()
118
+ }),
119
+ z.object({
120
+ type: z.literal("stx_mint"),
121
+ recipient: z.string().optional(),
122
+ minAmount: z.number().optional()
123
+ }),
124
+ z.object({
125
+ type: z.literal("stx_burn"),
126
+ sender: z.string().optional(),
127
+ minAmount: z.number().optional()
128
+ }),
129
+ z.object({
130
+ type: z.literal("stx_lock"),
131
+ lockedAddress: z.string().optional(),
132
+ minAmount: z.number().optional()
133
+ }),
134
+ z.object({
135
+ type: z.literal("ft_transfer"),
136
+ sender: z.string().optional(),
137
+ recipient: z.string().optional(),
138
+ assetIdentifier: z.string().optional(),
139
+ minAmount: z.number().optional()
140
+ }),
141
+ z.object({
142
+ type: z.literal("ft_mint"),
143
+ recipient: z.string().optional(),
144
+ assetIdentifier: z.string().optional(),
145
+ minAmount: z.number().optional()
146
+ }),
147
+ z.object({
148
+ type: z.literal("ft_burn"),
149
+ sender: z.string().optional(),
150
+ assetIdentifier: z.string().optional(),
151
+ minAmount: z.number().optional()
152
+ }),
153
+ z.object({
154
+ type: z.literal("nft_transfer"),
155
+ sender: z.string().optional(),
156
+ recipient: z.string().optional(),
157
+ assetIdentifier: z.string().optional(),
158
+ tokenId: z.string().optional()
159
+ }),
160
+ z.object({
161
+ type: z.literal("nft_mint"),
162
+ recipient: z.string().optional(),
163
+ assetIdentifier: z.string().optional(),
164
+ tokenId: z.string().optional()
165
+ }),
166
+ z.object({
167
+ type: z.literal("nft_burn"),
168
+ sender: z.string().optional(),
169
+ assetIdentifier: z.string().optional(),
170
+ tokenId: z.string().optional()
171
+ }),
172
+ z.object({
173
+ type: z.literal("contract_call"),
174
+ contractId: z.string().optional(),
175
+ functionName: z.string().optional(),
176
+ caller: z.string().optional()
177
+ }),
178
+ z.object({
179
+ type: z.literal("contract_deploy"),
180
+ deployer: z.string().optional(),
181
+ contractName: z.string().optional()
182
+ }),
183
+ z.object({
184
+ type: z.literal("print_event"),
185
+ contractId: z.string().optional(),
186
+ topic: z.string().optional(),
187
+ contains: z.string().optional()
188
+ })
189
+ ]);
190
+ function registerStreamTools(server) {
191
+ defineTool(server, "streams_list", "List all webhook streams. Returns summary fields only.", {
192
+ status: z.enum(["active", "inactive", "paused", "failed"]).optional().describe("Filter by status")
193
+ }, async ({ status }) => {
194
+ const { streams } = await getClient().streams.list(status ? { status } : undefined);
33
195
  return {
34
- content: [{
35
- type: "text",
36
- text: JSON.stringify(list.map((t) => ({
37
- id: t.id,
38
- name: t.name,
39
- description: t.description,
40
- category: t.category
41
- })), null, 2)
42
- }]
196
+ content: [
197
+ {
198
+ type: "text",
199
+ text: JSON.stringify(streams.map(formatStreamSummary), null, 2)
200
+ }
201
+ ]
43
202
  };
44
203
  });
45
- defineTool(server, "templates_get", "Get a template's full code and prompt by ID.", { id: z.string().describe("Template ID (e.g. 'dex-swaps')") }, async ({ id }) => {
46
- const template = getTemplateById(id);
47
- if (!template) {
48
- return { content: [{ type: "text", text: `Template "${id}" not found. Use templates_list to see available templates.` }], isError: true };
204
+ defineTool(server, "streams_get", "Get full details of a stream by ID (accepts UUID prefix).", { id: z.string().describe("Stream UUID or prefix") }, async ({ id }) => {
205
+ const stream = await getClient().streams.get(id);
206
+ return {
207
+ content: [{ type: "text", text: JSON.stringify(stream, null, 2) }]
208
+ };
209
+ });
210
+ defineTool(server, "streams_create", "Create a new webhook stream with filters.", {
211
+ name: z.string().describe("Stream name"),
212
+ endpointUrl: z.string().describe("Webhook endpoint URL"),
213
+ filters: z.array(FilterSchema).min(1).describe("Event filters (at least one required)")
214
+ }, async ({ name, endpointUrl, filters }) => {
215
+ const result = await getClient().streams.create({
216
+ name,
217
+ endpointUrl,
218
+ filters
219
+ });
220
+ return {
221
+ content: [
222
+ {
223
+ type: "text",
224
+ text: JSON.stringify({ id: result.stream.id, signingSecret: result.signingSecret }, null, 2)
225
+ }
226
+ ]
227
+ };
228
+ });
229
+ defineTool(server, "streams_update", "Update a stream's name, endpoint, or filters.", {
230
+ id: z.string().describe("Stream UUID or prefix"),
231
+ name: z.string().optional().describe("New name"),
232
+ endpointUrl: z.string().optional().describe("New endpoint URL"),
233
+ filters: z.array(FilterSchema).min(1).optional().describe("New filters")
234
+ }, async ({ id, name, endpointUrl, filters }) => {
235
+ const data = {};
236
+ if (name !== undefined)
237
+ data.name = name;
238
+ if (endpointUrl !== undefined)
239
+ data.endpointUrl = endpointUrl;
240
+ if (filters !== undefined)
241
+ data.filters = filters;
242
+ const stream = await getClient().streams.update(id, data);
243
+ return {
244
+ content: [
245
+ {
246
+ type: "text",
247
+ text: JSON.stringify(formatStreamSummary(stream), null, 2)
248
+ }
249
+ ]
250
+ };
251
+ });
252
+ defineTool(server, "streams_delete", "Delete a stream permanently.", { id: z.string().describe("Stream UUID or prefix") }, async ({ id }) => {
253
+ await getClient().streams.delete(id);
254
+ return { content: [{ type: "text", text: `Stream ${id} deleted.` }] };
255
+ });
256
+ defineTool(server, "streams_toggle", "Enable or disable a stream.", {
257
+ id: z.string().describe("Stream UUID or prefix"),
258
+ enabled: z.boolean().describe("true to enable, false to disable")
259
+ }, async ({ id, enabled }) => {
260
+ const stream = enabled ? await getClient().streams.enable(id) : await getClient().streams.disable(id);
261
+ return {
262
+ content: [
263
+ {
264
+ type: "text",
265
+ text: JSON.stringify({ id: stream.id, status: stream.status }, null, 2)
266
+ }
267
+ ]
268
+ };
269
+ });
270
+ defineTool(server, "streams_deliveries", "List recent deliveries for a stream (max 25).", {
271
+ id: z.string().describe("Stream UUID or prefix"),
272
+ limit: z.number().max(25).optional().describe("Max results (default 25)"),
273
+ status: z.string().optional().describe("Filter by delivery status")
274
+ }, async ({ id, limit, status }) => {
275
+ const { deliveries } = await getClient().streams.listDeliveries(id, {
276
+ limit: limit ?? 25,
277
+ status
278
+ });
279
+ const result = withCap(deliveries.map(formatDeliverySummary), 25);
280
+ return {
281
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
282
+ };
283
+ });
284
+ defineTool(server, "streams_pause_all", "Pause all active streams. Without confirm: true, returns a preview of streams that would be paused.", {
285
+ confirm: z.boolean().optional().describe("Set to true to execute. Omit or false for preview only.")
286
+ }, async ({ confirm }) => {
287
+ if (!confirm) {
288
+ const { streams } = await getClient().streams.list({
289
+ status: "active"
290
+ });
291
+ const names = streams.map((s) => s.name);
292
+ return {
293
+ content: [
294
+ {
295
+ type: "text",
296
+ text: JSON.stringify({ preview: true, count: names.length, streams: names }, null, 2)
297
+ }
298
+ ]
299
+ };
49
300
  }
301
+ const result = await getClient().streams.pauseAll();
302
+ return {
303
+ content: [
304
+ {
305
+ type: "text",
306
+ text: JSON.stringify({
307
+ paused: result.paused,
308
+ streams: result.streams.map(formatStreamSummary)
309
+ }, null, 2)
310
+ }
311
+ ]
312
+ };
313
+ });
314
+ defineTool(server, "streams_resume_all", "Resume all paused streams. Without confirm: true, returns a preview of streams that would be resumed.", {
315
+ confirm: z.boolean().optional().describe("Set to true to execute. Omit or false for preview only.")
316
+ }, async ({ confirm }) => {
317
+ if (!confirm) {
318
+ const { streams } = await getClient().streams.list({
319
+ status: "paused"
320
+ });
321
+ const names = streams.map((s) => s.name);
322
+ return {
323
+ content: [
324
+ {
325
+ type: "text",
326
+ text: JSON.stringify({ preview: true, count: names.length, streams: names }, null, 2)
327
+ }
328
+ ]
329
+ };
330
+ }
331
+ const result = await getClient().streams.resumeAll();
332
+ return {
333
+ content: [
334
+ {
335
+ type: "text",
336
+ text: JSON.stringify({
337
+ resumed: result.resumed,
338
+ streams: result.streams.map(formatStreamSummary)
339
+ }, null, 2)
340
+ }
341
+ ]
342
+ };
343
+ });
344
+ defineTool(server, "streams_replay", "Replay blocks through a stream, re-delivering events for a block range.", {
345
+ id: z.string().describe("Stream UUID or prefix"),
346
+ fromBlock: z.number().describe("Start block height (inclusive)"),
347
+ toBlock: z.number().describe("End block height (inclusive)")
348
+ }, async ({ id, fromBlock, toBlock }) => {
349
+ const result = await apiRequest("POST", `/api/streams/${id}/replay`, { fromBlock, toBlock });
50
350
  return {
51
- content: [{
52
- type: "text",
53
- text: JSON.stringify({ id: template.id, name: template.name, description: template.description, category: template.category, code: template.code, prompt: template.prompt }, null, 2)
54
- }]
351
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
352
+ };
353
+ });
354
+ defineTool(server, "streams_rotate_secret", "Rotate the signing secret for a stream. Returns the new secret.", { id: z.string().describe("Stream UUID or prefix") }, async ({ id }) => {
355
+ const result = await getClient().streams.rotateSecret(id);
356
+ return {
357
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
358
+ };
359
+ });
360
+ }
361
+
362
+ // src/resources.ts
363
+ var FILTERS_REFERENCE = FilterSchema.options.map((opt) => ({
364
+ type: opt.shape.type.def.values[0],
365
+ fields: Object.keys(opt.shape).filter((k) => k !== "type")
366
+ }));
367
+ var COLUMN_TYPES = [
368
+ {
369
+ type: "uint",
370
+ sqlType: "bigint",
371
+ description: "Unsigned integer (Clarity uint)"
372
+ },
373
+ {
374
+ type: "int",
375
+ sqlType: "bigint",
376
+ description: "Signed integer (Clarity int)"
377
+ },
378
+ { type: "text", sqlType: "text", description: "UTF-8 string" },
379
+ {
380
+ type: "principal",
381
+ sqlType: "text",
382
+ description: "Stacks address (standard or contract)"
383
+ },
384
+ { type: "bool", sqlType: "boolean", description: "Boolean value" },
385
+ { type: "json", sqlType: "jsonb", description: "Arbitrary JSON data" },
386
+ {
387
+ options: ["nullable", "indexed", "search"],
388
+ description: "Column options: nullable allows NULL, indexed creates a B-tree index, search enables full-text search"
389
+ }
390
+ ];
391
+ function registerResources(server) {
392
+ server.resource("filters", "secondlayer://filters", { description: "Stream filter types and their available fields" }, async () => ({
393
+ contents: [
394
+ {
395
+ uri: "secondlayer://filters",
396
+ mimeType: "application/json",
397
+ text: JSON.stringify(FILTERS_REFERENCE, null, 2)
398
+ }
399
+ ]
400
+ }));
401
+ server.resource("column-types", "secondlayer://column-types", { description: "Subgraph column types, SQL mappings, and options" }, async () => ({
402
+ contents: [
403
+ {
404
+ uri: "secondlayer://column-types",
405
+ mimeType: "application/json",
406
+ text: JSON.stringify(COLUMN_TYPES, null, 2)
407
+ }
408
+ ]
409
+ }));
410
+ server.resource("templates", "secondlayer://templates", {
411
+ description: "Available subgraph templates with descriptions and categories"
412
+ }, async () => ({
413
+ contents: [
414
+ {
415
+ uri: "secondlayer://templates",
416
+ mimeType: "application/json",
417
+ text: JSON.stringify(templates.map(({ id, name, description, category }) => ({
418
+ id,
419
+ name,
420
+ description,
421
+ category
422
+ })), null, 2)
423
+ }
424
+ ]
425
+ }));
426
+ }
427
+
428
+ // src/tools/account.ts
429
+ function registerAccountTools(server) {
430
+ defineTool(server, "account_whoami", "Show the authenticated account's email and plan.", {}, async () => {
431
+ const result = await apiRequest("GET", "/api/accounts/me");
432
+ return {
433
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
55
434
  };
56
435
  });
57
436
  }
@@ -270,221 +649,23 @@ function registerScaffoldTools(server) {
270
649
  try {
271
650
  parsed = JSON.parse(abi);
272
651
  } catch {
273
- return { content: [{ type: "text", text: "Invalid ABI JSON" }], isError: true };
274
- }
275
- const code = generateSubgraphCode(contractId, parsed.functions ?? [], subgraphName, parsed.maps ?? []);
276
- return { content: [{ type: "text", text: code }] };
277
- });
278
- }
279
-
280
- // src/tools/streams.ts
281
- import { z as z3 } from "zod/v4";
282
-
283
- // src/lib/client.ts
284
- import { SecondLayer } from "@secondlayer/sdk";
285
- var instance = null;
286
- function getClient() {
287
- if (!instance) {
288
- const apiKey = process.env.SECONDLAYER_API_KEY;
289
- if (!apiKey) {
290
- throw new Error("SECONDLAYER_API_KEY environment variable is required. " + "Get your key at https://app.secondlayer.tools/settings/api-keys");
291
- }
292
- instance = new SecondLayer({ apiKey });
293
- }
294
- return instance;
295
- }
296
- async function apiRequest(method, path, body) {
297
- const apiKey = process.env.SECONDLAYER_API_KEY;
298
- if (!apiKey)
299
- throw new Error("SECONDLAYER_API_KEY required");
300
- const baseUrl = process.env.SECONDLAYER_API_URL || "https://api.secondlayer.tools";
301
- const res = await fetch(`${baseUrl}${path}`, {
302
- method,
303
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
304
- body: body ? JSON.stringify(body) : undefined
305
- });
306
- if (!res.ok) {
307
- const text = await res.text().catch(() => "");
308
- throw Object.assign(new Error(text || `HTTP ${res.status}`), { status: res.status });
309
- }
310
- if (res.status === 204)
311
- return;
312
- return res.json();
313
- }
314
-
315
- // src/lib/format.ts
316
- function formatStreamSummary(s) {
317
- return {
318
- id: s.id,
319
- name: s.name,
320
- status: s.status,
321
- endpointUrl: s.endpointUrl,
322
- totalDeliveries: s.totalDeliveries,
323
- failedDeliveries: s.failedDeliveries
324
- };
325
- }
326
- function formatSubgraphSummary(s) {
327
- return {
328
- name: s.name,
329
- status: s.status,
330
- tables: Array.isArray(s.tables) ? s.tables : Object.keys(s.tables),
331
- lastProcessedBlock: s.lastProcessedBlock
332
- };
333
- }
334
- function formatDeliverySummary(d) {
335
- return {
336
- id: d.id,
337
- blockHeight: d.blockHeight,
338
- status: d.status,
339
- statusCode: d.statusCode,
340
- attempts: d.attempts,
341
- createdAt: d.createdAt
342
- };
343
- }
344
- function withCap(items, cap) {
345
- return {
346
- items: items.slice(0, cap),
347
- truncated: items.length > cap,
348
- total: items.length
349
- };
350
- }
351
-
352
- // src/tools/streams.ts
353
- var FilterSchema = z3.discriminatedUnion("type", [
354
- z3.object({ type: z3.literal("stx_transfer"), sender: z3.string().optional(), recipient: z3.string().optional(), minAmount: z3.number().optional(), maxAmount: z3.number().optional() }),
355
- z3.object({ type: z3.literal("stx_mint"), recipient: z3.string().optional(), minAmount: z3.number().optional() }),
356
- z3.object({ type: z3.literal("stx_burn"), sender: z3.string().optional(), minAmount: z3.number().optional() }),
357
- z3.object({ type: z3.literal("stx_lock"), lockedAddress: z3.string().optional(), minAmount: z3.number().optional() }),
358
- z3.object({ type: z3.literal("ft_transfer"), sender: z3.string().optional(), recipient: z3.string().optional(), assetIdentifier: z3.string().optional(), minAmount: z3.number().optional() }),
359
- z3.object({ type: z3.literal("ft_mint"), recipient: z3.string().optional(), assetIdentifier: z3.string().optional(), minAmount: z3.number().optional() }),
360
- z3.object({ type: z3.literal("ft_burn"), sender: z3.string().optional(), assetIdentifier: z3.string().optional(), minAmount: z3.number().optional() }),
361
- z3.object({ type: z3.literal("nft_transfer"), sender: z3.string().optional(), recipient: z3.string().optional(), assetIdentifier: z3.string().optional(), tokenId: z3.string().optional() }),
362
- z3.object({ type: z3.literal("nft_mint"), recipient: z3.string().optional(), assetIdentifier: z3.string().optional(), tokenId: z3.string().optional() }),
363
- z3.object({ type: z3.literal("nft_burn"), sender: z3.string().optional(), assetIdentifier: z3.string().optional(), tokenId: z3.string().optional() }),
364
- z3.object({ type: z3.literal("contract_call"), contractId: z3.string().optional(), functionName: z3.string().optional(), caller: z3.string().optional() }),
365
- z3.object({ type: z3.literal("contract_deploy"), deployer: z3.string().optional(), contractName: z3.string().optional() }),
366
- z3.object({ type: z3.literal("print_event"), contractId: z3.string().optional(), topic: z3.string().optional(), contains: z3.string().optional() })
367
- ]);
368
- function registerStreamTools(server) {
369
- defineTool(server, "streams_list", "List all webhook streams. Returns summary fields only.", { status: z3.enum(["active", "inactive", "paused", "failed"]).optional().describe("Filter by status") }, async ({ status }) => {
370
- const { streams } = await getClient().streams.list(status ? { status } : undefined);
371
- return {
372
- content: [{ type: "text", text: JSON.stringify(streams.map(formatStreamSummary), null, 2) }]
373
- };
374
- });
375
- defineTool(server, "streams_get", "Get full details of a stream by ID (accepts UUID prefix).", { id: z3.string().describe("Stream UUID or prefix") }, async ({ id }) => {
376
- const stream = await getClient().streams.get(id);
377
- return { content: [{ type: "text", text: JSON.stringify(stream, null, 2) }] };
378
- });
379
- defineTool(server, "streams_create", "Create a new webhook stream with filters.", {
380
- name: z3.string().describe("Stream name"),
381
- endpointUrl: z3.string().describe("Webhook endpoint URL"),
382
- filters: z3.array(FilterSchema).min(1).describe("Event filters (at least one required)")
383
- }, async ({ name, endpointUrl, filters }) => {
384
- const result = await getClient().streams.create({ name, endpointUrl, filters });
385
- return {
386
- content: [{ type: "text", text: JSON.stringify({ id: result.stream.id, signingSecret: result.signingSecret }, null, 2) }]
387
- };
388
- });
389
- defineTool(server, "streams_update", "Update a stream's name, endpoint, or filters.", {
390
- id: z3.string().describe("Stream UUID or prefix"),
391
- name: z3.string().optional().describe("New name"),
392
- endpointUrl: z3.string().optional().describe("New endpoint URL"),
393
- filters: z3.array(FilterSchema).min(1).optional().describe("New filters")
394
- }, async ({ id, name, endpointUrl, filters }) => {
395
- const data = {};
396
- if (name !== undefined)
397
- data.name = name;
398
- if (endpointUrl !== undefined)
399
- data.endpointUrl = endpointUrl;
400
- if (filters !== undefined)
401
- data.filters = filters;
402
- const stream = await getClient().streams.update(id, data);
403
- return { content: [{ type: "text", text: JSON.stringify(formatStreamSummary(stream), null, 2) }] };
404
- });
405
- defineTool(server, "streams_delete", "Delete a stream permanently.", { id: z3.string().describe("Stream UUID or prefix") }, async ({ id }) => {
406
- await getClient().streams.delete(id);
407
- return { content: [{ type: "text", text: `Stream ${id} deleted.` }] };
408
- });
409
- defineTool(server, "streams_toggle", "Enable or disable a stream.", {
410
- id: z3.string().describe("Stream UUID or prefix"),
411
- enabled: z3.boolean().describe("true to enable, false to disable")
412
- }, async ({ id, enabled }) => {
413
- const stream = enabled ? await getClient().streams.enable(id) : await getClient().streams.disable(id);
414
- return { content: [{ type: "text", text: JSON.stringify({ id: stream.id, status: stream.status }, null, 2) }] };
415
- });
416
- defineTool(server, "streams_deliveries", "List recent deliveries for a stream (max 25).", {
417
- id: z3.string().describe("Stream UUID or prefix"),
418
- limit: z3.number().max(25).optional().describe("Max results (default 25)"),
419
- status: z3.string().optional().describe("Filter by delivery status")
420
- }, async ({ id, limit, status }) => {
421
- const { deliveries } = await getClient().streams.listDeliveries(id, { limit: limit ?? 25, status });
422
- const result = withCap(deliveries.map(formatDeliverySummary), 25);
423
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
424
- });
425
- defineTool(server, "streams_pause_all", "Pause all active streams. Without confirm: true, returns a preview of streams that would be paused.", {
426
- confirm: z3.boolean().optional().describe("Set to true to execute. Omit or false for preview only.")
427
- }, async ({ confirm }) => {
428
- if (!confirm) {
429
- const { streams } = await getClient().streams.list({ status: "active" });
430
- const names = streams.map((s) => s.name);
431
- return {
432
- content: [{
433
- type: "text",
434
- text: JSON.stringify({ preview: true, count: names.length, streams: names }, null, 2)
435
- }]
436
- };
437
- }
438
- const result = await getClient().streams.pauseAll();
439
- return {
440
- content: [{
441
- type: "text",
442
- text: JSON.stringify({ paused: result.paused, streams: result.streams.map(formatStreamSummary) }, null, 2)
443
- }]
444
- };
445
- });
446
- defineTool(server, "streams_resume_all", "Resume all paused streams. Without confirm: true, returns a preview of streams that would be resumed.", {
447
- confirm: z3.boolean().optional().describe("Set to true to execute. Omit or false for preview only.")
448
- }, async ({ confirm }) => {
449
- if (!confirm) {
450
- const { streams } = await getClient().streams.list({ status: "paused" });
451
- const names = streams.map((s) => s.name);
452
652
  return {
453
- content: [{
454
- type: "text",
455
- text: JSON.stringify({ preview: true, count: names.length, streams: names }, null, 2)
456
- }]
653
+ content: [{ type: "text", text: "Invalid ABI JSON" }],
654
+ isError: true
457
655
  };
458
656
  }
459
- const result = await getClient().streams.resumeAll();
460
- return {
461
- content: [{
462
- type: "text",
463
- text: JSON.stringify({ resumed: result.resumed, streams: result.streams.map(formatStreamSummary) }, null, 2)
464
- }]
465
- };
466
- });
467
- defineTool(server, "streams_replay", "Replay blocks through a stream, re-delivering events for a block range.", {
468
- id: z3.string().describe("Stream UUID or prefix"),
469
- fromBlock: z3.number().describe("Start block height (inclusive)"),
470
- toBlock: z3.number().describe("End block height (inclusive)")
471
- }, async ({ id, fromBlock, toBlock }) => {
472
- const result = await apiRequest("POST", `/api/streams/${id}/replay`, { fromBlock, toBlock });
473
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
474
- });
475
- defineTool(server, "streams_rotate_secret", "Rotate the signing secret for a stream. Returns the new secret.", { id: z3.string().describe("Stream UUID or prefix") }, async ({ id }) => {
476
- const result = await getClient().streams.rotateSecret(id);
477
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
657
+ const code = generateSubgraphCode(contractId, parsed.functions ?? [], subgraphName, parsed.maps ?? []);
658
+ return { content: [{ type: "text", text: code }] };
478
659
  });
479
660
  }
480
661
 
481
662
  // src/tools/subgraphs.ts
482
- import { z as z4 } from "zod/v4";
663
+ import { z as z3 } from "zod/v4";
483
664
 
484
665
  // src/lib/bundle.ts
485
- import esbuild from "esbuild";
486
- import { validateSubgraphDefinition } from "@secondlayer/subgraphs/validate";
487
666
  import { sourceKey } from "@secondlayer/subgraphs";
667
+ import { validateSubgraphDefinition } from "@secondlayer/subgraphs/validate";
668
+ import esbuild from "esbuild";
488
669
  async function bundleSubgraphCode(code) {
489
670
  let result;
490
671
  try {
@@ -529,27 +710,46 @@ function registerSubgraphTools(server) {
529
710
  defineTool(server, "subgraphs_list", "List all deployed subgraphs. Returns summary fields only.", {}, async () => {
530
711
  const { data } = await getClient().subgraphs.list();
531
712
  return {
532
- content: [{ type: "text", text: JSON.stringify(data.map(formatSubgraphSummary), null, 2) }]
713
+ content: [
714
+ {
715
+ type: "text",
716
+ text: JSON.stringify(data.map(formatSubgraphSummary), null, 2)
717
+ }
718
+ ]
533
719
  };
534
720
  });
535
- defineTool(server, "subgraphs_get", "Get full details of a subgraph including schema, health, and table columns.", { name: z4.string().describe("Subgraph name") }, async ({ name }) => {
721
+ defineTool(server, "subgraphs_get", "Get full details of a subgraph including schema, health, and table columns.", { name: z3.string().describe("Subgraph name") }, async ({ name }) => {
536
722
  const detail = await getClient().subgraphs.get(name);
537
- return { content: [{ type: "text", text: JSON.stringify(detail, null, 2) }] };
723
+ return {
724
+ content: [{ type: "text", text: JSON.stringify(detail, null, 2) }]
725
+ };
538
726
  });
539
727
  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.', {
540
- name: z4.string().describe("Subgraph name"),
541
- table: z4.string().describe("Table name"),
542
- filters: z4.record(z4.string(), z4.string()).optional().describe('Column filters — plain values or with operators (e.g. {"amount.gte": "1000", "sender": "SP..."})'),
543
- sort: z4.string().optional().describe("Column to sort by"),
544
- order: z4.enum(["asc", "desc"]).optional().describe("Sort order"),
545
- limit: z4.number().max(200).optional().describe("Max rows (default 50, max 200)"),
546
- offset: z4.number().optional().describe("Offset for pagination"),
547
- fields: z4.string().optional().describe('Comma-separated column list to return (e.g. "sender,amount")'),
548
- count: z4.boolean().optional().describe("If true, return row count instead of rows")
549
- }, async ({ name, table, filters, sort, order, limit, offset, fields, count }) => {
728
+ name: z3.string().describe("Subgraph name"),
729
+ table: z3.string().describe("Table name"),
730
+ filters: z3.record(z3.string(), z3.string()).optional().describe('Column filters — plain values or with operators (e.g. {"amount.gte": "1000", "sender": "SP..."})'),
731
+ sort: z3.string().optional().describe("Column to sort by"),
732
+ order: z3.enum(["asc", "desc"]).optional().describe("Sort order"),
733
+ limit: z3.number().max(200).optional().describe("Max rows (default 50, max 200)"),
734
+ offset: z3.number().optional().describe("Offset for pagination"),
735
+ fields: z3.string().optional().describe('Comma-separated column list to return (e.g. "sender,amount")'),
736
+ count: z3.boolean().optional().describe("If true, return row count instead of rows")
737
+ }, async ({
738
+ name,
739
+ table,
740
+ filters,
741
+ sort,
742
+ order,
743
+ limit,
744
+ offset,
745
+ fields,
746
+ count
747
+ }) => {
550
748
  if (count) {
551
749
  const result2 = await getClient().subgraphs.queryTableCount(name, table, { filters, sort, order });
552
- return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
750
+ return {
751
+ content: [{ type: "text", text: JSON.stringify(result2, null, 2) }]
752
+ };
553
753
  }
554
754
  const rows = await getClient().subgraphs.queryTable(name, table, {
555
755
  filters,
@@ -561,23 +761,30 @@ function registerSubgraphTools(server) {
561
761
  });
562
762
  const cap = limit ?? 50;
563
763
  const result = withCap(rows, cap > 200 ? 200 : cap);
564
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
764
+ return {
765
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
766
+ };
565
767
  });
566
768
  defineTool(server, "subgraphs_reindex", "Reindex a subgraph from a specific block range.", {
567
- name: z4.string().describe("Subgraph name"),
568
- fromBlock: z4.number().optional().describe("Start block (defaults to beginning)"),
569
- toBlock: z4.number().optional().describe("End block (defaults to latest)")
769
+ name: z3.string().describe("Subgraph name"),
770
+ fromBlock: z3.number().optional().describe("Start block (defaults to beginning)"),
771
+ toBlock: z3.number().optional().describe("End block (defaults to latest)")
570
772
  }, async ({ name, fromBlock, toBlock }) => {
571
- const result = await getClient().subgraphs.reindex(name, { fromBlock, toBlock });
572
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
773
+ const result = await getClient().subgraphs.reindex(name, {
774
+ fromBlock,
775
+ toBlock
776
+ });
777
+ return {
778
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
779
+ };
573
780
  });
574
- defineTool(server, "subgraphs_delete", "Delete a subgraph permanently.", { name: z4.string().describe("Subgraph name") }, async ({ name }) => {
781
+ defineTool(server, "subgraphs_delete", "Delete a subgraph permanently.", { name: z3.string().describe("Subgraph name") }, async ({ name }) => {
575
782
  const result = await getClient().subgraphs.delete(name);
576
783
  return { content: [{ type: "text", text: result.message }] };
577
784
  });
578
785
  defineTool(server, "subgraphs_deploy", "Deploy a subgraph from TypeScript code. Pass the full defineSubgraph() source — it will be bundled, validated, and deployed.", {
579
- code: z4.string().describe("TypeScript source code containing a defineSubgraph() call"),
580
- reindex: z4.boolean().optional().describe("Force reindex on breaking schema change (drops and rebuilds all data)")
786
+ code: z3.string().describe("TypeScript source code containing a defineSubgraph() call"),
787
+ reindex: z3.boolean().optional().describe("Force reindex on breaking schema change (drops and rebuilds all data)")
581
788
  }, async ({ code, reindex }) => {
582
789
  const bundled = await bundleSubgraphCode(code);
583
790
  const result = await getClient().subgraphs.deploy({
@@ -595,54 +802,61 @@ function registerSubgraphTools(server) {
595
802
  });
596
803
  }
597
804
 
598
- // src/tools/account.ts
599
- function registerAccountTools(server) {
600
- defineTool(server, "account_whoami", "Show the authenticated account's email and plan.", {}, async () => {
601
- const result = await apiRequest("GET", "/api/accounts/me");
602
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
805
+ // src/tools/templates.ts
806
+ import {
807
+ getTemplateById,
808
+ getTemplatesByCategory,
809
+ templates as templates2
810
+ } from "@secondlayer/subgraphs/templates";
811
+ import { z as z4 } from "zod/v4";
812
+ function registerTemplateTools(server) {
813
+ defineTool(server, "templates_list", "List available subgraph templates. Returns metadata only — use templates_get for full code.", {
814
+ category: z4.enum(["defi", "nft", "token", "infrastructure"]).optional().describe("Filter by category")
815
+ }, async ({ category }) => {
816
+ const list = category ? getTemplatesByCategory(category) : templates2;
817
+ return {
818
+ content: [
819
+ {
820
+ type: "text",
821
+ text: JSON.stringify(list.map((t) => ({
822
+ id: t.id,
823
+ name: t.name,
824
+ description: t.description,
825
+ category: t.category
826
+ })), null, 2)
827
+ }
828
+ ]
829
+ };
830
+ });
831
+ defineTool(server, "templates_get", "Get a template's full code and prompt by ID.", { id: z4.string().describe("Template ID (e.g. 'dex-swaps')") }, async ({ id }) => {
832
+ const template = getTemplateById(id);
833
+ if (!template) {
834
+ return {
835
+ content: [
836
+ {
837
+ type: "text",
838
+ text: `Template "${id}" not found. Use templates_list to see available templates.`
839
+ }
840
+ ],
841
+ isError: true
842
+ };
843
+ }
844
+ return {
845
+ content: [
846
+ {
847
+ type: "text",
848
+ text: JSON.stringify({
849
+ id: template.id,
850
+ name: template.name,
851
+ description: template.description,
852
+ category: template.category,
853
+ code: template.code,
854
+ prompt: template.prompt
855
+ }, null, 2)
856
+ }
857
+ ]
858
+ };
603
859
  });
604
- }
605
-
606
- // src/resources.ts
607
- import { templates as templates2 } from "@secondlayer/subgraphs/templates";
608
- var FILTERS_REFERENCE = FilterSchema.options.map((opt) => ({
609
- type: opt.shape.type.def.values[0],
610
- fields: Object.keys(opt.shape).filter((k) => k !== "type")
611
- }));
612
- var COLUMN_TYPES = [
613
- { type: "uint", sqlType: "bigint", description: "Unsigned integer (Clarity uint)" },
614
- { type: "int", sqlType: "bigint", description: "Signed integer (Clarity int)" },
615
- { type: "text", sqlType: "text", description: "UTF-8 string" },
616
- { type: "principal", sqlType: "text", description: "Stacks address (standard or contract)" },
617
- { type: "bool", sqlType: "boolean", description: "Boolean value" },
618
- { type: "json", sqlType: "jsonb", description: "Arbitrary JSON data" },
619
- {
620
- options: ["nullable", "indexed", "search"],
621
- description: "Column options: nullable allows NULL, indexed creates a B-tree index, search enables full-text search"
622
- }
623
- ];
624
- function registerResources(server) {
625
- server.resource("filters", "secondlayer://filters", { description: "Stream filter types and their available fields" }, async () => ({
626
- contents: [{
627
- uri: "secondlayer://filters",
628
- mimeType: "application/json",
629
- text: JSON.stringify(FILTERS_REFERENCE, null, 2)
630
- }]
631
- }));
632
- server.resource("column-types", "secondlayer://column-types", { description: "Subgraph column types, SQL mappings, and options" }, async () => ({
633
- contents: [{
634
- uri: "secondlayer://column-types",
635
- mimeType: "application/json",
636
- text: JSON.stringify(COLUMN_TYPES, null, 2)
637
- }]
638
- }));
639
- server.resource("templates", "secondlayer://templates", { description: "Available subgraph templates with descriptions and categories" }, async () => ({
640
- contents: [{
641
- uri: "secondlayer://templates",
642
- mimeType: "application/json",
643
- text: JSON.stringify(templates2.map(({ id, name, description, category }) => ({ id, name, description, category })), null, 2)
644
- }]
645
- }));
646
860
  }
647
861
 
648
862
  // src/server.ts
@@ -665,5 +879,5 @@ export {
665
879
  createServer
666
880
  };
667
881
 
668
- //# debugId=29052EA0601609F164756E2164756E21
882
+ //# debugId=7316E5E5188B313F64756E2164756E21
669
883
  //# sourceMappingURL=index.js.map