Skip to content

Expand MCP tools from 12 to 35+ with Plus subscription gating - #1

Merged
TheEagleByte merged 4 commits into
mainfrom
feature/expand-tools-plus-subscription
Dec 29, 2025
Merged

TheEagleByte merged 4 commits into
mainfrom
feature/expand-tools-plus-subscription

Conversation

@TheEagleByte

@TheEagleByte TheEagleByte commented Dec 29, 2025 •

Copy link
Copy Markdown
Owner

Summary

  • Expand MCP server from 12 to 35+ tools with full CRUD operations
  • Add Plus subscription gating for premium features (rewards, meals, photos)
  • Add ESLint configuration and 31 unit tests
  • Improve type safety and reduce code duplication

Changes

New Tools (23+)

Calendar: create_calendar_event, update_calendar_event, delete_calendar_event
Chores: update_chore, delete_chore
Lists: create_list, update_list, delete_list, create_list_item, update_list_item, delete_list_item
Misc: get_avatars, get_colors

Plus-Only:

  • Rewards: create_reward, update_reward, delete_reward, redeem_reward, unredeem_reward
  • Meals: get_meal_categories, get_recipes, get_recipe, create_recipe, update_recipe, delete_recipe, add_recipe_to_grocery_list, get_meal_sittings, create_meal_sitting
  • Photos: get_albums

Code Quality Improvements

  • Added .eslintrc.cjs configuration
  • Added 31 unit tests for date utilities and error handling
  • Typed SubscriptionStatus with proper union type
  • Made formatErrorForMcp accept unknown for safer error handling
  • Extracted resolveListId helper to reduce duplication in list tools

Test plan

  • npm run typecheck passes
  • npm run lint passes
  • npm test - 31/31 tests pass
  • Manual testing with Skylight account

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Meals: full recipe & sitting management and categories.
    • Photos: album listing.
    • Rewards: create/update/delete, redeem/unredeem.
    • Calendar, chores, lists: full create/update/delete operations.
    • Avatars & colors endpoints.
    • Plus subscription support with gated features.
  • Infrastructure

    • CI and Release workflows added.
    • ESLint configuration and OpenAPI type generation script.
  • Bug Fixes

    • More robust error formatting and handling.
  • Tests

    • Added date utilities and error handling tests.

✏️ Tip: You can customize this high-level summary in your review settings.

TheEagleByte and others added 2 commits December 29, 2025 15:14
- Add subscription status tracking to client.ts (hasPlus(), initialize())
- Pre-initialize client in server.ts for conditional tool registration
- Add Plus-only tool gating for Rewards, Meals, and Photos domains

New tools added:
- Calendar: create_calendar_event, update_calendar_event, delete_calendar_event
- Chores: update_chore, delete_chore
- Lists: create_list, update_list, delete_list, create_list_item, update_list_item, delete_list_item
- Misc: get_avatars, get_colors
- Rewards (Plus): create_reward, update_reward, delete_reward, redeem_reward, unredeem_reward
- Meals (Plus): 9 tools for categories, recipes, and meal sittings
- Photos (Plus): get_albums

Also adds:
- openapi-typescript for type generation from OpenAPI spec
- Generated types in src/api/generated-types.ts
- Updated CLAUDE.md documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ESLint configuration (.eslintrc.cjs)
- Add 31 unit tests for date utilities and error handling
- Type subscriptionStatus with proper union type
- Make formatErrorForMcp accept unknown for safer error handling
- Extract resolveListId helper to reduce duplication in list tools
- Fix lint error in dates.ts (const vs let)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- CI workflow: runs lint, typecheck, tests, and build on PRs and main
- Tests against Node.js 18, 20, and 22
- Release workflow: creates GitHub releases on version tags (v*)
- Auto-generates changelog from commits since last tag

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Dec 29, 2025 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds subscription-aware client initialization and Plus gating; implements many new API endpoints (meals, lists, calendar events, chores, photos, rewards) and corresponding MCP tools; introduces ESLint config, CI/release workflows, OpenAPI type generation, tests for dates and error formatting, and error-handling signature changes.

