@limeadelabs/launchpad-mcp 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/index.js +182 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,11 +54,21 @@ Or add manually to `~/.claude/claude_desktop_config.json`:
|
|
|
54
54
|
| `lp_list_pages` | List spec/doc pages for a project |
|
|
55
55
|
| `lp_get_page` | Get a page's full content |
|
|
56
56
|
| `lp_get_workflow` | Get valid workflow states and transitions |
|
|
57
|
+
| `lp_context_list` | List context entries with optional filters (project, type, search) |
|
|
58
|
+
| `lp_context_get` | Fetch a context entry's full content by slug or ID |
|
|
59
|
+
| `lp_context_package` | Get assembled context package for a task (org + project entries, specs, comments) |
|
|
60
|
+
| `lp_context_update` | Update a context entry's content (requires write access) |
|
|
57
61
|
|
|
58
62
|
## Resources
|
|
59
63
|
|
|
60
64
|
- `launchpad://project/{id}/context` — Project instructions, conventions, and page summaries
|
|
61
65
|
- `launchpad://task/{id}/spec` — Full generated task spec
|
|
66
|
+
- `context://entries` — Browse all active context entries
|
|
67
|
+
- `context://entries/{slug}` — Read a specific context entry
|
|
68
|
+
|
|
69
|
+
### Resources vs Tools
|
|
70
|
+
|
|
71
|
+
Resources are for **passive browsing** in the Claude Code resource panel. Tools are for **active agent invocation** during conversation. Both access the same underlying context registry API.
|
|
62
72
|
|
|
63
73
|
## Prompts
|
|
64
74
|
|
package/dist/index.js
CHANGED
|
@@ -99,6 +99,25 @@ var LaunchPadClient = class {
|
|
|
99
99
|
getWorkflow(projectId) {
|
|
100
100
|
return this.request("GET", `/projects/${projectId}/workflow`);
|
|
101
101
|
}
|
|
102
|
+
listContextEntries(params) {
|
|
103
|
+
const searchParams = new URLSearchParams();
|
|
104
|
+
if (params?.project_id !== void 0) searchParams.set("project_id", String(params.project_id));
|
|
105
|
+
if (params?.entry_type) searchParams.set("entry_type", params.entry_type);
|
|
106
|
+
if (params?.search) searchParams.set("search", params.search);
|
|
107
|
+
if (params?.limit !== void 0) searchParams.set("limit", String(params.limit));
|
|
108
|
+
if (params?.offset !== void 0) searchParams.set("offset", String(params.offset));
|
|
109
|
+
const query = searchParams.toString();
|
|
110
|
+
return this.request("GET", `/context_entries${query ? `?${query}` : ""}`);
|
|
111
|
+
}
|
|
112
|
+
getContextEntry(identifier) {
|
|
113
|
+
return this.request("GET", `/context_entries/${encodeURIComponent(identifier)}`);
|
|
114
|
+
}
|
|
115
|
+
getTaskContextPackage(taskId) {
|
|
116
|
+
return this.request("GET", `/tasks/${taskId}/context`);
|
|
117
|
+
}
|
|
118
|
+
updateContextEntry(identifier, data) {
|
|
119
|
+
return this.request("PATCH", `/context_entries/${encodeURIComponent(identifier)}`, data);
|
|
120
|
+
}
|
|
102
121
|
};
|
|
103
122
|
|
|
104
123
|
// src/tools/shared.ts
|
|
@@ -402,6 +421,96 @@ function registerPageTools(server2, client2) {
|
|
|
402
421
|
);
|
|
403
422
|
}
|
|
404
423
|
|
|
424
|
+
// src/tools/context.ts
|
|
425
|
+
import { z as z8 } from "zod";
|
|
426
|
+
function registerContextTools(server2, client2) {
|
|
427
|
+
server2.tool(
|
|
428
|
+
"lp_context_list",
|
|
429
|
+
"List available context entries with optional filters",
|
|
430
|
+
{
|
|
431
|
+
project_id: z8.number().optional().describe("Filter by project ID"),
|
|
432
|
+
entry_type: z8.string().optional().describe("Filter by entry type"),
|
|
433
|
+
search: z8.string().optional().describe("Text search on entry name"),
|
|
434
|
+
limit: z8.number().min(1).max(100).optional().describe("Max entries to return (default 50)"),
|
|
435
|
+
offset: z8.number().min(0).optional().describe("Offset for pagination")
|
|
436
|
+
},
|
|
437
|
+
async (params) => {
|
|
438
|
+
try {
|
|
439
|
+
const result = await client2.listContextEntries(params);
|
|
440
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
441
|
+
} catch (error) {
|
|
442
|
+
return handleError(error, client2.timeoutMs);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
);
|
|
446
|
+
server2.tool(
|
|
447
|
+
"lp_context_get",
|
|
448
|
+
"Fetch a specific context entry by slug or ID",
|
|
449
|
+
{
|
|
450
|
+
slug: z8.string().optional().describe("Entry slug"),
|
|
451
|
+
id: z8.number().optional().describe("Entry ID")
|
|
452
|
+
},
|
|
453
|
+
async (params) => {
|
|
454
|
+
const identifier = params.slug || (params.id !== void 0 ? String(params.id) : void 0);
|
|
455
|
+
if (!identifier) {
|
|
456
|
+
return {
|
|
457
|
+
content: [{ type: "text", text: JSON.stringify({ error: true, code: "validation_error", message: "Either slug or id is required" }) }],
|
|
458
|
+
isError: true
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
try {
|
|
462
|
+
const result = await client2.getContextEntry(identifier);
|
|
463
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
464
|
+
} catch (error) {
|
|
465
|
+
return handleError(error, client2.timeoutMs);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
);
|
|
469
|
+
server2.tool(
|
|
470
|
+
"lp_context_package",
|
|
471
|
+
"Get the assembled context package for a task \u2014 org entries, project entries, specs, comments",
|
|
472
|
+
{
|
|
473
|
+
task_id: z8.number().describe("Task ID")
|
|
474
|
+
},
|
|
475
|
+
async (params) => {
|
|
476
|
+
try {
|
|
477
|
+
const result = await client2.getTaskContextPackage(params.task_id);
|
|
478
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
479
|
+
} catch (error) {
|
|
480
|
+
return handleError(error, client2.timeoutMs);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
);
|
|
484
|
+
server2.tool(
|
|
485
|
+
"lp_context_update",
|
|
486
|
+
"Update a context entry's content (requires write access)",
|
|
487
|
+
{
|
|
488
|
+
slug: z8.string().optional().describe("Entry slug"),
|
|
489
|
+
id: z8.number().optional().describe("Entry ID"),
|
|
490
|
+
content: z8.string().describe("New markdown content"),
|
|
491
|
+
change_summary: z8.string().describe("What changed and why")
|
|
492
|
+
},
|
|
493
|
+
async (params) => {
|
|
494
|
+
const identifier = params.slug || (params.id !== void 0 ? String(params.id) : void 0);
|
|
495
|
+
if (!identifier) {
|
|
496
|
+
return {
|
|
497
|
+
content: [{ type: "text", text: JSON.stringify({ error: true, code: "validation_error", message: "Either slug or id is required" }) }],
|
|
498
|
+
isError: true
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
try {
|
|
502
|
+
const result = await client2.updateContextEntry(identifier, {
|
|
503
|
+
content: params.content,
|
|
504
|
+
change_summary: params.change_summary
|
|
505
|
+
});
|
|
506
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
507
|
+
} catch (error) {
|
|
508
|
+
return handleError(error, client2.timeoutMs);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
405
514
|
// src/resources/project-context.ts
|
|
406
515
|
function registerProjectContextResource(server2, client2) {
|
|
407
516
|
server2.resource(
|
|
@@ -479,14 +588,79 @@ function registerTaskSpecResource(server2, client2) {
|
|
|
479
588
|
);
|
|
480
589
|
}
|
|
481
590
|
|
|
591
|
+
// src/resources/context-entries.ts
|
|
592
|
+
function registerContextEntriesResource(server2, client2) {
|
|
593
|
+
server2.resource(
|
|
594
|
+
"context-entries",
|
|
595
|
+
"context://entries",
|
|
596
|
+
{
|
|
597
|
+
description: "Browse all active context entries"
|
|
598
|
+
},
|
|
599
|
+
async (uri) => {
|
|
600
|
+
const result = await client2.listContextEntries();
|
|
601
|
+
const lines = ["# Context Entries\n"];
|
|
602
|
+
for (const entry of result.entries) {
|
|
603
|
+
const project = entry.project ? ` (${entry.project.name})` : "";
|
|
604
|
+
lines.push(`- **${entry.name}**${project} \u2014 \`${entry.slug}\` [${entry.entry_type}] (v${entry.version_count}, updated ${entry.updated_at})`);
|
|
605
|
+
}
|
|
606
|
+
return {
|
|
607
|
+
contents: [
|
|
608
|
+
{
|
|
609
|
+
uri: uri.href,
|
|
610
|
+
mimeType: "text/markdown",
|
|
611
|
+
text: lines.join("\n")
|
|
612
|
+
}
|
|
613
|
+
]
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
);
|
|
617
|
+
server2.resource(
|
|
618
|
+
"context-entry",
|
|
619
|
+
"context://entries/{slug}",
|
|
620
|
+
{
|
|
621
|
+
description: "Read a specific context entry by slug"
|
|
622
|
+
},
|
|
623
|
+
async (uri) => {
|
|
624
|
+
if (uri.host !== "entries" || !uri.pathname || uri.pathname === "/") {
|
|
625
|
+
throw new Error(`Invalid URI: ${uri.href}`);
|
|
626
|
+
}
|
|
627
|
+
const slug = decodeURIComponent(uri.pathname.slice(1));
|
|
628
|
+
const result = await client2.getContextEntry(slug);
|
|
629
|
+
const entry = result.entry;
|
|
630
|
+
const text = [
|
|
631
|
+
`# ${entry.name}`,
|
|
632
|
+
``,
|
|
633
|
+
`- **Type:** ${entry.entry_type}`,
|
|
634
|
+
`- **Slug:** ${entry.slug}`,
|
|
635
|
+
`- **Version:** ${entry.version}`,
|
|
636
|
+
`- **Updated:** ${entry.updated_at}`,
|
|
637
|
+
entry.project ? `- **Project:** ${entry.project.name}` : null,
|
|
638
|
+
``,
|
|
639
|
+
`---`,
|
|
640
|
+
``,
|
|
641
|
+
entry.content
|
|
642
|
+
].filter(Boolean).join("\n");
|
|
643
|
+
return {
|
|
644
|
+
contents: [
|
|
645
|
+
{
|
|
646
|
+
uri: uri.href,
|
|
647
|
+
mimeType: "text/markdown",
|
|
648
|
+
text
|
|
649
|
+
}
|
|
650
|
+
]
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
|
|
482
656
|
// src/prompts/start-task.ts
|
|
483
|
-
import { z as
|
|
657
|
+
import { z as z9 } from "zod";
|
|
484
658
|
function registerStartTaskPrompt(server2) {
|
|
485
659
|
server2.prompt(
|
|
486
660
|
"lp_start_task",
|
|
487
661
|
"Guided workflow: find a task, claim it, get context, start working",
|
|
488
662
|
{
|
|
489
|
-
project_id:
|
|
663
|
+
project_id: z9.string().optional().describe("Optional project ID to filter tasks")
|
|
490
664
|
},
|
|
491
665
|
async (args) => {
|
|
492
666
|
const projectLine = args.project_id ? ` Project ID: ${args.project_id}.` : "";
|
|
@@ -516,14 +690,14 @@ IMPORTANT: If this task takes more than 20 minutes, call lp_heartbeat every 20 m
|
|
|
516
690
|
}
|
|
517
691
|
|
|
518
692
|
// src/prompts/submit-task.ts
|
|
519
|
-
import { z as
|
|
693
|
+
import { z as z10 } from "zod";
|
|
520
694
|
function registerSubmitTaskPrompt(server2) {
|
|
521
695
|
server2.prompt(
|
|
522
696
|
"lp_submit_task",
|
|
523
697
|
"Guided workflow: mark task done, add summary comment, release claim",
|
|
524
698
|
{
|
|
525
|
-
task_id:
|
|
526
|
-
summary:
|
|
699
|
+
task_id: z10.string().describe("Task ID to submit"),
|
|
700
|
+
summary: z10.string().describe("Summary of what was completed")
|
|
527
701
|
},
|
|
528
702
|
async (args) => {
|
|
529
703
|
return {
|
|
@@ -572,7 +746,7 @@ try {
|
|
|
572
746
|
}
|
|
573
747
|
var server = new McpServer({
|
|
574
748
|
name: "launchpad",
|
|
575
|
-
version: "1.
|
|
749
|
+
version: "1.1.0"
|
|
576
750
|
});
|
|
577
751
|
registerBootstrapTool(server, client);
|
|
578
752
|
registerProjectTools(server, client);
|
|
@@ -582,8 +756,10 @@ registerCommentTools(server, client);
|
|
|
582
756
|
registerTimeTools(server, client);
|
|
583
757
|
registerPromptTools(server, client);
|
|
584
758
|
registerPageTools(server, client);
|
|
759
|
+
registerContextTools(server, client);
|
|
585
760
|
registerProjectContextResource(server, client);
|
|
586
761
|
registerTaskSpecResource(server, client);
|
|
762
|
+
registerContextEntriesResource(server, client);
|
|
587
763
|
registerStartTaskPrompt(server);
|
|
588
764
|
registerSubmitTaskPrompt(server);
|
|
589
765
|
var transport = new StdioServerTransport();
|