@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/dist/index.js CHANGED
@@ -5,17 +5,30 @@ import { fileURLToPath } from "node:url";
5
5
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
6
 
7
7
  // src/resources.ts
8
- import { templates as subgraphTemplates } from "@secondlayer/subgraphs/templates";
9
- import { templates as workflowTemplates } from "@secondlayer/workflows/templates";
10
8
  var FILTERS_REFERENCE = [
11
- { type: "stx_transfer", fields: ["sender", "recipient", "minAmount", "maxAmount"] },
9
+ {
10
+ type: "stx_transfer",
11
+ fields: ["sender", "recipient", "minAmount", "maxAmount"]
12
+ },
12
13
  { type: "stx_mint", fields: ["recipient", "minAmount"] },
13
14
  { type: "stx_burn", fields: ["sender", "minAmount"] },
14
15
  { type: "stx_lock", fields: ["lockedAddress", "minAmount"] },
15
- { type: "ft_transfer", fields: ["sender", "recipient", "assetIdentifier", "minAmount", "maxAmount"] },
16
+ {
17
+ type: "ft_transfer",
18
+ fields: [
19
+ "sender",
20
+ "recipient",
21
+ "assetIdentifier",
22
+ "minAmount",
23
+ "maxAmount"
24
+ ]
25
+ },
16
26
  { type: "ft_mint", fields: ["recipient", "assetIdentifier", "minAmount"] },
17
27
  { type: "ft_burn", fields: ["sender", "assetIdentifier", "minAmount"] },
18
- { type: "nft_transfer", fields: ["sender", "recipient", "assetIdentifier", "tokenId"] },
28
+ {
29
+ type: "nft_transfer",
30
+ fields: ["sender", "recipient", "assetIdentifier", "tokenId"]
31
+ },
19
32
  { type: "nft_mint", fields: ["recipient", "assetIdentifier", "tokenId"] },
20
33
  { type: "nft_burn", fields: ["sender", "assetIdentifier", "tokenId"] },
21
34
  { type: "contract_call", fields: ["contract", "function"] },
@@ -65,33 +78,6 @@ function registerResources(server) {
65
78
  }
66
79
  ]
67
80
  }));
68
- server.resource("templates", "secondlayer://templates", {
69
- description: "Available subgraph and workflow templates with descriptions and categories"
70
- }, async () => ({
71
- contents: [
72
- {
73
- uri: "secondlayer://templates",
74
- mimeType: "application/json",
75
- text: JSON.stringify([
76
- ...subgraphTemplates.map(({ id, name, description, category }) => ({
77
- kind: "subgraph",
78
- id,
79
- name,
80
- description,
81
- category
82
- })),
83
- ...workflowTemplates.map(({ id, name, description, category, trigger }) => ({
84
- kind: "workflow",
85
- id,
86
- name,
87
- description,
88
- category,
89
- trigger
90
- }))
91
- ], null, 2)
92
- }
93
- ]
94
- }));
95
81
  }
96
82
 
97
83
  // src/lib/client.ts
@@ -343,391 +329,6 @@ function registerSubgraphTools(server) {
343
329
  });
344
330
  }
345
331
 