Changes

Cohort / File(s) Summary
Config & CI
\.eslintrc.cjs, package.json, .github/workflows/ci.yml, .github/workflows/release.yml
Add ESLint config, openapi-typescript devDependency and generate:types script, CI matrix (Node 18/20/22), and release workflow extracting changelog from tags.
Docs & Generated Types
CLAUDE.md, src/api/generated-types.ts
Document Plus subscription, update tool/feature lists, and add generated OpenAPI types file (note: generated file referenced).
Client & Core Types
src/api/client.ts, src/api/types.ts
Add SubscriptionStatus, subscription tracking on SkylightClient, hasPlus(), getSubscriptionStatus(), initialize(), exported initializeClient(), add RequestOptions.body?, and new request/response types for lists, calendar events, chores, rewards.
API Endpoints — Lists / Calendar / Chores
src/api/endpoints/lists.ts, src/api/endpoints/calendar.ts, src/api/endpoints/chores.ts
Add create/update/delete endpoints for lists, list items, calendar events; add chore update and delete endpoints; import new request/response types.
API Endpoints — Meals / Photos / Misc / Rewards
src/api/endpoints/meals.ts, src/api/endpoints/photos.ts, src/api/endpoints/misc.ts, src/api/endpoints/rewards.ts
New modules for meals (categories, recipes, sittings), photo albums, avatars/colors, and rewards (create/update/delete/redeem/unredeem) with typed payloads.
Server Startup & Tool Registration
src/server.ts
Initialize client at startup, read hasPlus(), register base tools always and conditionally register Plus-only tools (meals, rewards, photos); log Plus status.
MCP Tools — Calendar / Lists / Chores
src/tools/calendar.ts, src/tools/lists.ts, src/tools/chores.ts
Add create/update/delete calendar tools; full CRUD list/item tools with resolveListId() helper; add update/delete chore tools; adjust error formatting usage.
MCP Tools — Meals / Photos / Misc / Rewards
src/tools/meals.ts, src/tools/photos.ts, src/tools/misc.ts, src/tools/rewards.ts
Register Meal tools (9 tools), Photo album tool, Misc tools (avatars/colors), and Reward management tools; schemas and handlers added.
Error & Date Utilities
src/utils/errors.ts, src/utils/dates.ts, src/tools/family.ts, src/tools/tasks.ts
Change formatErrorForMcp(error) signature to accept unknown; update callers to pass caught error without casting; small const binding change in parseTime.
Tests
tests/dates.test.ts, tests/errors.test.ts
Add comprehensive tests for date utilities and error formatting/exception classes, including non-Error inputs.

Sequence Diagram(s)

sequenceDiagram
    rect rgb(240,248,255)
    participant Server as Server Startup
    participant Client as SkylightClient
    participant Auth as Auth Service
    participant Registry as MCP Tool Registry
    end

    Server->>Client: initializeClient()
    activate Client
    Client->>Auth: login()
    activate Auth
    Auth-->>Client: { subscriptionStatus }
    deactivate Auth
    Client->>Client: store subscriptionStatus
    deactivate Client

    Server->>Client: hasPlus()
    Client-->>Server: boolean

    alt hasPlus = true
        Server->>Registry: register base tools
        Server->>Registry: register reward, meal, photo tools
    else hasPlus = false
        Server->>Registry: register base tools only
    end

    Server-->>Server: Log "Skylight MCP Server started (Plus: true/false)"
Loading

🎯 4 (Complex) | ⏱️ ~60 minutes

🐰 I hopped in code beneath the moon,
Gates for Plus and tools in tune,
Meals, lists, photos — features bloom,
Workflows hum and tests consume,
A nibble, a hop — the server’s new boon!

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.72% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: expanding MCP tools from 12 to 35+ with Plus subscription gating.

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a167a2 and 89d4c5e.

📒 Files selected for processing (1)
  • .github/workflows/release.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/release.yml

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/api/client.ts (1)

70-81: Unsafe type assertion and missing subscription status for token-based auth users.

