@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.
Files changed (3) hide show
  1. package/README.md +10 -0
  2. package/dist/index.js +182 -6
  3. 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 z8 } from "zod";
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: z8.string().optional().describe("Optional project ID to filter tasks")
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 z9 } from "zod";
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: z9.string().describe("Task ID to submit"),
526
- summary: z9.string().describe("Summary of what was completed")
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.0.0"
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@limeadelabs/launchpad-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "LaunchPad MCP server for Claude Code — AI-native project management integration",
5
5
  "type": "module",
6
6
  "exports": {