346
- // src/tools/workflows.ts
347
- import { bundleWorkflowCode } from "@secondlayer/bundler";
348
- import {
349
- generateWorkflowCode
350
- } from "@secondlayer/scaffold";
351
- import { VersionConflictError } from "@secondlayer/sdk";
352
- import {
353
- getTemplateById as getWorkflowTemplateById,
354
- templates as workflowTemplates2
355
- } from "@secondlayer/workflows/templates";
356
- import { createPatch } from "diff";
357
- import { z as z3 } from "zod/v4";
358
- function registerWorkflowTools(server) {
359
- defineTool(server, "workflows_list", "List all workflows. Returns summary fields only.", {}, async () => {
360
- const { workflows } = await getClient().workflows.list();
361
- return {
362
- content: [
363
- {
364
- type: "text",
365
- text: JSON.stringify(workflows, null, 2)
366
- }
367
- ]
368
- };
369
- });
370
- defineTool(server, "workflows_get", "Get full details of a workflow by name.", { name: z3.string().describe("Workflow name") }, async ({ name }) => {
371
- const detail = await getClient().workflows.get(name);
372
- return {
373
- content: [{ type: "text", text: JSON.stringify(detail, null, 2) }]
374
- };
375
- });
376
- 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 }) => {
377
- const source = await getClient().workflows.getSource(name);
378
- return {
379
- content: [{ type: "text", text: JSON.stringify(source, null, 2) }]
380
- };
381
- });
382
- 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.", {
383
- name: z3.string().describe("Workflow name"),
384
- proposedCode: z3.string().describe("New TypeScript source — must compile and validate."),
385
- expectedVersion: z3.string().regex(/^\d+\.\d+\.\d+$/).optional().describe("Version the proposer is editing from (for audit).")
386
- }, async ({ name, proposedCode, expectedVersion }) => {
387
- const current = await getClient().workflows.getSource(name);
388
- if (current.sourceCode === null) {
389
- return {
390
- isError: true,
391
- content: [
392
- {
393
- type: "text",
394
- text: JSON.stringify({
395
- error: "Workflow has no stored source. Redeploy via CLI first.",
396
- readOnly: true,
397
- version: current.version
398
- }, null, 2)
399
- }
400
- ]
401
- };
402
- }
403
- let bundleValid = false;
404
- let validation;
405
- let bundleSize = 0;
406
- try {
407
- const bundled = await bundleWorkflowCode(proposedCode);
408
- bundleValid = true;
409
- bundleSize = Buffer.byteLength(bundled.handlerCode, "utf8");
410
- validation = {
411
- name: bundled.name,
412
- triggerType: bundled.trigger.type
413
- };
414
- } catch (err) {
415
- validation = {
416
- error: err instanceof Error ? err.message : String(err)
417
- };
418
- }
419
- const diffText = createPatch(`${name}.ts`, current.sourceCode, proposedCode, `v${current.version}`, "proposed");
420
- return {
421
- content: [
422
- {
423
- type: "text",
424
- text: JSON.stringify({
425
- currentVersion: current.version,
426
- expectedVersion,
427
- currentSource: current.sourceCode,
428
- proposedSource: proposedCode,
429
- diffText,
430
- bundleValid,
431
- validation,
432
- bundleSize
433
- }, null, 2)
434
- }
435
- ]
436
- };
437
- });
438
- 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.", {
439
- name: z3.string().describe("Workflow name"),
440
- runId: z3.string().describe("Run id"),
441
- limit: z3.number().int().positive().max(200).optional().describe("Max step events to collect (default 50)"),
442
- timeoutMs: z3.number().int().positive().max(5 * 60 * 1000).optional().describe("Hard timeout in ms (default 60000, max 300000)")
443
- }, async ({ name, runId, limit, timeoutMs }) => {
444
- const cap = limit ?? 50;
445
- const deadline = timeoutMs ?? 60000;
446
- const events = [];
447
- let finalStatus = null;
448
- let stoppedBy = "timeout";
449
- const controller = new AbortController;
450
- const timer = setTimeout(() => {
451
- stoppedBy = "timeout";
452
- controller.abort();
453
- }, deadline);
454
- try {
455
- await getClient().workflows.streamRun(name, runId, (event) => {
456
- if (event.type === "step") {
457
- events.push(event.step);
458
- if (events.length >= cap) {
459
- stoppedBy = "limit";
460
- controller.abort();
461
- }
462
- } else if (event.type === "done") {
463
- finalStatus = event.done.status;
464
- stoppedBy = "done";
465
- }
466
- }, controller.signal);
467
- } catch (err) {
468
- if (!(err instanceof Error) || err.name !== "AbortError") {
469
- throw err;
470
- }
471
- } finally {
472
- clearTimeout(timer);
473
- }
474
- return {
475
- content: [
476
- {
477
- type: "text",
478
- text: JSON.stringify({
479
- runId,
480
- finalStatus,
481
- stoppedBy,
482
- eventCount: events.length,
483
- events
484
- }, null, 2)
485
- }
486
- ]
487
- };
488
- });
489
- 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 () => {
490
- const result = await getClient().workflows.pauseAll();
491
- return {
492
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
493
- };
494
- });
495
- 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 }) => {
496
- const result = await getClient().workflows.cancelRun(runId);
497
- return {
498
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
499
- };
500
- });
501
- 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.", {
502
- name: z3.string().describe("Workflow name"),
503
- toVersion: z3.string().regex(/^\d+\.\d+\.\d+$/).optional().describe("Target version to restore. Must be one of the retained bundles on disk.")
504
- }, async ({ name, toVersion }) => {
505
- const result = await getClient().workflows.rollback(name, toVersion);
506
- return {
507
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
508
- };
509
- });
510
- defineTool(server, "workflows_delete", "Delete a workflow permanently.", { name: z3.string().describe("Workflow name") }, async ({ name }) => {
511
- await getClient().workflows.delete(name);
512
- return {
513
- content: [{ type: "text", text: `Deleted workflow "${name}"` }]
514
- };
515
- });
516
- defineTool(server, "workflows_trigger", "Trigger a workflow run. Optionally pass input as a JSON string.", {
517
- name: z3.string().describe("Workflow name"),
518
- input: z3.string().optional().describe("Input as JSON string")
519
- }, async ({ name, input }) => {
520
- const parsed = input ? JSON.parse(input) : undefined;
521
- const result = await getClient().workflows.trigger(name, parsed);
522
- return {
523
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
524
- };
525
- });
526
- defineTool(server, "workflows_pause", "Pause a running workflow.", { name: z3.string().describe("Workflow name") }, async ({ name }) => {
527
- await getClient().workflows.pause(name);
528
- return {
529
- content: [{ type: "text", text: `Paused workflow "${name}"` }]
530
- };
531
- });
532
- defineTool(server, "workflows_resume", "Resume a paused workflow.", { name: z3.string().describe("Workflow name") }, async ({ name }) => {
533
- await getClient().workflows.resume(name);
534
- return {
535
- content: [{ type: "text", text: `Resumed workflow "${name}"` }]
536
- };
537
- });
538
- defineTool(server, "workflows_template_list", "List available workflow templates. Returns metadata only — use workflows_template_get for the full source.", {}, async () => {
539
- return {
540
- content: [
541
- {
542
- type: "text",
543
- text: JSON.stringify(workflowTemplates2.map((t) => ({
544
- id: t.id,
545
- name: t.name,
546
- description: t.description,
547
- category: t.category,
548
- trigger: t.trigger
549
- })), null, 2)
550
- }
551
- ]
552
- };
553
- });
554
- 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 }) => {
555
- const template = getWorkflowTemplateById(id);
556
- if (!template) {
557
- return {
558
- isError: true,
559
- content: [
560
- {
561
- type: "text",
562
- text: `Template "${id}" not found. Use workflows_template_list to see available templates.`
563
- }
564
- ]
565
- };
566
- }
567
- return {
568
- content: [
569
- {
570
- type: "text",
571
- text: JSON.stringify(template, null, 2)
572
- }
573
- ]
574
- };
575
- });
576
- 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.", {
577
- name: z3.string().regex(/^[a-z][a-z0-9-]*$/).describe("Workflow name (lowercase, hyphens)"),
578
- trigger: z3.discriminatedUnion("type", [
579
- z3.object({
580
- type: z3.literal("event"),
581
- filterType: z3.string().optional()
582
- }),
583
- z3.object({
584
- type: z3.literal("schedule"),
585
- cron: z3.string().min(1),
586
- timezone: z3.string().optional()
587
- }),
588
- z3.object({ type: z3.literal("manual") })
589
- ]).describe("Trigger shape"),
590
- steps: z3.array(z3.enum(["run", "query", "ai", "deliver"])).describe("Ordered list of step kinds to include in the handler"),
591
- deliveryTarget: z3.enum(["webhook", "slack", "email", "discord", "telegram"]).optional().describe("Delivery target used when steps includes `deliver`")
592
- }, async ({ name, trigger, steps, deliveryTarget }) => {
593
- const code = generateWorkflowCode({
594
- name,
595
- trigger,
596
- steps,
597
- deliveryTarget
598
- });
599
- return { content: [{ type: "text", text: code }] };
600
- });
601
- 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.", {
602
- code: z3.string().describe("TypeScript source code containing a defineWorkflow() call"),
603
- expectedVersion: z3.string().regex(/^\d+\.\d+\.\d+$/).optional().describe("Stored version the client expects (major.minor.patch). Server returns 409 on mismatch."),
604
- dryRun: z3.boolean().optional().describe("If true, validate and bundle only — do not persist.")
605
- }, async ({ code, expectedVersion, dryRun }) => {
606
- let bundled;
607
- try {
608
- bundled = await bundleWorkflowCode(code);
609
- } catch (err) {
610
- return {
611
- isError: true,
612
- content: [
613
- {
614
- type: "text",
615
- text: err instanceof Error ? err.message : String(err)
616
- }
617
- ]
618
- };
619
- }
620
- const base = {
621
- name: bundled.name,
622
- trigger: bundled.trigger,
623
- handlerCode: bundled.handlerCode,
624
- sourceCode: bundled.sourceCode,
625
- retries: bundled.retries,
626
- timeout: bundled.timeout,
627
- expectedVersion
628
- };
629
- try {
630
- const result = dryRun ? await getClient().workflows.deploy({ ...base, dryRun: true }) : await getClient().workflows.deploy(base);
631
- return {
632
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
633
- };
634
- } catch (err) {
635
- if (err instanceof VersionConflictError) {
636
- return {
637
- isError: true,
638
- content: [
639
- {
640
- type: "text",
641
- text: JSON.stringify({
642
- error: err.message,
643
- code: "VERSION_CONFLICT",
644
- currentVersion: err.currentVersion,
645
- expectedVersion: err.expectedVersion
646
- }, null, 2)
647
- }
648
- ]
649
- };
650
- }
651
- throw err;
652
- }
653
- });
654
- defineTool(server, "workflows_runs", "List runs for a workflow. Optionally filter by status and limit results.", {
655
- name: z3.string().describe("Workflow name"),
656
- status: z3.enum(["running", "completed", "failed", "cancelled"]).optional().describe("Filter by run status"),
657
- limit: z3.number().optional().describe("Max runs to return (default 20)")
658
- }, async ({ name, status, limit }) => {
659
- const { runs } = await getClient().workflows.listRuns(name, {
660
- status,
661
- limit
662
- });
663
- return {
664
- content: [
665
- {
666
- type: "text",
667
- text: JSON.stringify(runs, null, 2)
668
- }
669
- ]
670
- };
671
- });
672
- }
673
-
674
- // src/tools/templates.ts
675
- import {
676
- getTemplateById,
677
- getTemplatesByCategory,
678
- templates
679
- } from "@secondlayer/subgraphs/templates";
680
- import { z as z4 } from "zod/v4";
681
- function registerTemplateTools(server) {
682
- defineTool(server, "templates_list", "List available subgraph templates. Returns metadata only — use templates_get for full code.", {
683
- category: z4.enum(["defi", "nft", "token", "infrastructure"]).optional().describe("Filter by category")
684
- }, async ({ category }) => {
685
- const list = category ? getTemplatesByCategory(category) : templates;
686
- return {
687
- content: [
688
- {
689
- type: "text",
690
- text: JSON.stringify(list.map((t) => ({
691
- id: t.id,
692
- name: t.name,
693
- description: t.description,
694
- category: t.category
695
- })), null, 2)
696
- }
697
- ]
698
- };
699
- });
700
- 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 }) => {
701
- const template = getTemplateById(id);
702
- if (!template) {
703
- return {
704
- content: [
705
- {
706
- type: "text",
707
- text: `Template "${id}" not found. Use templates_list to see available templates.`
708
- }
709
- ],
710
- isError: true
711
- };
712
- }
713
- return {
714
- content: [
715
- {
716
- type: "text",
717
- text: JSON.stringify({
718
- id: template.id,
719
- name: template.name,
720
- description: template.description,
721
- category: template.category,
722
- code: template.code,
723
- prompt: template.prompt
724
- }, null, 2)
725
- }
726
- ]
727
- };
728
- });
729
- }
730
-
731
332
  // src/server.ts
732
333
  var __dirname2 = dirname(fileURLToPath(import.meta.url));
733
334
  var pkg = JSON.parse(readFileSync(join(__dirname2, "../package.json"), "utf-8"));
@@ -736,11 +337,9 @@ function createServer() {
736
337
  name: "secondlayer",
737
338
  version: pkg.version
738
339
  });
739
- registerTemplateTools(server);
740
340
  registerScaffoldTools(server);
741
341
  registerSubgraphTools(server);
742
342
  registerAccountTools(server);
743
- registerWorkflowTools(server);
744
343
  registerResources(server);
745
344
  return server;
746
345
  }
@@ -748,5 +347,5 @@ export {
748
347
  createServer
749
348
  };
750
349
 
751
- //# debugId=6CB2325BA11B0F2F64756E2164756E21
350
+ //# debugId=8A098600E383462464756E2164756E21
752
351
  //# sourceMappingURL=index.js.map