Two confirmed concerns:

  1. Line 78 - Unsafe type assertion: The API returns subscription_status as a generic string, but the code blindly asserts it to SubscriptionStatus ("plus" | "free" | "trial" | null) without validation. If the API returns an unexpected value, the type assertion silently passes and may cause incorrect behavior in hasPlus().

  2. Token-based auth limitation: Users who configure SKYLIGHT_TOKEN instead of email/password skip performLogin() entirely, leaving subscriptionStatus permanently null. Since hasPlus() only returns true when subscriptionStatus === "plus", token-based auth users cannot access Plus features (rewards, meals, photos) regardless of their actual subscription.

Add validation for the subscription status value before assignment, and provide a mechanism for token-based auth users to set their subscription status (either by fetching it from a separate API endpoint or storing it in configuration).

🧹 Nitpick comments (9)
.github/workflows/release.yml (1)

70-81: Consider more explicit prerelease detection.

The current prerelease detection on Line 79 uses a simple check for hyphens in the tag name. While this works for common conventions (v1.0.0-beta, v1.0.0-rc1), it may produce false positives if your tags include hyphens for other reasons (e.g., date-based tags like v2025-01-15).

Alternative prerelease detection

If you want more explicit control, consider checking for specific prerelease identifiers:

           draft: false
-          prerelease: ${{ contains(github.ref, '-') }}
+          prerelease: ${{ contains(github.ref, '-beta') || contains(github.ref, '-alpha') || contains(github.ref, '-rc') }}

Or use a regex-based check in a previous step if you need more complex logic.

Optional: Pin actions to commit SHAs for enhanced security.

Currently, actions are pinned to major versions (@v4, @v2). While this is a common practice, pinning to specific commit SHAs provides stronger security guarantees against potential supply chain attacks.

Example with SHA pinning
       - name: Checkout repository
-        uses: actions/checkout@v4
+        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
         with:
           fetch-depth: 0

       - name: Setup Node.js
-        uses: actions/setup-node@v4
+        uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2

Note: You would need to look up the specific SHA for the versions you want to use and update them periodically.

src/utils/dates.ts (1)

97-97: Good refactor: immutable binding.

Changing from let to const for the destructured match groups is a good practice that prevents accidental reassignment.

package.json (1)

19-19: Document or reconfigure the OpenAPI type generation setup.

The generate:types script assumes ../skylight-api/ exists as a sibling directory, but this isn't documented in the README. While the pre-generated types are committed so it doesn't block users or CI, contributors who need to regenerate types from the OpenAPI spec will encounter this undocumented dependency.

Consider:

  • Adding a section in the README documenting the type generation process and the skylight-api directory requirement
  • Making the OpenAPI spec path configurable via an environment variable (e.g., OPENAPI_SPEC_PATH)
  • Clarifying whether the OpenAPI spec should be included or vendored in this repository
tests/errors.test.ts (1)

1-11: Good test coverage; consider adding ParseError test.

ParseError is imported (line 8) but not tested. Consider adding a test case for consistency with the other error classes.

🔎 Suggested test case
describe("ParseError", () => {
  it("creates error with default message", () => {
    const error = new ParseError();
    expect(error.message).toBe("Unexpected API response format");
    expect(error.code).toBe("PARSE_ERROR");
    expect(error.recoverable).toBe(false);
  });
});
src/tools/misc.ts (1)

34-44: Consider extracting shared formatting logic.

The attribute formatting logic (lines 34-44 and 90-100) is nearly identical between get_avatars and get_colors. This could be extracted into a helper function for maintainability.

🔎 Example helper extraction
function formatResourceList<T extends { id: string; attributes: Record<string, unknown> }>(
  items: T[],
  labelFn: (item: T) => string
): string {
  return items
    .map((item) => {
      const parts = [labelFn(item)];
      for (const [key, value] of Object.entries(item.attributes)) {
        if (value !== null && value !== undefined) {
          parts.push(`  ${key}: ${value}`);
        }
      }
      return parts.join("\n");
    })
    .join("\n\n");
}

Also applies to: 90-100

src/tools/lists.ts (4)

29-55: Consider improving name resolution when only listId is provided.

