@secondlayer/mcp 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,14 +45,13 @@ npx @secondlayer/mcp-http
45
45
 
46
46
  ## Tools
47
47
 
48
- Tools across 5 domains.
48
+ Tools across 4 domains.
49
49
 
50
50
  | Domain | Tools |
51
51
  | --- | --- |
52
52
  | **Subgraphs** (6) | `subgraphs_list`, `subgraphs_get`, `subgraphs_query`, `subgraphs_reindex`, `subgraphs_delete`, `subgraphs_deploy` |
53
53
  | **Workflows** (6) | `workflows_list`, `workflows_get`, `workflows_trigger`, `workflows_pause`, `workflows_resume`, `workflows_runs` |
54
54
  | **Scaffold** (2) | `scaffold_from_contract`, `scaffold_from_abi` |
55
- | **Templates** (2) | `templates_list`, `templates_get` |
56
55
  | **Account** (1) | `account_whoami` |
57
56
 
58
57
  ### `subgraphs_query` enhancements
@@ -64,13 +63,12 @@ Tools across 5 domains.
64
63
 
65
64
  ## Resources
66
65
 
67
- 3 MCP resources for agent context:
66
+ 2 MCP resources for agent context:
68
67
 
69
68
  | URI | Description |
70
69
  | --- | --- |
71
70
  | `secondlayer://filters` | Filter types reference |
72
71
  | `secondlayer://column-types` | Column type mappings and options |
73
- | `secondlayer://templates` | Available subgraph templates |
74
72
 
75
73
  ## Error Handling
76
74
 
package/dist/bin-http.js CHANGED
@@ -13,17 +13,30 @@ import { fileURLToPath } from "node:url";
13
13
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
14
14
 
15
15
  // src/resources.ts
16
- import { templates as subgraphTemplates } from "@secondlayer/subgraphs/templates";
17
- import { templates as workflowTemplates } from "@secondlayer/workflows/templates";
18
16
  var FILTERS_REFERENCE = [
19
- { type: "stx_transfer", fields: ["sender", "recipient", "minAmount", "maxAmount"] },
17
+ {
18
+ type: "stx_transfer",
19
+ fields: ["sender", "recipient", "minAmount", "maxAmount"]
20
+ },
20
21
  { type: "stx_mint", fields: ["recipient", "minAmount"] },
21
22
  { type: "stx_burn", fields: ["sender", "minAmount"] },
22
23
  { type: "stx_lock", fields: ["lockedAddress", "minAmount"] },
23
- { type: "ft_transfer", fields: ["sender", "recipient", "assetIdentifier", "minAmount", "maxAmount"] },
24
+ {
25
+ type: "ft_transfer",
26
+ fields: [
27
+ "sender",
28
+ "recipient",
29
+ "assetIdentifier",
30
+ "minAmount",
31
+ "maxAmount"
32
+ ]
33
+ },
24
34
  { type: "ft_mint", fields: ["recipient", "assetIdentifier", "minAmount"] },
25
35
  { type: "ft_burn", fields: ["sender", "assetIdentifier", "minAmount"] },
26
- { type: "nft_transfer", fields: ["sender", "recipient", "assetIdentifier", "tokenId"] },
36
+ {
37
+ type: "nft_transfer",
38
+ fields: ["sender", "recipient", "assetIdentifier", "tokenId"]
39
+ },
27
40
  { type: "nft_mint", fields: ["recipient", "assetIdentifier", "tokenId"] },
28
41
  { type: "nft_burn", fields: ["sender", "assetIdentifier", "tokenId"] },
29
42
  { type: "contract_call", fields: ["contract", "function"] },
@@ -73,33 +86,6 @@ function registerResources(server) {
73
86
  }
74
87
  ]
75
88
  }));
76
- server.resource("templates", "secondlayer://templates", {
77
- description: "Available subgraph and workflow templates with descriptions and categories"
78
- }, async () => ({
79
- contents: [
80
- {
81
- uri: "secondlayer://templates",
82
- mimeType: "application/json",
83
- text: JSON.stringify([
84
- ...subgraphTemplates.map(({ id, name, description, category }) => ({
85
- kind: "subgraph",
86
- id,
87
- name,
88
- description,
89
- category
90
- })),
91
- ...workflowTemplates.map(({ id, name, description, category, trigger }) => ({
92
- kind: "workflow",
93
- id,
94
- name,
95
- description,
96
- category,
97
- trigger
98
- }))
99
- ], null, 2)
100
- }
101
- ]
102
- }));
103
89
  }
