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