When listId is provided without listName, the function returns the ID as the name (line 35). This means confirmation messages like "Deleted list '123abc'" might be less user-friendly. Consider fetching the list name if needed for better UX, or document this behavior.


345-350: Consider validating that at least one update field is provided.

If the user calls update_list without providing any of label, kind, or color, an empty updates object is sent to the API. Consider returning an early error message if no updates are specified.

🔎 Proposed fix
         const updates: { label?: string; kind?: "shopping" | "to_do"; color?: string | null } = {};
         if (label !== undefined) updates.label = label;
         if (kind !== undefined) updates.kind = kind;
         if (color !== undefined) updates.color = color;
 
+        if (Object.keys(updates).length === 0) {
+          return {
+            content: [{ type: "text" as const, text: "No updates provided. Specify at least one of: label, kind, or color." }],
+            isError: true,
+          };
+        }
+
         const updated = await updateList(resolved.id, updates);

495-503: Same consideration: validate that updates are provided.

Similar to update_list, if no update fields are specified, an empty updates object is sent. Consider early validation.


539-549: Consider including item details in deletion confirmation.

The confirmation message "Deleted item from list" doesn't include the item ID or label. Including the itemId would help users confirm the correct item was deleted.

🔎 Proposed fix
-              text: `Deleted item from list`,
+              text: `Deleted item (ID: ${itemId}) from list`,
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9638f06 and 7a167a2.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (29)
  • .eslintrc.cjs
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • CLAUDE.md
  • package.json
  • src/api/client.ts
  • src/api/endpoints/calendar.ts
  • src/api/endpoints/chores.ts
  • src/api/endpoints/lists.ts
  • src/api/endpoints/meals.ts
  • src/api/endpoints/misc.ts
  • src/api/endpoints/photos.ts
  • src/api/endpoints/rewards.ts
  • src/api/generated-types.ts
  • src/api/types.ts
  • src/server.ts
  • src/tools/calendar.ts
  • src/tools/chores.ts
  • src/tools/family.ts
  • src/tools/lists.ts
  • src/tools/meals.ts
  • src/tools/misc.ts
  • src/tools/photos.ts
  • src/tools/rewards.ts
  • src/tools/tasks.ts
  • src/utils/dates.ts
  • src/utils/errors.ts
  • tests/dates.test.ts
  • tests/errors.test.ts
🧰 Additional context used
🧬 Code graph analysis (18)
src/tools/tasks.ts (1)
src/utils/errors.ts (1)
  • formatErrorForMcp (75-117)
src/tools/misc.ts (2)
src/api/endpoints/misc.ts (2)
  • getAvatars (34-38)
  • getColors (43-47)
src/utils/errors.ts (1)
  • formatErrorForMcp (75-117)
src/api/endpoints/photos.ts (1)
src/api/client.ts (1)
  • getClient (244-249)
src/tools/photos.ts (2)
src/api/endpoints/photos.ts (1)
  • getAlbums (19-23)
src/utils/errors.ts (1)
  • formatErrorForMcp (75-117)
tests/dates.test.ts (1)
src/utils/dates.ts (5)
  • getTodayDate (8-14)
  • getDateOffset (19-26)
  • parseDate (32-79)
  • parseTime (85-109)
  • formatDateForDisplay (114-121)
src/api/endpoints/misc.ts (1)
src/api/client.ts (1)
  • getClient (244-249)
src/api/endpoints/rewards.ts (2)
src/api/types.ts (4)
  • RewardResource (162-166)
  • CreateRewardRequest (298-314)
  • RewardResponse (334-334)
  • UpdateRewardRequest (316-332)
src/api/client.ts (2)
  • getClient (244-249)
  • request (157-189)
src/api/endpoints/chores.ts (2)
src/api/types.ts (3)
  • ChoreResource (56-61)
  • UpdateChoreRequest (289-295)
  • ChoreResponse (180-180)
src/api/client.ts (2)
  • getClient (244-249)
  • request (157-189)
src/tools/family.ts (1)
src/utils/errors.ts (1)
  • formatErrorForMcp (75-117)