104
90
 
105
91
  // src/lib/client.ts
@@ -351,391 +337,6 @@ function registerSubgraphTools(server) {
351
337
  });
352
338
  }
353
339
 
354
- // src/tools/workflows.ts
355
- import { bundleWorkflowCode } from "@secondlayer/bundler";
356
- import {
357
- generateWorkflowCode
358
- } from "@secondlayer/scaffold";
359
- import { VersionConflictError } from "@secondlayer/sdk";
360
- import {
361
- getTemplateById as getWorkflowTemplateById,
362
- templates as workflowTemplates2
363
- } from "@secondlayer/workflows/templates";
364
- import { createPatch } from "diff";
365
- import { z as z3 } from "zod/v4";
366
- function registerWorkflowTools(server) {
367
- defineTool(server, "workflows_list", "List all workflows. Returns summary fields only.", {}, async () => {
368
- const { workflows } = await getClient().workflows.list();
369
- return {
370
- content: [
371
- {
372
- type: "text",
373
- text: JSON.stringify(workflows, null, 2)
374
- }
375
- ]
376
- };
377
- });
378
- defineTool(server, "workflows_get", "Get full details of a workflow by name.", { name: z3.string().describe("Workflow name") }, async ({ name }) => {
379
- const detail = await getClient().workflows.get(name);
380
- return {
381
- content: [{ type: "text", text: JSON.stringify(detail, null, 2) }]
382
- };
383
- });
384
- defineTool(server, "workflows_get_definition", "Return the deployed TypeScript source of a workflow plus its stored version. Returns `sourceCode: null` + `readOnly: true` for workflows deployed before source capture.", { name: z3.string().describe("Workflow name") }, async ({ name }) => {
385
- const source = await getClient().workflows.getSource(name);
386
- return {
387
- content: [{ type: "text", text: JSON.stringify(source, null, 2) }]
388
- };
389
- });
390
- defineTool(server, "workflows_propose_edit", "Validate a proposed edit WITHOUT deploying. Fetches the current stored source, bundles the proposed source, computes a unified diff, and returns everything for review. Use this when you want to show the user a diff before committing — pair it with workflows_deploy(expectedVersion=...) to persist.", {
391
- name: z3.string().describe("Workflow name"),
392
- proposedCode: z3.string().describe("New TypeScript source — must compile and validate."),
393
- expectedVersion: z3.string().regex(/^\d+\.\d+\.\d+$/).optional().describe("Version the proposer is editing from (for audit).")
394
- }, async ({ name, proposedCode, expectedVersion }) => {
395
- const current = await getClient().workflows.getSource(name);
396
- if (current.sourceCode === null) {
397
- return {
398
- isError: true,
399
- content: [
400
- {
401
- type: "text",
402
- text: JSON.stringify({
403
- error: "Workflow has no stored source. Redeploy via CLI first.",
404
- readOnly: true,
405
- version: current.version
406
- }, null, 2)
407
- }
408
- ]
409
- };
410
- }
411
- let bundleValid = false;
412
- let validation;
413
- let bundleSize = 0;
414
- try {
415
- const bundled = await bundleWorkflowCode(proposedCode);
416
- bundleValid = true;
417
- bundleSize = Buffer.byteLength(bundled.handlerCode, "utf8");
418
- validation = {
419
- name: bundled.name,
420
- triggerType: bundled.trigger.type
421
- };
422
- } catch (err) {
423
- validation = {
424
- error: err instanceof Error ? err.message : String(err)
425
- };
426
- }
427
- const diffText = createPatch(`${name}.ts`, current.sourceCode, proposedCode, `v${current.version}`, "proposed");
428
- return {
429
- content: [
430
- {
431
- type: "text",
432
- text: JSON.stringify({
433
- currentVersion: current.version,
434
- expectedVersion,
435
- currentSource: current.sourceCode,
436
- proposedSource: proposedCode,
437
- diffText,
438
- bundleValid,
439
- validation,
440
- bundleSize
441
- }, null, 2)
442
- }
443
- ]
444
- };
445
- });
446
- defineTool(server, "workflows_tail_run", "Tail a workflow run via SSE and return a compacted log. Resolves as soon as the run completes, `limit` events are collected, or `timeoutMs` elapses (default 60s). MCP is not streaming-first — use this for short-lived follow-ups, not long tails.", {
447
- name: z3.string().describe("Workflow name"),
448
- runId: z3.string().describe("Run id"),
449
- limit: z3.number().int().positive().max(200).optional().describe("Max step events to collect (default 50)"),
450
- timeoutMs: z3.number().int().positive().max(5 * 60 * 1000).optional().describe("Hard timeout in ms (default 60000, max 300000)")
451
- }, async ({ name, runId, limit, timeoutMs }) => {
452
- const cap = limit ?? 50;
453
- const deadline = timeoutMs ?? 60000;
454
- const events = [];
455
- let finalStatus = null;
456
- let stoppedBy = "timeout";
457
- const controller = new AbortController;
458
- const timer = setTimeout(() => {
459
- stoppedBy = "timeout";
460
- controller.abort();
461
- }, deadline);
462
- try {
463
- await getClient().workflows.streamRun(name, runId, (event) => {
464
- if (event.type === "step") {
465
- events.push(event.step);
466
- if (events.length >= cap) {
467
- stoppedBy = "limit";
468
- controller.abort();
469
- }
470
- } else if (event.type === "done") {
471
- finalStatus = event.done.status;
472
- stoppedBy = "done";
473
- }
474
- }, controller.signal);
475
- } catch (err) {
476
- if (!(err instanceof Error) || err.name !== "AbortError") {
477
- throw err;
478
- }
479
- } finally {
480
- clearTimeout(timer);
481
- }
482
- return {
483
- content: [
484
- {
485
- type: "text",
486
- text: JSON.stringify({
487
- runId,
488
- finalStatus,
489
- stoppedBy,
490
- eventCount: events.length,
491
- events
492
- }, null, 2)
493
- }
494
- ]
495
- };
496
- });
497
- defineTool(server, "workflows_pause_all", "Pause ALL active workflows for the authenticated account. Irreversible only by calling pause/resume per workflow. Returns the list of affected workflows.", {}, async () => {
498
- const result = await getClient().workflows.pauseAll();
499
- return {
500
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
501
- };
502
- });
503
- defineTool(server, "workflows_cancel_run", "Cancel an in-flight workflow run. Marks the run as cancelled and removes any pending queue entry. No-ops if the run is already terminal.", { runId: z3.string().describe("Run id to cancel") }, async ({ runId }) => {
504
- const result = await getClient().workflows.cancelRun(runId);
505
- return {
506
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
507
- };
508
- });
509
- defineTool(server, "workflows_rollback", "Roll a workflow back to a prior version. The restored handler is re-published as a NEW version (audit trail), so no history is lost. Pass toVersion to pick a specific bundle; omit to roll back to the immediate previous version on disk. Last 3 versions are retained.", {
510
- name: z3.string().describe("Workflow name"),
511
- toVersion: z3.string().regex(/^\d+\.\d+\.\d+$/).optional().describe("Target version to restore. Must be one of the retained bundles on disk.")
512
- }, async ({ name, toVersion }) => {
513
- const result = await getClient().workflows.rollback(name, toVersion);
514
- return {
515
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
516
- };
517
- });
518
- defineTool(server, "workflows_delete", "Delete a workflow permanently.", { name: z3.string().describe("Workflow name") }, async ({ name }) => {
519
- await getClient().workflows.delete(name);
520
- return {
521
- content: [{ type: "text", text: `Deleted workflow "${name}"` }]
522
- };
523
- });
524
- defineTool(server, "workflows_trigger", "Trigger a workflow run. Optionally pass input as a JSON string.", {
525
- name: z3.string().describe("Workflow name"),
526
- input: z3.string().optional().describe("Input as JSON string")
527
- }, async ({ name, input }) => {
528
- const parsed = input ? JSON.parse(input) : undefined;
529
- const result = await getClient().workflows.trigger(name, parsed);
530
- return {
531
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
532
- };
533
- });
534
- defineTool(server, "workflows_pause", "Pause a running workflow.", { name: z3.string().describe("Workflow name") }, async ({ name }) => {
535
- await getClient().workflows.pause(name);
536
- return {
537
- content: [{ type: "text", text: `Paused workflow "${name}"` }]
538
- };
539
- });
540
- defineTool(server, "workflows_resume", "Resume a paused workflow.", { name: z3.string().describe("Workflow name") }, async ({ name }) => {
541
- await getClient().workflows.resume(name);
542
- return {
543
- content: [{ type: "text", text: `Resumed workflow "${name}"` }]
544
- };
545
- });
546
- defineTool(server, "workflows_template_list", "List available workflow templates. Returns metadata only — use workflows_template_get for the full source.", {}, async () => {
547
- return {
548
- content: [
549
- {
550
- type: "text",
551
- text: JSON.stringify(workflowTemplates2.map((t) => ({
552
- id: t.id,
553
- name: t.name,
554
- description: t.description,
555
- category: t.category,
556
- trigger: t.trigger
557
- })), null, 2)
558
- }
559
- ]
560
- };
561
- });
562
- defineTool(server, "workflows_template_get", "Get a workflow template's full TypeScript source and prompt by id.", { id: z3.string().describe("Template id, e.g. 'whale-alert'") }, async ({ id }) => {
563
- const template = getWorkflowTemplateById(id);
564
- if (!template) {
565
- return {
566
- isError: true,
567
- content: [
568
- {
569
- type: "text",
570
- text: `Template "${id}" not found. Use workflows_template_list to see available templates.`
571
- }
572
- ]
573
- };
574
- }
575
- return {
576
- content: [
577
- {
578
- type: "text",
579
- text: JSON.stringify(template, null, 2)
580
- }
581
- ]
582
- };
583
- });
584
- defineTool(server, "workflows_scaffold", "Generate a compilable defineWorkflow() skeleton from a typed intent. Returns the TypeScript source; pass it to workflows_deploy to persist. Placeholders inside the source must be filled in before running a real workflow.", {
585
- name: z3.string().regex(/^[a-z][a-z0-9-]*$/).describe("Workflow name (lowercase, hyphens)"),
586
- trigger: z3.discriminatedUnion("type", [
587
- z3.object({
588
- type: z3.literal("event"),
589
- filterType: z3.string().optional()
590
- }),
591
- z3.object({
592
- type: z3.literal("schedule"),
593
- cron: z3.string().min(1),
594
- timezone: z3.string().optional()
595
- }),
596
- z3.object({ type: z3.literal("manual") })
597
- ]).describe("Trigger shape"),
598
- steps: z3.array(z3.enum(["run", "query", "ai", "deliver"])).describe("Ordered list of step kinds to include in the handler"),
599
- deliveryTarget: z3.enum(["webhook", "slack", "email", "discord", "telegram"]).optional().describe("Delivery target used when steps includes `deliver`")
600
- }, async ({ name, trigger, steps, deliveryTarget }) => {
601
- const code = generateWorkflowCode({
602
- name,
603
- trigger,
604
- steps,
605
- deliveryTarget
606
- });
607
- return { content: [{ type: "text", text: code }] };
608
- });
609
- defineTool(server, "workflows_deploy", "Deploy a workflow from TypeScript source. Pass the full defineWorkflow() source — it will be bundled, validated, and deployed. Use expectedVersion for optimistic concurrency, or dryRun to validate without persisting.", {
610
- code: z3.string().describe("TypeScript source code containing a defineWorkflow() call"),
611
- expectedVersion: z3.string().regex(/^\d+\.\d+\.\d+$/).optional().describe("Stored version the client expects (major.minor.patch). Server returns 409 on mismatch."),
612
- dryRun: z3.boolean().optional().describe("If true, validate and bundle only — do not persist.")
613
- }, async ({ code, expectedVersion, dryRun }) => {
614
- let bundled;
615
- try {
616
- bundled = await bundleWorkflowCode(code);
617
- } catch (err) {
618
- return {
619
- isError: true,
620
- content: [
621
- {
622
- type: "text",
623
- text: err instanceof Error ? err.message : String(err)
624
- }
625
- ]
626
- };
627
- }
628
- const base = {
629
- name: bundled.name,
630
- trigger: bundled.trigger,
631
- handlerCode: bundled.handlerCode,
632
- sourceCode: bundled.sourceCode,
633
- retries: bundled.retries,
634
- timeout: bundled.timeout,
635
- expectedVersion
636
- };
637
- try {
638
- const result = dryRun ? await getClient().workflows.deploy({ ...base, dryRun: true }) : await getClient().workflows.deploy(base);
639
- return {
640
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
641
- };
642
- } catch (err) {
643
- if (err instanceof VersionConflictError) {
644
- return {
645
- isError: true,
646
- content: [
647
- {
648
- type: "text",
649
- text: JSON.stringify({
650
- error: err.message,
651
- code: "VERSION_CONFLICT",
652
- currentVersion: err.currentVersion,
653
- expectedVersion: err.expectedVersion
654
- }, null, 2)
655
- }
656
- ]
657
- };
658
- }
659
- throw err;
660
- }
661
- });
662
- defineTool(server, "workflows_runs", "List runs for a workflow. Optionally filter by status and limit results.", {
663
- name: z3.string().describe("Workflow name"),
664
- status: z3.enum(["running", "completed", "failed", "cancelled"]).optional().describe("Filter by run status"),
665
- limit: z3.number().optional().describe("Max runs to return (default 20)")
666
- }, async ({ name, status, limit }) => {
667
- const { runs } = await getClient().workflows.listRuns(name, {
668
- status,
669
- limit
670
- });
671
- return {
672
- content: [
673
- {
674
- type: "text",
675
- text: JSON.stringify(runs, null, 2)
676
- }
677
- ]
678
- };
679
- });
680
- }
681
-
682
- // src/tools/templates.ts
683
- import {
684
- getTemplateById,
685
- getTemplatesByCategory,
686
- templates
687
- } from "@secondlayer/subgraphs/templates";
688
- import { z as z4 } from "zod/v4";
689
- function registerTemplateTools(server) {
690
- defineTool(server, "templates_list", "List available subgraph templates. Returns metadata only — use templates_get for full code.", {
691
- category: z4.enum(["defi", "nft", "token", "infrastructure"]).optional().describe("Filter by category")
692
- }, async ({ category }) => {
693
- const list = category ? getTemplatesByCategory(category) : templates;
694
- return {
695
- content: [
696
- {
697
- type: "text",
698
- text: JSON.stringify(list.map((t) => ({
699
- id: t.id,
700
- name: t.name,
701
- description: t.description,
702
- category: t.category
703
- })), null, 2)
704
- }
705
- ]
706
- };
707
- });
708
- 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 }) => {
709
- const template = getTemplateById(id);
710
- if (!template) {
711
- return {
712
- content: [
713
- {
714
- type: "text",
715
- text: `Template "${id}" not found. Use templates_list to see available templates.`
716
- }
717
- ],
718
- isError: true
719
- };
720
- }
721
- return {
722
- content: [
723
- {
724
- type: "text",
725
- text: JSON.stringify({
726
- id: template.id,
727
- name: template.name,
728
- description: template.description,
729
- category: template.category,
730
- code: template.code,
731
- prompt: template.prompt
732
- }, null, 2)
733
- }
734
- ]
735
- };
736
- });
737
- }
738
-
739
340
  // src/server.ts
740
341
  var __dirname2 = dirname(fileURLToPath(import.meta.url));
741
342
  var pkg = JSON.parse(readFileSync(join(__dirname2, "../package.json"), "utf-8"));
@@ -744,11 +345,9 @@ function createServer() {
744
345
  name: "secondlayer",
745
346
  version: pkg.version
746
347
  });
747
- registerTemplateTools(server);
748
348
  registerScaffoldTools(server);
749
349
  registerSubgraphTools(server);
750
350
  registerAccountTools(server);
751
- registerWorkflowTools(server);
752
351
  registerResources(server);
753
352
  return server;
754
353
  }
@@ -839,5 +438,5 @@ httpServer.listen(port, () => {
839
438
  console.error("Warning: SECONDLAYER_MCP_SECRET not set, authentication disabled");
840
439
  });
841
440
 
842
- //# debugId=1386FA48A488D24664756E2164756E21
441
+ //# debugId=DFBEBA63DCCFB17E64756E2164756E21
843
442
  //# sourceMappingURL=bin-http.js.map