@limeadelabs/launchpad-mcp 1.2.0 → 1.2.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.
Files changed (2) hide show
  1. package/dist/index.js +43 -38
  2. package/package.json +8 -2
package/dist/index.js CHANGED
@@ -184,7 +184,7 @@ function registerProjectTools(server2, client2) {
184
184
  "lp_get_project",
185
185
  "Get project details including agent_instructions",
186
186
  {
187
- project_id: z.number().describe("Project ID")
187
+ project_id: z.coerce.number().describe("Project ID")
188
188
  },
189
189
  async (params) => {
190
190
  try {
@@ -204,11 +204,11 @@ function registerTaskTools(server2, client2) {
204
204
  "lp_list_tasks",
205
205
  "List tasks from LaunchPad with optional filters (project, status, priority, label, assignee)",
206
206
  {
207
- project_id: z2.number().optional().describe("Filter by project ID"),
207
+ project_id: z2.coerce.number().optional().describe("Filter by project ID"),
208
208
  status: z2.string().optional().describe("Filter by status: todo, ready, in_progress, review, done"),
209
209
  priority: z2.string().optional().describe("Filter by priority: low, medium, high"),
210
210
  label: z2.string().optional().describe("Filter by label name"),
211
- assignee_id: z2.number().optional().describe("Filter by assignee user ID")
211
+ assignee_id: z2.coerce.number().optional().describe("Filter by assignee user ID")
212
212
  },
213
213
  async (params) => {
214
214
  try {
@@ -223,7 +223,7 @@ function registerTaskTools(server2, client2) {
223
223
  "lp_get_task",
224
224
  "Get full task context \u2014 description, comments, links, specs",
225
225
  {
226
- task_id: z2.number().describe("Task ID")
226
+ task_id: z2.coerce.number().describe("Task ID")
227
227
  },
228
228
  async (params) => {
229
229
  try {
@@ -238,7 +238,7 @@ function registerTaskTools(server2, client2) {
238
238
  "lp_create_task",
239
239
  "Create a new task in LaunchPad",
240
240
  {
241
- project_id: z2.number().describe("Project ID to create the task in"),
241
+ project_id: z2.coerce.number().describe("Project ID to create the task in"),
242
242
  title: z2.string().describe("Task title"),
243
243
  description: z2.string().optional().describe("Task description"),
244
244
  status: z2.string().optional().describe("Initial status: todo, ready, in_progress, review, done"),
@@ -257,10 +257,10 @@ function registerTaskTools(server2, client2) {
257
257
  "lp_update_task",
258
258
  "Update task status and/or fields. LaunchPad enforces workflow transitions \u2014 use lp_get_workflow to check valid transitions before changing status.",
259
259
  {
260
- task_id: z2.number().describe("Task ID"),
260
+ task_id: z2.coerce.number().describe("Task ID"),
261
261
  status: z2.string().optional().describe("New status (must be a valid workflow transition)"),
262
262
  priority: z2.string().optional().describe("New priority: low, medium, high"),
263
- assignee_id: z2.number().optional().describe("Assignee user ID"),
263
+ assignee_id: z2.coerce.number().optional().describe("Assignee user ID"),
264
264
  title: z2.string().optional().describe("New title"),
265
265
  description: z2.string().optional().describe("New description")
266
266
  },
@@ -283,7 +283,7 @@ function registerClaimTools(server2, client2) {
283
283
  "lp_claim_task",
284
284
  "Claim a task before working on it. Prevents other agents from picking it up.",
285
285
  {
286
- task_id: z3.number().describe("Task ID to claim")
286
+ task_id: z3.coerce.number().describe("Task ID to claim")
287
287
  },
288
288
  async (params) => {
289
289
  try {
@@ -298,7 +298,7 @@ function registerClaimTools(server2, client2) {
298
298
  "lp_release_task",
299
299
  "Release a claimed task so others can pick it up",
300
300
  {
301
- task_id: z3.number().describe("Task ID to release")
301
+ task_id: z3.coerce.number().describe("Task ID to release")
302
302
  },
303
303
  async (params) => {
304
304
  try {
@@ -313,7 +313,7 @@ function registerClaimTools(server2, client2) {
313
313
  "lp_heartbeat",
314
314
  "Send a heartbeat for a claimed task to keep the claim alive. Claims expire after 30 minutes without a heartbeat.",
315
315
  {
316
- task_id: z3.number().describe("Task ID to heartbeat")
316
+ task_id: z3.coerce.number().describe("Task ID to heartbeat")
317
317
  },
318
318
  async (params) => {
319
319
  try {
@@ -333,7 +333,7 @@ function registerCommentTools(server2, client2) {
333
333
  "lp_add_comment",
334
334
  "Post a comment on a task (progress updates, notes, questions)",
335
335
  {
336
- task_id: z4.number().describe("Task ID to comment on"),
336
+ task_id: z4.coerce.number().describe("Task ID to comment on"),
337
337
  body: z4.string().describe("Comment body text")
338
338
  },
339
339
  async (params) => {
@@ -354,8 +354,8 @@ function registerTimeTools(server2, client2) {
354
354
  "lp_log_time",
355
355
  "Track time spent on a task",
356
356
  {
357
- task_id: z5.number().describe("Task ID"),
358
- duration_minutes: z5.number().positive().describe("Duration in minutes"),
357
+ task_id: z5.coerce.number().describe("Task ID"),
358
+ duration_minutes: z5.coerce.number().positive().describe("Duration in minutes"),
359
359
  description: z5.string().optional().describe("Description of work done")
360
360
  },
361
361
  async (params) => {
@@ -376,7 +376,7 @@ function registerPromptTools(server2, client2) {
376
376
  "lp_generate_prompt",
377
377
  "Generate a build-ready prompt/spec for a task with full context, conventions, and acceptance criteria",
378
378
  {
379
- task_id: z6.number().describe("Task ID")
379
+ task_id: z6.coerce.number().describe("Task ID")
380
380
  },
381
381
  async (params) => {
382
382
  try {
@@ -394,14 +394,16 @@ import { z as z7 } from "zod";
394
394
  function registerPageTools(server2, client2) {
395
395
  server2.tool(
396
396
  "lp_list_pages",
397
- "List spec/doc pages for a project",
397
+ "List spec/doc pages for a project (titles and IDs only \u2014 use lp_get_page to fetch full content)",
398
398
  {
399
- project_id: z7.number().describe("Project ID")
399
+ project_id: z7.coerce.number().describe("Project ID")
400
400
  },
401
401
  async (params) => {
402
402
  try {
403
403
  const result = await client2.listPages(params.project_id);
404
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
404
+ const pages = result.pages ?? result;
405
+ const summary = Array.isArray(pages) ? pages.map(({ id, title, author, created_at, updated_at }) => ({ id, title, author, created_at, updated_at })) : pages;
406
+ return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
405
407
  } catch (error) {
406
408
  return handleError(error, client2.timeoutMs);
407
409
  }
@@ -409,14 +411,17 @@ function registerPageTools(server2, client2) {
409
411
  );
410
412
  server2.tool(
411
413
  "lp_get_page",
412
- "Get a page's full content",
414
+ "Get a page's full content by project_id and page_id (also accepts id as alias for page_id)",
413
415
  {
414
- project_id: z7.number().describe("Project ID"),
415
- page_id: z7.number().describe("Page ID")
416
+ project_id: z7.coerce.number().describe("Project ID"),
417
+ page_id: z7.coerce.number().optional().describe("Page ID"),
418
+ id: z7.coerce.number().optional().describe("Page ID (alias for page_id)")
416
419
  },
417
420
  async (params) => {
418
421
  try {
419
- const result = await client2.getPage(params.project_id, params.page_id);
422
+ const pageId = params.page_id ?? params.id;
423
+ if (!pageId) throw new Error("page_id or id is required");
424
+ const result = await client2.getPage(params.project_id, pageId);
420
425
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
421
426
  } catch (error) {
422
427
  return handleError(error, client2.timeoutMs);
@@ -427,7 +432,7 @@ function registerPageTools(server2, client2) {
427
432
  "lp_get_workflow",
428
433
  "Get valid workflow states and transitions for a project",
429
434
  {
430
- project_id: z7.number().describe("Project ID")
435
+ project_id: z7.coerce.number().describe("Project ID")
431
436
  },
432
437
  async (params) => {
433
438
  try {
@@ -447,11 +452,11 @@ function registerContextTools(server2, client2) {
447
452
  "lp_context_list",
448
453
  "List available context entries with optional filters",
449
454
  {
450
- project_id: z8.number().optional().describe("Filter by project ID"),
455
+ project_id: z8.coerce.number().optional().describe("Filter by project ID"),
451
456
  entry_type: z8.string().optional().describe("Filter by entry type"),
452
457
  search: z8.string().optional().describe("Text search on entry name"),
453
- limit: z8.number().min(1).max(100).optional().describe("Max entries to return (default 50)"),
454
- offset: z8.number().min(0).optional().describe("Offset for pagination")
458
+ limit: z8.coerce.number().min(1).max(100).optional().describe("Max entries to return (default 50)"),
459
+ offset: z8.coerce.number().min(0).optional().describe("Offset for pagination")
455
460
  },
456
461
  async (params) => {
457
462
  try {
@@ -467,7 +472,7 @@ function registerContextTools(server2, client2) {
467
472
  "Fetch a specific context entry by slug or ID",
468
473
  {
469
474
  slug: z8.string().optional().describe("Entry slug"),
470
- id: z8.number().optional().describe("Entry ID")
475
+ id: z8.coerce.number().optional().describe("Entry ID")
471
476
  },
472
477
  async (params) => {
473
478
  const identifier = params.slug || (params.id !== void 0 ? String(params.id) : void 0);
@@ -489,7 +494,7 @@ function registerContextTools(server2, client2) {
489
494
  "lp_context_package",
490
495
  "Get the assembled context package for a task \u2014 task details, context entries, specs, pages, comments",
491
496
  {
492
- task_id: z8.number().describe("Task ID")
497
+ task_id: z8.coerce.number().describe("Task ID")
493
498
  },
494
499
  async (params) => {
495
500
  try {
@@ -505,7 +510,7 @@ function registerContextTools(server2, client2) {
505
510
  "Update a context entry's content (requires write access)",
506
511
  {
507
512
  slug: z8.string().optional().describe("Entry slug"),
508
- id: z8.number().optional().describe("Entry ID"),
513
+ id: z8.coerce.number().optional().describe("Entry ID"),
509
514
  content: z8.string().describe("New markdown content"),
510
515
  change_summary: z8.string().describe("What changed and why")
511
516
  },
@@ -563,7 +568,7 @@ function registerSessionTools(server2, client2) {
563
568
  "lp_session_start",
564
569
  "Start a new agent session for a task. Creates session via API and saves session_id to disk for later lookup.",
565
570
  {
566
- task_id: z9.number().describe("LaunchPad task ID"),
571
+ task_id: z9.coerce.number().describe("LaunchPad task ID"),
567
572
  agent_type: z9.string().optional().describe("Agent type (default: claude_code)")
568
573
  },
569
574
  async ({ task_id, agent_type }) => {
@@ -582,8 +587,8 @@ function registerSessionTools(server2, client2) {
582
587
  "lp_session_heartbeat",
583
588
  "Send a heartbeat for an active session. Provide session_id directly or task_id to look up session from disk. Graceful no-op if neither provided.",
584
589
  {
585
- session_id: z9.number().optional().describe("Session ID"),
586
- task_id: z9.number().optional().describe("Task ID to look up session from disk")
590
+ session_id: z9.coerce.number().optional().describe("Session ID"),
591
+ task_id: z9.coerce.number().optional().describe("Task ID to look up session from disk")
587
592
  },
588
593
  async ({ session_id, task_id }) => {
589
594
  try {
@@ -606,7 +611,7 @@ function registerSessionTools(server2, client2) {
606
611
  "lp_session_progress",
607
612
  'Report progress on an active session. Sends an activity update with action "progress".',
608
613
  {
609
- session_id: z9.number().describe("Session ID"),
614
+ session_id: z9.coerce.number().describe("Session ID"),
610
615
  detail: z9.string().describe("Description of progress made")
611
616
  },
612
617
  async ({ session_id, detail }) => {
@@ -626,7 +631,7 @@ function registerSessionTools(server2, client2) {
626
631
  "lp_session_event",
627
632
  "Log a discrete event for a session (commit, ci_pass, ci_fail, pr_opened, blocker, cost_update).",
628
633
  {
629
- session_id: z9.number().describe("Session ID"),
634
+ session_id: z9.coerce.number().describe("Session ID"),
630
635
  event_type: z9.enum(["commit", "ci_pass", "ci_fail", "pr_opened", "blocker", "cost_update"]).describe("Type of event"),
631
636
  payload: z9.record(z9.unknown()).describe("Event payload data")
632
637
  },
@@ -645,7 +650,7 @@ function registerSessionTools(server2, client2) {
645
650
  "lp_session_blocked",
646
651
  'Mark a session as blocked. Updates status to "blocked" and logs a blocker event.',
647
652
  {
648
- session_id: z9.number().describe("Session ID"),
653
+ session_id: z9.coerce.number().describe("Session ID"),
649
654
  reason: z9.string().describe("Reason the session is blocked")
650
655
  },
651
656
  async ({ session_id, reason }) => {
@@ -669,9 +674,9 @@ function registerSessionTools(server2, client2) {
669
674
  "lp_session_complete",
670
675
  "Mark a session as completed with a result summary. Clears session from disk.",
671
676
  {
672
- session_id: z9.number().describe("Session ID"),
677
+ session_id: z9.coerce.number().describe("Session ID"),
673
678
  result_summary: z9.string().describe("Summary of what was accomplished"),
674
- task_id: z9.number().optional().describe("Task ID to clear from disk (if not provided, derived from session)")
679
+ task_id: z9.coerce.number().optional().describe("Task ID to clear from disk (if not provided, derived from session)")
675
680
  },
676
681
  async ({ session_id, result_summary, task_id }) => {
677
682
  try {
@@ -693,9 +698,9 @@ function registerSessionTools(server2, client2) {
693
698
  "lp_session_fail",
694
699
  "Mark a session as failed with an error detail. Clears session from disk.",
695
700
  {
696
- session_id: z9.number().describe("Session ID"),
701
+ session_id: z9.coerce.number().describe("Session ID"),
697
702
  error_detail: z9.string().describe("Description of the failure"),
698
- task_id: z9.number().optional().describe("Task ID to clear from disk (if not provided, derived from session)")
703
+ task_id: z9.coerce.number().optional().describe("Task ID to clear from disk (if not provided, derived from session)")
699
704
  },
700
705
  async ({ session_id, error_detail, task_id }) => {
701
706
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@limeadelabs/launchpad-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "LaunchPad MCP server for Claude Code — AI-native project management integration",
5
5
  "type": "module",
6
6
  "exports": {
@@ -29,7 +29,13 @@
29
29
  "engines": {
30
30
  "node": ">=18.0.0"
31
31
  },
32
- "keywords": ["mcp", "launchpad", "claude", "project-management", "ai-agents"],
32
+ "keywords": [
33
+ "mcp",
34
+ "launchpad",
35
+ "claude",
36
+ "project-management",
37
+ "ai-agents"
38
+ ],
33
39
  "license": "MIT",
34
40
  "publishConfig": {
35
41
  "access": "public"