src/api/endpoints/calendar.ts (2)
src/api/types.ts (4)
  • CreateCalendarEventRequest (257-271)
  • CalendarEventResource (140-144)
  • CalendarEventResponse (286-286)
  • UpdateCalendarEventRequest (273-284)
src/api/client.ts (1)
  • getClient (244-249)
src/tools/rewards.ts (3)
src/utils/errors.ts (1)
  • formatErrorForMcp (75-117)
src/api/endpoints/categories.ts (1)
  • findCategoryByName (32-40)
src/api/endpoints/rewards.ts (5)
  • createReward (53-78)
  • updateReward (92-125)
  • deleteReward (130-135)
  • redeemReward (140-151)
  • unredeemReward (156-163)
src/api/endpoints/meals.ts (1)
src/api/client.ts (1)
  • getClient (244-249)
src/tools/lists.ts (2)
src/api/endpoints/lists.ts (8)
  • findListByName (46-50)
  • findListByType (55-68)
  • createList (73-91)
  • updateList (96-112)
  • deleteList (117-120)
  • createListItem (125-145)
  • updateListItem (150-167)
  • deleteListItem (172-177)
src/utils/errors.ts (1)
  • formatErrorForMcp (75-117)
src/server.ts (4)
src/api/client.ts (2)
  • initializeClient (255-259)
  • hasPlus (222-224)
src/tools/misc.ts (1)
  • registerMiscTools (6-118)
src/tools/meals.ts (1)
  • registerMealTools (19-388)
src/tools/photos.ts (1)
  • registerPhotoTools (6-51)
src/tools/calendar.ts (3)
src/utils/errors.ts (1)
  • formatErrorForMcp (75-117)
src/config.ts (1)
  • getConfig (89-94)
src/api/endpoints/calendar.ts (3)
  • createCalendarEvent (52-61)
  • updateCalendarEvent (66-76)
  • deleteCalendarEvent (81-86)
tests/errors.test.ts (1)
src/utils/errors.ts (6)
  • SkylightError (4-14)
  • AuthenticationError (19-24)
  • NotFoundError (39-44)
  • RateLimitError (49-59)
  • formatErrorForMcp (75-117)
  • ConfigurationError (29-34)
src/api/endpoints/lists.ts (2)
src/api/types.ts (8)
  • ListResource (77-82)
  • CreateListRequest (209-218)
  • ListResponse (182-182)
  • UpdateListRequest (220-229)
  • ListItemResource (93-97)
  • CreateListItemRequest (232-240)
  • ListItemResponse (254-254)
  • UpdateListItemRequest (242-252)
src/api/client.ts (2)
  • getClient (244-249)
  • request (157-189)
src/tools/chores.ts (5)
src/utils/errors.ts (1)
  • formatErrorForMcp (75-117)
src/config.ts (1)
  • getConfig (89-94)
src/api/endpoints/chores.ts (2)
  • updateChore (118-158)
  • deleteChore (163-168)
src/utils/dates.ts (2)
  • parseDate (32-79)
  • parseTime (85-109)
src/api/endpoints/categories.ts (1)
  • findCategoryByName (32-40)
🔇 Additional comments (66)
.github/workflows/release.yml (4)

1-10: LGTM! Standard release trigger and permissions.

The workflow correctly triggers on semantic version tags and sets appropriate permissions for creating releases.


16-28: LGTM! Proper setup for release workflow.

The checkout with fetch-depth: 0 correctly retrieves full git history needed for changelog generation, and the Node.js setup with caching is appropriate.


30-40: LGTM! Comprehensive quality gates before release.

Running lint, typecheck, tests, and build ensures that only validated code is released. The workflow will correctly abort if any check fails.


42-44: LGTM! Correct version extraction.

The shell parameter expansion correctly extracts the version number from the tag reference.

.github/workflows/ci.yml (1)

1-40: Solid CI setup with comprehensive checks.

The workflow structure is well-designed with matrix testing across supported Node versions (18, 20, 22) and includes all essential quality gates (lint, typecheck, tests, build). The use of current action versions (@v4) and proper npm caching is appropriate.

tests/dates.test.ts (1)

1-142: Comprehensive test coverage for date utilities.

The test suite is well-structured with:

  • Proper use of fake timers with cleanup in afterEach hooks
  • Coverage of edge cases (timezones, month/year boundaries, natural language parsing)
  • Tests for both 12-hour and 24-hour time formats including midnight/noon edge cases
  • Appropriate handling of unparseable inputs
.eslintrc.cjs (1)

1-23: Well-balanced ESLint configuration.

The configuration follows TypeScript best practices:

  • Appropriate rule severity (errors for unused vars, warnings for any and console usage)
  • Smart ignore pattern for unused parameters (^_)
  • Correctly excludes generated files from linting
  • Allows console.error and console.warn which are reasonable for a server application
src/tools/tasks.ts (1)

71-71: Improved type safety in error handling.

Removing the type assertion aligns with the updated formatErrorForMcp(error: unknown) signature, allowing safer handling of non-Error values without forcing a cast.

src/tools/family.ts (1)

94-94: Consistent error handling improvements.

All three error handling sites have been updated to remove unnecessary type assertions, consistently applying the safer formatErrorForMcp(error: unknown) pattern throughout the file.

Also applies to: 144-144, 205-205

src/api/endpoints/photos.ts (1)

1-23: Clean Photos API endpoint implementation.

The implementation follows the established patterns from other endpoint files:

  • Consistent use of getClient()
  • Well-typed interfaces with flexible index signatures
  • Simple, focused function

The URL pattern /api/frames/{frameId}/albums appears to rely on the client's URL interpolation to substitute {frameId}. This is consistent with the patterns used across the API layer.

src/tools/photos.ts (1)

6-51: LGTM! Clean implementation following established patterns.

The tool registration, error handling with formatErrorForMcp, and empty-state handling are well-implemented. The attribute formatting loop at lines 31-35 correctly filters out null/undefined values.

src/server.ts (2)

25-27: Good approach: pre-initialization for subscription gating.

Initializing the client before tool registration ensures Plus status is known at startup. Note that if initializeClient() fails (e.g., network error, invalid token), the server will fail to start entirely. This is appropriate fail-fast behavior for an MCP server.


40-47: Verify: Should registerMiscTools be Plus-gated?

The PR description mentions "Plus-only — Misc: get_avatars, get_colors", but registerMiscTools is registered outside the hasPlus block (line 40) while rewards, meals, and photos are inside. Please confirm whether this is intentional or if misc tools should also be gated.

tests/errors.test.ts (1)

12-116: LGTM! Comprehensive test coverage for error handling.

The tests thoroughly cover error class properties, default values, and the formatErrorForMcp function behavior across different error types including non-Error edge cases.

src/tools/misc.ts (1)

6-118: LGTM! Implementation follows established patterns.

Both tools have clear descriptions, proper empty-state handling, and consistent error handling.

src/api/endpoints/rewards.ts (3)

53-78: LGTM! Well-structured reward creation.

The createReward function correctly builds the JSON:API request structure, handles optional fields with null defaults, and conditionally adds category relationships.


112-118: Verify: Empty categoryIds array behavior differs from createReward.

In createReward (line 68), empty arrays are ignored (categoryIds.length > 0), but here an empty array would send relationships.categories.data: [], potentially clearing all categories. Verify if this is intentional to allow category clearing on update.


130-163: LGTM! Clean implementation of delete and redeem operations.

The deleteReward, redeemReward, and unredeemReward functions are straightforward and follow consistent patterns.

src/utils/errors.ts (1)

73-116: Good improvement: accepting unknown for safer error handling.

This change correctly handles the TypeScript reality that catch blocks receive unknown values. The instanceof chain gracefully degrades from specific errors to generic Error to String fallback.

src/api/endpoints/chores.ts (3)

152-155: Verify: Using PUT for partial update vs PATCH in rewards.

This uses PUT for updating a chore, while updateReward in rewards.ts uses PATCH. PUT typically implies replacing the entire resource, while PATCH is for partial updates. If the API supports both, this may work, but consider aligning with PATCH for consistency and clearer semantics.


103-158: LGTM! Comprehensive update implementation with proper null handling.

The updateChore function correctly handles optional fields, nullable values for clearing, and category relationship updates (including unsetting via null).


160-168: LGTM! Simple and correct delete implementation.

src/tools/chores.ts (3)

4-4: LGTM! Import updated to include new API functions.


267-345: LGTM! Well-implemented update tool with proper validation.

The update_chore tool correctly handles:

  • Nullable fields (time, assignee can be set to null to clear)
  • Assignee resolution via findCategoryByName with appropriate error message
  • Date/time parsing with timezone support

347-381: LGTM! Clean delete implementation with helpful documentation.

The tool description includes a useful note about recurring chores, and the implementation follows established error-handling patterns.

src/api/types.ts (1)

207-334: LGTM! Type definitions are well-structured.

The new request/response type definitions follow consistent JSON:API patterns and use appropriate TypeScript constructs:

  • Create requests use required fields with optional extensions
  • Update requests use Partial<> for flexible updates
  • Response types correctly alias JsonApiResponse<Resource>
  • Naming is consistent across all resource types
src/api/endpoints/misc.ts (1)

1-47: LGTM! Clean implementation following established patterns.

The avatar and color endpoints are straightforward and consistent with other endpoint modules. The use of index signatures ([key: string]: unknown) in the attributes provides appropriate flexibility for API evolution.

src/api/endpoints/calendar.ts (1)

48-86: LGTM! Calendar CRUD operations implemented correctly.

The three new calendar event operations follow established patterns and maintain type safety. The implementation correctly uses:

  • POST for creation
  • PUT for updates (via client.request())
  • DELETE for deletion
CLAUDE.md (1)

1-83: LGTM! Documentation accurately reflects the expanded feature set.

The documentation updates clearly communicate:

  • The expanded tool count (12 → 35+)
  • Plus subscription requirements and detection
  • Clear categorization of base vs Plus-only tools
  • The new type generation workflow
src/api/client.ts (3)

12-15: LGTM! Well-designed subscription status type.

The union type appropriately models the possible subscription states, including null for when the status is unknown or not yet determined.


218-238: LGTM! Clean API for subscription status.

The three new methods provide a clear, type-safe interface for checking subscription status:

  • hasPlus() provides a convenient boolean check
  • getSubscriptionStatus() exposes the full status
  • initialize() enables pre-initialization to avoid lazy auth

250-259: LGTM! Useful helper for explicit initialization.

The initializeClient() function provides a convenient way to ensure the client is authenticated before use, which is helpful for the server startup flow.

src/tools/rewards.ts (5)

151-216: LGTM! Well-implemented create_reward tool.

The implementation correctly:

  • Validates required parameters (name, pointValue)
  • Resolves assignee names to category IDs using findCategoryByName
  • Provides clear error messages when family members aren't found
  • Returns actionable success messages with the created reward ID

218-269: LGTM! Clean update_reward implementation.

The conditional updates object construction (lines 245-250) is a clean pattern that only includes fields that were actually provided. Good use of Parameters<typeof updateReward>[1] for type inference.


271-305: LGTM! Simple and correct delete operation.

The delete tool appropriately warns users about permanent removal in the description.


307-356: LGTM! Redeem tool with good assignee resolution.

Correctly reuses the findCategoryByName pattern from create_reward to resolve the assignee parameter, maintaining consistency across tools.


358-392: LGTM! Complete unredeem implementation.

Provides the necessary undo functionality for reward redemptions.

src/api/endpoints/lists.ts (2)

70-120: LGTM! Complete list CRUD operations.

The three list operations are well-implemented with:

  • Appropriate parameter types and defaults (color defaults to null)
  • Consistent use of request/response types
  • Correct HTTP methods (POST, PUT, DELETE)
  • Type-safe request body construction

122-177: LGTM! Complete list item CRUD operations.

The list item operations mirror the list operations pattern effectively:

  • Clear parameter lists with optional fields
  • Flexible update mechanism via partial updates object
  • Proper path construction with both listId and itemId
  • Consistent null defaults for optional fields
src/tools/meals.ts (6)

19-55: LGTM! Clean meal categories retrieval.

Simple and effective tool for discovering meal category IDs needed for other meal operations.


57-133: LGTM! Recipe listing and detail tools implemented well.

Both get_recipes and get_recipe provide clear, structured output. The attribute iteration in get_recipe (lines 117-121) elegantly handles dynamic recipe fields.


135-240: LGTM! Complete recipe CRUD operations.

The create, update, and delete tools follow the same robust patterns established in the rewards module:

  • Conditional updates object construction
  • Clear success messages with IDs
  • Appropriate parameter validation
  • Permanent deletion warnings

242-276: LGTM! Useful grocery list integration.

The add_recipe_to_grocery_list tool provides helpful meal planning workflow integration.


278-339: LGTM! Thoughtful date range defaults.

Lines 299-300 provide sensible defaults:

  • Start: today
  • End: 7 days from start

This makes the tool easy to use for the common "what's for dinner this week?" query while still allowing custom ranges.


341-387: LGTM! Complete meal sitting creation.

The tool correctly:

  • Parses user-friendly date strings ("today", "tomorrow", or YYYY-MM-DD)
  • Handles timezone conversions via config
  • Links recipes to meal sittings via optional recipeId
src/tools/calendar.ts (6)

4-10: LGTM!

Import expansion correctly brings in the new CRUD endpoints from the calendar API module, aligning with the new tool implementations below.


99-99: Improved type safety.

Passing the raw error to formatErrorForMcp(error) is correct since the function accepts unknown and handles all error types gracefully.


162-162: Consistent error handling.

Same improvement as above—correctly leverages the unknown parameter type of formatErrorForMcp.


170-232: Well-structured tool implementation.

The tool correctly validates inputs via zod, documents parameters clearly, and handles errors consistently. The ISO format requirement for timestamps is documented in the schema descriptions.


264-275: Verify partial update handling.

The updates object is typed as Record<string, unknown>, which bypasses the UpdateCalendarEventRequest type. Ensure the API accepts partial updates with only the fields that changed, and consider using a stricter type if the request schema is known.


294-328: LGTM!

The delete tool is straightforward and follows the established pattern with proper error handling and user feedback.

src/tools/lists.ts (5)

4-15: LGTM!

Import expansion correctly brings in all required list API endpoints for the new CRUD operations.


112-112: Consistent error handling.

Correctly updated to pass raw error to formatErrorForMcp.


271-309: LGTM!

The create_list tool correctly validates inputs, creates the list via the API, and returns a clear confirmation message.


368-412: LGTM!

The delete_list tool correctly resolves the list, performs the deletion, and provides confirmation. Error handling is consistent.


414-468: LGTM!

Good UX with the default-to-grocery-list behavior, making it easy for users to add items without specifying a list. The confirmation message is informative.

src/api/endpoints/meals.ts (10)

1-12: LGTM!

The import and MealCategoryResource interface are well-defined. Using [key: string]: unknown provides flexibility for additional API fields.


14-42: LGTM!

Resource interfaces are well-structured with appropriate optional fields and relationship definitions.


44-61: LGTM!

Response interfaces correctly model JSON:API patterns with optional included arrays for sideloaded resources.


63-72: LGTM!

Simple and correct implementation for fetching meal categories.


74-100: LGTM!

Recipe fetching functions are well-implemented with sensible defaults for including related meal categories.


102-125: LGTM!

The createRecipe function correctly handles required and optional fields with appropriate defaults.


127-151: LGTM!

Partial update implementation is correct, only sending fields that were explicitly provided.


153-172: LGTM!

Both functions are implemented correctly. The addRecipeToGroceryList action endpoint follows REST conventions for resource actions.


174-195: LGTM!

The getMealSittings function correctly handles optional date range filtering.


197-223: LGTM!

The createMealSitting function correctly handles required and optional parameters for scheduling meals.

Comment thread .github/workflows/release.yml
Use version-sorted tag list instead of git describe HEAD^ for more
reliable changelog generation across different repository structures.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant