Skip to content

feat(examples): Add weather app example with Open-Meteo API integration - #81

Merged
gabrypavanello merged 4 commits into
mainfrom
claude/weather-app-example-0Cqai
Jan 10, 2026
Merged

feat(examples): Add weather app example with Open-Meteo API integration#81
gabrypavanello merged 4 commits into
mainfrom
claude/weather-app-example-0Cqai

Conversation

@gabrypavanello

Copy link
Copy Markdown
Contributor
  • Create new weather-app example using create-app CLI scaffolding
  • Implement three weather tools: getCurrentWeather, getForecast, getWeatherAlerts
  • Integrate Open-Meteo API (free, no API key required) with mock fallback
  • Add React UI components for displaying weather data, forecast, and alerts
  • Include comprehensive test suite (33 tests) using @mcp-apps-kit/testing
  • Support mock mode via USE_MOCK_WEATHER env variable for testing

- Create new weather-app example using create-app CLI scaffolding
- Implement three weather tools: getCurrentWeather, getForecast, getWeatherAlerts
- Integrate Open-Meteo API (free, no API key required) with mock fallback
- Add React UI components for displaying weather data, forecast, and alerts
- Include comprehensive test suite (33 tests) using @mcp-apps-kit/testing
- Support mock mode via USE_MOCK_WEATHER env variable for testing
@coderabbitai

coderabbitai Bot commented Jan 10, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Full Weather App example: current conditions, multi-day forecast, and alerts with real API plus robust mock fallback and a web UI.
  • Documentation

    • Comprehensive README covering features, tooling, development/build/test commands, mock mode, and API notes.
  • Tests

    • Integration and unit suites verifying service behavior, fallbacks, forecasts, alerts, and tool outputs.
  • Chores

    • Project configs, scripts, TypeScript/Vite/Vitest setup, UI entry, styles, and .gitignore entries added.

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

Walkthrough

Adds a complete Weather App example: an MCP server with three tools (current weather, forecast, alerts), a WeatherService integrating Open‑Meteo with mock fallbacks, a React UI, project configs (Vite/TS/Vitest), and unit/integration tests and docs.

Changes

Cohort / File(s) Summary
Project config & tooling
examples/weather-app/.gitignore, examples/weather-app/package.json, examples/weather-app/tsconfig.json, examples/weather-app/vitest.config.ts, examples/weather-app/ui/vite.config.ts
Add ignore rules, package manifest, TypeScript, Vitest and Vite configs, scripts, dependencies, and tooling settings.
Server & API service
examples/weather-app/server/index.ts, examples/weather-app/server/services/weatherService.ts
New MCP app exporting three tools and types; WeatherService with Open‑Meteo integration, Zod validation, mock data generators, and exported real/mock instances.
UI assets & entry
examples/weather-app/ui/index.html, examples/weather-app/ui/src/main.tsx, examples/weather-app/ui/src/App.tsx, examples/weather-app/ui/src/styles.css
Add HTML entry, React bootstrap, App component with display components and runtime type guards, and comprehensive light/dark styling.
Tests & test setup
examples/weather-app/tests/setup.ts, examples/weather-app/tests/unit/weatherService.test.ts, examples/weather-app/tests/integration/server.test.ts
Add Vitest setup, extensive unit tests for WeatherService (mock and network fallback cases), and integration tests exercising MCP server tools and structured outputs.
Documentation
examples/weather-app/README.md
New README documenting features, API usage, mock mode, development/build/test commands, and project layout.

Sequence Diagram(s)

sequenceDiagram
    participant UI as React UI
    participant App as MCP Server
    participant Service as WeatherService
    participant API as Open‑Meteo
    participant Mock as Mock Data

    UI->>App: invoke tool (e.g., getCurrentWeather(location))
    App->>Service: getCurrentWeather(location)
    Service->>API: geocodeLocation(query)
    alt Geocode success
        API-->>Service: coordinates
        Service->>API: fetchCurrentWeather(lat,lon)
        API-->>Service: weather payload
        Service-->>App: CurrentWeather (validated)
    else Geocode/fetch failure
        Service->>Mock: generateMockCurrentWeather()
        Mock-->>Service: mock CurrentWeather
        Service-->>App: CurrentWeather (mock)
    end
    App-->>UI: structuredContent (tool output)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main addition: a new weather app example with Open-Meteo API integration, which is the primary focus of all file changes.
Description check ✅ Passed The description is directly related to the changeset, detailing the weather tools, API integration, React UI components, test suite, and mock mode support across the added files.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8bb21ff and 19a3a04.

📒 Files selected for processing (1)
  • examples/weather-app/server/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/weather-app/server/index.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test-and-lint

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

@claude

claude Bot commented Jan 10, 2026

Copy link
Copy Markdown
Contributor

Code Review: Weather App Example

Summary

This PR adds a comprehensive weather application example demonstrating integration with the Open-Meteo API. The implementation is well-structured and follows the project's conventions. Overall, this is a high-quality contribution that serves as an excellent example for the framework.

✅ Strengths

Architecture & Design

  • Excellent separation of concerns: Weather service cleanly separated from MCP tool definitions
  • Smart fallback strategy: Gracefully handles API failures with mock data
  • Type safety: Strong TypeScript typing throughout with proper Zod schemas
  • Testability: Mock mode via environment variable makes testing straightforward
  • Framework patterns: Proper use of defineTool, defineUI, and colocated UI pattern

Code Quality

  • Clean error handling: Silent failures with fallbacks (appropriate for weather data)
  • Comprehensive testing: 33 tests covering unit, integration, and edge cases
  • Good documentation: README with clear setup instructions and API reference
  • Consistent styling: Dark mode support with CSS variables

MCP Apps Kit Usage

  • Demonstrates all key concepts: tools, UIs, handlers, schemas
  • Proper use of visibility: "both" and readOnlyHint annotation
  • Good example of React UI integration with hooks

🔧 Issues Found

Critical Issues

None found.

High Priority

1. Type duplication in UI component (ui/src/App.tsx:18-73)
The UI duplicates all type definitions that are already exported from the server. This creates maintenance burden and potential for drift.

Recommendation: Import types from server or create a shared types file:

import type { 
  CurrentWeather, 
  WeatherForecast, 
  WeatherAlertsResponse 
} from "../../server/services/weatherService.js";

2. Mock data always uses same coordinates (weatherService.ts:232-239)
All locations in mock mode return NYC coordinates (40.7128, -74.006). This could hide bugs where location-specific logic is needed.

Recommendation: Generate varied mock coordinates based on location name hash or use a small lookup table.

Medium Priority

3. Missing error handling for invalid location input
The service accepts any string as location without validation. Malformed input (empty strings, special characters) could cause issues.

Recommendation: Add input validation:

async getCurrentWeather(locationQuery: string): Promise<CurrentWeather> {
  if (!locationQuery?.trim()) {
    throw new AppError({ 
      code: ErrorCode.VALIDATION_ERROR, 
      message: "Location query cannot be empty" 
    });
  }
  // ... rest of implementation
}

4. Unconstrained random mock data (weatherService.ts:249-253)
Temperature ranges (5-35°C) and humidity (30-90%) work for most locations but could generate unrealistic combinations (e.g., 35°C with 90% humidity in Arctic).

Impact: Low - only affects testing/demo scenarios

5. Missing type guards for API responses (weatherService.ts:94-127)
The code uses type assertions without validating the API response structure. If Open-Meteo changes their API, silent failures could occur.

Recommendation: Use Zod to validate API responses:

const GeocodingResponseSchema = z.object({
  results: z.array(z.object({
    name: z.string(),
    latitude: z.number(),
    // ... rest of schema
  })).optional()
});

const data = GeocodingResponseSchema.parse(await response.json());

6. Race condition in mock alert IDs (weatherService.ts:321)
Using Date.now() for alert IDs could create duplicates if called in quick succession.

Recommendation: Use crypto.randomUUID() or add a counter.

7. Magic number in isDay calculation (weatherService.ts:257)
Hardcoded hours (6-20) don't account for seasonal variation or timezones.

Impact: Low - acceptable for mock data

Low Priority

8. Missing JSDoc comments
Public API methods lack documentation. While types are self-explanatory, adding JSDoc would improve the example quality.

9. Hard-coded port (server/index.ts:203)
Port 3005 is hardcoded. Consider using process.env.PORT || 3005.

10. CSS could use more modular approach
The 411-line CSS file could benefit from component-scoped styles or CSS modules for better maintainability.

🛡️ Security Considerations

No API keys required - Good choice using Open-Meteo
No user data stored
Read-only operations - Proper use of readOnlyHint
No SQL injection risk - No database usage
XSS protection - React handles escaping

Minor concern: URL construction uses string interpolation (weatherService.ts:96, 133, 180). While encodeURIComponent is used for the query, consider using URL constructor for robustness:

const url = new URL("https://geocoding-api.open-meteo.com/v1/search");
url.searchParams.set("name", query);
url.searchParams.set("count", "1");

📊 Test Coverage

Excellent coverage with 33 tests across:

  • Unit tests for service layer (mock mode, API fallback, edge cases)
  • Integration tests for MCP server (tool execution, schema validation)
  • Randomness testing for mock data variations

Minor gap: No tests for UI components. Consider adding React Testing Library tests for the display components.

🎯 Performance Considerations

Good: No expensive computations
Good: Efficient data structures
⚠️ Note: No request caching - each tool call hits the API. For production use, consider adding cache with TTL.

Example caching approach:

private cache = new Map<string, { data: CurrentWeather, expires: number }>();

async getCurrentWeather(location: string): Promise<CurrentWeather> {
  const cacheKey = `current:${location}`;
  const cached = this.cache.get(cacheKey);
  if (cached && Date.now() < cached.expires) {
    return cached.data;
  }
  // ... fetch and cache with 5-minute TTL
}

📝 Code Conventions

Follows Project Guidelines ✅

  • ✅ Strict TypeScript (no any types)
  • ✅ No unused variables
  • ✅ Proper use of defineTool and defineUI
  • ✅ Exports only in index.ts
  • ✅ Tests mirror source structure
  • ✅ Package naming convention

Minor Convention Issues

  • ⚠️ Empty catch blocks (weatherService.ts:125, 172, 226) - consider logging in dev mode
  • ⚠️ Could use export type for type-only exports (server/index.ts:194-199)

🚀 Suggested Enhancements (Optional)

  1. Add location search history - Demonstrate state persistence
  2. Add units toggle - Celsius/Fahrenheit conversion
  3. Add weather map widget - Demonstrate image handling
  4. Add comparison view - Show multiple locations side-by-side
  5. Add hourly forecast - Open-Meteo supports this

📋 Checklist Before Merge

Verdict

Recommendation: ✅ Approve with minor revisions

This is a well-crafted example that demonstrates the framework effectively. The high-priority issues are relatively minor and can be addressed quickly. The code is production-quality for an example application.

Estimated effort to address high-priority issues: ~30 minutes


Great work! This will be a valuable addition to the examples collection. 🎉

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @examples/weather-app/ui/index.html:
- Around line 1-12: The HTML file (contains the DOCTYPE, <div id="root"></div>,
and the <script type="module" src="/src/main.tsx"> entry) has formatting issues
flagged by the pipeline; run the formatter to fix and commit the changes by
running: prettier --write examples/weather-app/ui/index.html, then verify the
file is properly formatted and include the updated file in your commit/pull
request.

In @examples/weather-app/ui/src/App.tsx:
- Around line 18-73: These interfaces (Location, CurrentWeather, DailyForecast,
WeatherForecast, WeatherAlert, WeatherAlertsResponse) are duplicates; remove
their local declarations and import the named type exports from the server
module that defines them instead. Replace the duplicated interface blocks with a
single type-only import for those exact symbols at the top of the file, and
update any local references to use the imported types; ensure the import uses
type-only syntax where supported so you don't pull runtime code.

In @examples/weather-app/ui/src/styles.css:
- Around line 1-411: The CSS file has formatting issues; run the project's
Prettier formatting on examples/weather-app/ui/src/styles.css (e.g. run
"prettier --write examples/weather-app/ui/src/styles.css"), review the resulting
changes across selectors like :root, .weather-card, .forecast-day, .actions,
etc., stage and commit the formatted file, and push the updated commit so the
pipeline passes.
🧹 Nitpick comments (11)
examples/weather-app/README.md (1)

69-85: Add language identifier to fenced code block.

The project structure code block should have a language identifier for consistency. Use text or plaintext for directory trees.

📝 Suggested fix
-```
+```text
 weather-app/
 ├── server/
examples/weather-app/ui/index.html (1)

6-6: Consider a more user-friendly title.

The current title "weather-app" could be improved to "Weather App" for better user experience.

📝 Suggested improvement
-    <title>weather-app</title>
+    <title>Weather App</title>
examples/weather-app/tests/integration/server.test.ts (3)

17-18: Remove hardcoded delay or document its necessity.

The 100ms delay after starting the test server may cause flakiness or unnecessary slowdown. If the server needs time to initialize, consider using a readiness check instead.

♻️ Alternative approach

If the delay is truly necessary, document why:

    const server = await startTestServer(app, { port: 0 });
+    // Wait for server to fully initialize internal state
    await new Promise((r) => setTimeout(r, 100));

Or better yet, check if startTestServer already handles initialization:

    const server = await startTestServer(app, { port: 0 });
-    await new Promise((r) => setTimeout(r, 100));

78-84: Consider importing types instead of inline type assertions.

Throughout the test file, inline type assertions are used repeatedly. Consider importing the actual types from weatherService.ts to improve type safety and reduce duplication.

This pattern appears in lines 78-84, 101-104, 119-122, 135, 149-153, 169-171, 184-189, 205-209, 225-230, 246-250, and 266-276.

♻️ Refactoring suggestion

Add imports at the top:

import type { 
  CurrentWeather, 
  WeatherForecast, 
  WeatherAlertsResponse 
} from "../../server/services/weatherService.js";

Then simplify assertions:

-      const content = result.structuredContent as {
-        location?: { name?: string };
-        temperature?: number;
-        humidity?: number;
-        description?: string;
-        icon?: string;
-      };
+      const content = result.structuredContent as CurrentWeather;

258-294: Test for alerts has low assertion value.

The test runs up to 10 iterations to potentially find alerts, but ultimately accepts no alerts as valid (line 293: expect(true).toBe(true)). This makes the test less valuable.

Consider either:

  1. Using a deterministic mock that always returns alerts for testing
  2. Testing the no-alerts case explicitly in a separate test
  3. Removing the loop and accepting that alerts may not always be present
♻️ Suggested improvement
-    it("should return valid alert structure when alerts exist", async () => {
-      // Run multiple times to get alerts (random in mock)
-      let alertsFound = false;
-      for (let i = 0; i < 10 && !alertsFound; i++) {
-        const result = await env.client.callTool("getWeatherAlerts", {
-          location: "Test City",
-        });
-
-        const content = result.structuredContent as {
-          alerts?: Array<{
-            id?: string;
-            type?: string;
-            severity?: string;
-            headline?: string;
-            description?: string;
-            startTime?: string;
-            endTime?: string;
-          }>;
-        };
-
-        if (content.alerts && content.alerts.length > 0) {
-          alertsFound = true;
-          const alert = content.alerts[0];
-
-          expect(alert.id).toBeDefined();
-          expect(["warning", "watch", "advisory"]).toContain(alert.type);
-          expect(["minor", "moderate", "severe", "extreme"]).toContain(alert.severity);
-          expect(alert.headline).toBeTruthy();
-          expect(alert.description).toBeTruthy();
-          expect(alert.startTime).toBeDefined();
-          expect(alert.endTime).toBeDefined();
-        }
-      }
-
-      // It's ok if no alerts were found in mock mode
-      expect(true).toBe(true);
-    });
+    it("should return valid alert structure when present", async () => {
+      const result = await env.client.callTool("getWeatherAlerts", {
+        location: "Test City",
+      });
+
+      expectToolResult(result).toHaveNoError();
+      const content = result.structuredContent as WeatherAlertsResponse;
+      
+      // Verify structure regardless of whether alerts exist
+      expect(Array.isArray(content.alerts)).toBe(true);
+      
+      // If alerts exist, validate their structure
+      if (content.alerts.length > 0) {
+        const alert = content.alerts[0];
+        expect(alert.id).toBeDefined();
+        expect(["warning", "watch", "advisory"]).toContain(alert.type);
+        expect(["minor", "moderate", "severe", "extreme"]).toContain(alert.severity);
+        expect(alert.headline).toBeTruthy();
+        expect(alert.description).toBeTruthy();
+        expect(alert.startTime).toBeDefined();
+        expect(alert.endTime).toBeDefined();
+      }
+    });
examples/weather-app/ui/src/App.tsx (2)

168-173: Add explicit typing for severityColors.

The severityColors object would benefit from explicit typing for better type safety.

♻️ Suggested improvement
-  const severityColors: Record<string, string> = {
+  const severityColors: Record<WeatherAlert["severity"], string> = {
     minor: "#ffc107",
     moderate: "#fd7e14",
     severe: "#dc3545",
     extreme: "#6f42c1",
   };

218-238: Type guards could be more robust.

The type guard functions use minimal checks that could result in false positives. Consider more thorough validation.

For example, isCurrentWeather only checks for temperature and humidity, but other objects could have these properties. Similar issues exist in isForecast and isAlerts.

♻️ More robust type guards
 function isCurrentWeather(data: unknown): data is CurrentWeather {
-  return !!data && typeof data === "object" && "temperature" in data && "humidity" in data;
+  return (
+    !!data &&
+    typeof data === "object" &&
+    "temperature" in data &&
+    "humidity" in data &&
+    "location" in data &&
+    !("daily" in data) &&
+    !("alerts" in data)
+  );
 }

 function isForecast(data: unknown): data is WeatherForecast {
   return (
     !!data &&
     typeof data === "object" &&
     "daily" in data &&
-    Array.isArray((data as WeatherForecast).daily)
+    Array.isArray((data as WeatherForecast).daily) &&
+    "location" in data &&
+    !("temperature" in data) &&
+    !("alerts" in data)
   );
 }

 function isAlerts(data: unknown): data is WeatherAlertsResponse {
   return (
     !!data &&
     typeof data === "object" &&
     "alerts" in data &&
-    Array.isArray((data as WeatherAlertsResponse).alerts)
+    Array.isArray((data as WeatherAlertsResponse).alerts) &&
+    "location" in data &&
+    !("temperature" in data) &&
+    !("daily" in data)
   );
 }
examples/weather-app/tests/unit/weatherService.test.ts (2)

120-144: Test may pass without actually verifying alert structure.

If no alerts are generated within 20 iterations (possible with random behavior), alertFound remains false and the test passes without validating the alert structure. Consider adding an assertion or increasing iterations to ensure meaningful coverage.

♻️ Suggested improvement
       }
     }
+    // Ensure we actually tested an alert structure
+    expect(alertFound).toBe(true);
   });

239-248: Consider using Array.some() for cleaner emoji check.

The manual loop can be simplified using array methods.

♻️ Suggested simplification
       // Should have weather-related emojis
       const weatherEmojis = ["☀️", "🌤️", "⛅", "☁️", "🌧️", "🌦️"];
-      let foundWeatherEmoji = false;
-      for (const icon of icons) {
-        if (weatherEmojis.includes(icon)) {
-          foundWeatherEmoji = true;
-          break;
-        }
-      }
-      expect(foundWeatherEmoji).toBe(true);
+      const foundWeatherEmoji = [...icons].some((icon) => weatherEmojis.includes(icon));
+      expect(foundWeatherEmoji).toBe(true);
examples/weather-app/server/index.ts (1)

10-15: Consider separating type-only imports.

Per coding guidelines, type-only exports should use export type. The same principle applies to imports—separating type imports improves tree-shaking and clarity.

♻️ Suggested change
-import {
-  WeatherService,
-  type CurrentWeather,
-  type WeatherForecast,
-  type WeatherAlertsResponse,
-} from "./services/weatherService.js";
+import { WeatherService } from "./services/weatherService.js";
+import type {
+  CurrentWeather,
+  WeatherForecast,
+  WeatherAlertsResponse,
+} from "./services/weatherService.js";
examples/weather-app/server/services/weatherService.ts (1)

93-128: Consider adding a timeout to fetch requests.

The fetch calls don't have a timeout, which could cause requests to hang indefinitely if the Open-Meteo API is unresponsive. While the fallback to mock data provides resilience, a timeout would improve user experience.

♻️ Suggested approach
// Using AbortController for timeout
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);

try {
  const response = await fetch(url, { signal: controller.signal });
  // ...
} finally {
  clearTimeout(timeoutId);
}

This pattern could be applied to all three fetch locations: geocodeLocation, fetchCurrentWeather, and fetchForecast.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd806fe and d5aa2ac.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • examples/weather-app/.gitignore
  • examples/weather-app/README.md
  • examples/weather-app/package.json
  • examples/weather-app/server/index.ts
  • examples/weather-app/server/services/weatherService.ts
  • examples/weather-app/tests/integration/server.test.ts
  • examples/weather-app/tests/setup.ts
  • examples/weather-app/tests/unit/weatherService.test.ts
  • examples/weather-app/tsconfig.json
  • examples/weather-app/ui/index.html
  • examples/weather-app/ui/src/App.tsx
  • examples/weather-app/ui/src/main.tsx
  • examples/weather-app/ui/src/styles.css
  • examples/weather-app/ui/vite.config.ts
  • examples/weather-app/vitest.config.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Use Strict TypeScript mode: no any types, use unknown and narrow types instead
Remove all unused variables or prefix them with underscore (_)
Use export type for type-only exports

Files:

  • examples/weather-app/ui/vite.config.ts
  • examples/weather-app/tests/setup.ts
  • examples/weather-app/tests/unit/weatherService.test.ts
  • examples/weather-app/server/index.ts
  • examples/weather-app/tests/integration/server.test.ts
  • examples/weather-app/server/services/weatherService.ts
  • examples/weather-app/vitest.config.ts
**/tests/**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files in tests/ directory mirroring source structure with subdirectories for unit/, integration/, and contract/ tests

Files:

  • examples/weather-app/tests/unit/weatherService.test.ts
  • examples/weather-app/tests/integration/server.test.ts
**/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export public API only through index.ts files

Files:

  • examples/weather-app/server/index.ts
🧠 Learnings (4)
📚 Learning: 2026-01-09T14:18:43.516Z
Learnt from: CR
Repo: AndurilCode/mcp-apps-kit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T14:18:43.516Z
Learning: Applies to **/tests/**/*.test.ts : Place test files in `tests/` directory mirroring source structure with subdirectories for unit/, integration/, and contract/ tests

Applied to files:

  • examples/weather-app/tests/setup.ts
  • examples/weather-app/tests/unit/weatherService.test.ts
  • examples/weather-app/tests/integration/server.test.ts
  • examples/weather-app/vitest.config.ts
📚 Learning: 2026-01-09T14:18:43.516Z
Learnt from: CR
Repo: AndurilCode/mcp-apps-kit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T14:18:43.516Z
Learning: Applies to **/*.ts : Use `export type` for type-only exports

Applied to files:

  • examples/weather-app/tsconfig.json
📚 Learning: 2026-01-09T14:18:43.516Z
Learnt from: CR
Repo: AndurilCode/mcp-apps-kit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T14:18:43.516Z
Learning: Applies to **/*.ts : Use Strict TypeScript mode: no `any` types, use `unknown` and narrow types instead

Applied to files:

  • examples/weather-app/tsconfig.json
📚 Learning: 2026-01-09T14:18:43.516Z
Learnt from: CR
Repo: AndurilCode/mcp-apps-kit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T14:18:43.516Z
Learning: Applies to packages/core/**/*.ts : Always use `defineTool` and `defineUI` for type inference when creating tools and UI widgets

Applied to files:

  • examples/weather-app/tsconfig.json
🧬 Code graph analysis (4)
examples/weather-app/ui/src/main.tsx (1)
examples/weather-app/ui/src/App.tsx (1)
  • App (240-309)
examples/weather-app/ui/src/App.tsx (2)
examples/weather-app/server/services/weatherService.ts (6)
  • Location (36-42)
  • CurrentWeather (44-56)
  • DailyForecast (58-69)
  • WeatherForecast (71-75)
  • WeatherAlert (77-85)
  • WeatherAlertsResponse (87-91)
examples/weather-app/server/index.ts (1)
  • AppTools (199-199)
examples/weather-app/tests/setup.ts (1)
packages/testing/src/adapters/vitest.ts (1)
  • setupVitestMatchers (28-46)
examples/weather-app/tests/unit/weatherService.test.ts (1)
examples/weather-app/server/services/weatherService.ts (2)
  • WeatherService (344-416)
  • mockWeatherService (422-422)
🪛 GitHub Actions: PR Check
examples/weather-app/ui/index.html

[error] 1-1: Prettier formatting check reported formatting issues. Run 'prettier --write' to fix code style issues in this file.

examples/weather-app/ui/src/styles.css

[error] 1-1: Prettier formatting check reported formatting issues. Run 'prettier --write' to fix code style issues in this file.

🪛 LanguageTool
examples/weather-app/README.md

[grammar] ~61-~61: Ensure spelling is correct
Context: ...ndpoint: http://localhost:3005/mcp Stdio mode (for hosts that support it): ``...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
examples/weather-app/README.md

69-69: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: claude-review
🔇 Additional comments (20)
examples/weather-app/.gitignore (1)

1-6: LGTM!

Standard and appropriate ignore patterns for a Node.js/TypeScript project with build artifacts and environment files correctly excluded.

examples/weather-app/tsconfig.json (1)

1-15: LGTM!

Well-configured TypeScript setup with strict: true enabled as per coding guidelines. The include patterns correctly cover both server and UI source directories.

examples/weather-app/vitest.config.ts (1)

1-12: LGTM!

Clean Vitest configuration with appropriate test patterns and timeouts suitable for integration tests that may involve API calls.

examples/weather-app/tests/setup.ts (1)

1-7: LGTM!

Correctly initializes MCP Apps Kit testing matchers for use in unit and integration tests.

examples/weather-app/package.json (1)

1-25: LGTM!

Well-structured package configuration with appropriate scripts for development, build, and testing workflows. The workspace dependencies and ESM module type are correctly configured for this monorepo example.

examples/weather-app/README.md (1)

1-66: LGTM!

Excellent documentation covering features, usage, development workflow, mock mode, and project structure. Clear and helpful for developers exploring this example.

examples/weather-app/ui/vite.config.ts (1)

1-12: LGTM!

Clean Vite configuration with React plugin and viteSingleFile for generating a bundled single-file output, which is appropriate for MCP app embedded UIs. The root path is correctly set since the config is referenced from the project root via --config ui/vite.config.ts.

examples/weather-app/ui/src/main.tsx (1)

1-20: LGTM! Clean React 18 entry point.

The implementation correctly validates the root element, uses React 18's createRoot, and properly wraps the application with StrictMode and AppsProvider.

examples/weather-app/ui/src/App.tsx (1)

240-309: LGTM! Well-structured main component.

The App component properly integrates MCP client hooks, applies theming, handles multiple data types gracefully with appropriate fallbacks, and provides user actions for follow-up interactions.

examples/weather-app/tests/unit/weatherService.test.ts (2)

1-7: LGTM! Test file structure and imports look correct.

The test file is correctly placed in tests/unit/ following the coding guidelines, and imports are properly structured with .js extension for ESM compatibility.


177-226: LGTM! Fallback behavior tests are well-structured.

Good coverage of different failure scenarios: HTTP errors (500), network failures, and empty geocoding results. The mock setup correctly tests that the service gracefully falls back to mock data.

examples/weather-app/server/index.ts (3)

44-110: LGTM! Schema definitions are well-structured.

The Zod schemas include helpful descriptions and properly constrain enum values. They align well with the TypeScript interfaces defined in the service.


121-185: LGTM! Tool definitions are comprehensive and well-documented.

The tools have clear descriptions, proper input validation with Zod schemas, and appropriate readOnlyHint annotations. Handler implementations correctly delegate to the WeatherService.


188-208: LGTM! Export structure and startup logic are well-designed.

The type exports follow coding guidelines with export type. The conditional startup based on NODE_ENV ensures the server doesn't start during tests, and the named app export enables integration testing.

examples/weather-app/server/services/weatherService.ts (6)

36-91: LGTM! Interface definitions are clean and well-typed.

The interfaces properly use optional fields, union types for constrained values, and are all exported for use by consumers.


130-175: LGTM! Current weather fetch is well-implemented.

The function properly handles API failures by returning null, which the caller converts to mock data. The inline type assertion for the API response is appropriate for external API integration.


177-229: LGTM! Forecast fetch implementation is solid.

The function cleanly transforms the Open-Meteo API response into the DailyForecast[] structure, with proper fallback for unknown weather codes.


242-260: LGTM! Mock weather generator produces valid data.

The random ranges for temperature, humidity, wind speed, and direction are realistic. The weather code selection from the hardcoded array ensures valid icons and descriptions.


343-416: LGTM! WeatherService class is well-designed with proper fallbacks.

The class implements a clean fallback pattern: mock mode → real API → mock fallback on failure. The safeDays clamping for forecasts is good defensive programming, and the comment explaining why alerts always use mock data (Open-Meteo limitation) is helpful.


418-422: LGTM! Exported instances support both production and testing scenarios.

Providing both a default instance and an explicit mock instance is a good pattern for the example app, supporting easy testing and demonstration.

Comment thread examples/weather-app/ui/index.html Outdated
Comment thread examples/weather-app/ui/src/App.tsx Outdated
Comment thread examples/weather-app/ui/src/styles.css

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d5aa2ac70f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +13 to +15
beforeAll(async () => {
// Set mock mode for predictable tests
process.env.USE_MOCK_WEATHER = "true";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Set USE_MOCK_WEATHER before importing the app

Setting USE_MOCK_WEATHER in beforeAll happens after app has already been imported, but the server module reads process.env.USE_MOCK_WEATHER at import time when it constructs weatherService. That means these tests will still use the real Open‑Meteo API whenever the env var wasn’t set before module load, making the suite network‑dependent and flaky in offline CI. Consider setting the env var before importing app (e.g., via a dynamic import after the env assignment) or injecting a mock service into the app for tests.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
examples/weather-app/ui/index.html (1)

1-12: Optional: add color-scheme / theme-color metadata (and a nicer title) for better UA integration

This app supports light/dark themes in CSS; declaring it in HTML helps browsers pick appropriate default form-control styling and improves theming on some platforms.

Proposed tweak
   <head>
     <meta charset="UTF-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>weather-app</title>
+    <meta name="color-scheme" content="light dark" />
+    <meta name="theme-color" content="#f5f7fa" />
+    <title>Weather App</title>
   </head>
examples/weather-app/ui/src/styles.css (3)

46-53: Optional: consider 100dvh fallback for mobile browser UI chrome

100vh can be slightly off on mobile when the address bar collapses/expands.

Proposed tweak
 .container {
   max-width: 480px;
   margin: 0 auto;
   padding: 20px;
-  min-height: 100vh;
+  min-height: 100vh;
+  min-height: 100dvh;
   display: flex;
   flex-direction: column;
 }

307-321: Add prefers-reduced-motion handling for the loading animation

This avoids forcing motion/animation for users who opt out at the OS level.

Proposed tweak
 @keyframes float {
   0%,
   100% {
     transform: translateY(0);
   }
   50% {
     transform: translateY(-10px);
   }
 }
+
+@media (prefers-reduced-motion: reduce) {
+  .loading-icon {
+    animation: none;
+  }
+}

357-378: Add explicit .button:focus-visible styling (keyboard accessibility)

Right now focus indication depends on browser defaults; adding a clear focus ring makes it consistent.

Proposed tweak
 .button:active {
   transform: scale(0.98);
 }
+
+.button:focus-visible {
+  outline: 2px solid var(--accent-hover);
+  outline-offset: 2px;
+}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5aa2ac and 2c392a2.

📒 Files selected for processing (2)
  • examples/weather-app/ui/index.html
  • examples/weather-app/ui/src/styles.css
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: claude-review
  • GitHub Check: test-and-lint

@claude

claude Bot commented Jan 10, 2026

Copy link
Copy Markdown
Contributor

Code Review: Weather App Example

Summary

This PR adds a well-structured weather app example that demonstrates the @mcp-apps-kit framework. The implementation follows best practices with comprehensive testing, proper error handling, and a clean separation of concerns. Overall, this is a high-quality contribution.


Strengths

1. Architecture & Code Quality

  • Excellent separation of concerns: Service layer (weatherService.ts) cleanly separated from server logic
  • Type safety: Proper TypeScript usage with no any types, strict Zod schemas for validation
  • Error handling: Graceful fallback from real API to mock data when API fails
  • Testing: Comprehensive test suite (33 tests) covering both unit and integration scenarios
  • Follows framework patterns: Proper use of defineTool, defineUI, and colocated UI pattern

2. API Integration

  • Smart design: Uses free Open-Meteo API (no authentication required)
  • Resilient: Automatic fallback to mock data when API is unavailable
  • Configurable: USE_MOCK_WEATHER environment variable for testing
  • Proper data mapping: Weather codes correctly mapped to descriptions and icons

3. UI Components

  • Well-structured React components: Separate components for current weather, forecast, and alerts
  • Type guards: Proper runtime type checking (isCurrentWeather, isForecast, isAlerts)
  • Good UX: Follow-up action buttons for user engagement
  • Theme-aware: Uses useDocumentTheme and useHostStyleVariables

4. Documentation

  • Clear README: Installation, usage, testing, and API reference
  • Inline comments: Good explanatory comments throughout code
  • Examples: Shows both HTTP and stdio connection modes

Issues & Recommendations

Critical Issues

None found

Medium Priority

1. Type Duplication (weatherService.ts, ui/src/App.tsx:18-73)
The same interfaces are defined in both the service and UI components. Recommendation: Export types from server/index.ts to maintain single source of truth.

2. Mock Alert Generation (weatherService.ts:321)
Using Date.now() in a loop could create ID collisions. Recommendation: Use crypto.randomUUID() for guaranteed unique IDs.

3. Empty Catch Blocks (weatherService.ts:94-128, 131-175, 178-229)
Multiple catch blocks silently swallow errors. Recommendation: Add optional logging for debugging in development mode.

Low Priority

4. Magic Numbers (ui/src/App.tsx:85)
The value 45 (degrees per wind direction) should be a named constant for clarity.

5. Mock Location Hardcoded (weatherService.ts:232-240)
All mock locations return New York coordinates. Could vary by location name for better demo experience.

6. Redundant Validation (weatherService.ts:378)
The days parameter is validated in both Zod schema and service code. Zod validation should be sufficient.


Security Review

No security concerns:

  • No secrets or API keys required
  • Proper input sanitization via encodeURIComponent
  • Read-only operations (readOnlyHint: true)
  • No SQL injection, XSS, or command injection vectors

Performance Considerations

Generally good:

  • Fetch requests are properly async/await
  • Consider adding request timeout to prevent hanging on slow API responses

Test Coverage

Excellent coverage (33 tests):

  • Unit tests cover mock mode thoroughly
  • Integration tests verify tool contracts
  • Edge cases tested (day limits, data validation)

Minor gap: Real API mode not tested (understandable since it requires network access)


Checklist Compliance

Requirement Status Notes
Strict TypeScript Pass No any types
No unused variables Pass Clean code
80% test coverage Pass 33 comprehensive tests
Tool definition pattern Pass Proper defineTool usage
Error handling Pass Graceful fallbacks
Documentation Pass Complete README

Recommendations Summary

Before Merge:

  1. Consider deduplicating type definitions between server and UI
  2. Add optional error logging in catch blocks for debugging

Post-Merge (Optional):
3. Add request timeouts to API calls
4. Use crypto.randomUUID() for alert IDs
5. Extract magic numbers to named constants


Final Verdict

APPROVE WITH MINOR SUGGESTIONS

This is a well-crafted example that demonstrates best practices for building MCP apps. The code is production-quality with excellent test coverage. The suggestions above are minor improvements that do not block merging.

Great work!


Reviewed using repository CLAUDE.md guidelines

- Fix type duplication: UI now imports types from server instead of duplicating
- Add varied mock coordinates with MOCK_LOCATIONS lookup table and hash-based fallback
- Add input validation for empty location queries
- Add Zod validation for Open-Meteo API responses
- Use crypto.randomUUID() for alert IDs to prevent race conditions
- Use URL constructor for cleaner API URL building
- Use env variable for port configuration
- Use 'export type' for type-only exports
- Add JSDoc documentation for public functions
- Update tests to verify varied coordinates behavior
@claude

claude Bot commented Jan 10, 2026

Copy link
Copy Markdown
Contributor

Code Review: Weather App Example

Thank you for this comprehensive weather app example! This is a well-structured addition that demonstrates MCP Apps Kit capabilities effectively. Here's my detailed review:

✅ Strengths

Architecture & Design

  • Excellent separation of concerns: Clean separation between server logic, UI components, and weather service
  • Robust fallback mechanism: Graceful degradation from real API → mock data prevents failures
  • Type safety: Comprehensive TypeScript types with proper Zod schema validation
  • Colocated UI pattern: Correctly uses defineUI and links to tools, following framework conventions
  • Mock mode support: USE_MOCK_WEATHER env var enables predictable testing

Code Quality

  • Comprehensive test coverage: 33 tests covering unit and integration scenarios
  • API response validation: Uses Zod schemas to validate external API responses (safeParse)
  • Error handling: Proper try-catch blocks with null returns for fallback logic
  • Documentation: Clear JSDoc comments and helpful README
  • Consistent with framework: Follows all MCP Apps Kit patterns from CLAUDE.md

Testing

  • Unit tests: Thorough coverage of WeatherService in both mock and real modes
  • Integration tests: Tests all three tools via MCP protocol
  • Edge cases: Tests invalid inputs, API failures, and boundary conditions
  • Deterministic mocks: Hash-based coordinate generation ensures consistent test results

🔍 Issues Found

1. Security: Unvalidated External API Data (Medium Severity)

Location: server/services/weatherService.ts:192-203

The code uses safeParse but doesn't handle partial validation failures gracefully:

const parseResult = CurrentWeatherApiSchema.safeParse(rawData);
if (\!parseResult.success) {
  return null;  // ⚠️ Silently fails on malformed data
}

Recommendation: Log validation errors for debugging:

if (\!parseResult.success) {
  console.warn('Weather API validation failed:', parseResult.error);
  return null;
}

2. Potential Bug: Inconsistent Hash Algorithm (Low Severity)

Location: server/services/weatherService.ts:300-308

The hashCode function uses hash & hash which is a no-op:

hash = hash & hash; // Convert to 32bit integer

Issue: This doesn't actually convert to 32-bit. Should be:

hash = hash | 0; // Convert to 32bit integer

3. Performance: Unnecessary Re-renders (Low Severity)

Location: ui/src/App.tsx:181-218

The result extraction logic runs on every render:

const rawResult = result?.getCurrentWeather ?? result?.getForecast ?? result?.getWeatherAlerts ?? result;

Recommendation: Memoize this with useMemo to avoid redundant type checks.

4. Type Safety: Loose Type Guards (Low Severity)

Location: ui/src/App.tsx:158-178

Type guards check for property existence but don't validate structure:

function isCurrentWeather(data: unknown): data is CurrentWeather {
  return \!\!data && typeof data === 'object' && 'temperature' in data && 'humidity' in data;
}

Recommendation: Consider using Zod schemas for runtime validation or more specific checks.

5. Missing Input Validation (Low Severity)

Location: server/index.ts:138-140

Tool handlers don't validate the location string format before processing:

handler: async ({ location }): Promise<CurrentWeather> => {
  return await weatherService.getCurrentWeather(location);
}

Recommendation: Add basic validation (e.g., max length, allowed characters) to prevent abuse.

📋 Minor Suggestions

Code Style

  1. Consistent error messages: Some use "Location query cannot be empty" while others are silent
  2. Magic numbers: Extract constants like latRange = 140, lonRange = 360 with explanatory comments
  3. Unused client variable: ui/src/App.tsx:181 - client is defined but only used in button handlers

Documentation

  1. README: Consider adding troubleshooting section for API failures
  2. Comments: Add note about Open-Meteo API rate limits (currently says "no rate limits")
  3. Mock data disclaimer: Clarify that weather alerts are always mock data

Testing

  1. Missing tests: No UI component tests (consider adding React Testing Library tests)
  2. Network timeouts: Integration tests don't test timeout scenarios
  3. Timezone handling: No tests for different timezone edge cases

🎯 Recommendations

High Priority

  1. Fix the hash function bug (hash & hashhash | 0)
  2. Add validation error logging for debugging

Medium Priority

  1. Add input validation/sanitization for location strings
  2. Memoize result extraction in UI component
  3. Add UI component tests

Low Priority

  1. Extract magic numbers to named constants
  2. Strengthen type guards with Zod validation
  3. Add troubleshooting documentation

📊 Summary

Code Quality: ⭐⭐⭐⭐⭐ (5/5)
Test Coverage: ⭐⭐⭐⭐⭐ (5/5)
Documentation: ⭐⭐⭐⭐☆ (4/5)
Security: ⭐⭐⭐⭐☆ (4/5)
Performance: ⭐⭐⭐⭐☆ (4/5)

Overall: ⭐⭐⭐⭐⭐ (4.6/5)

This is an excellent example that demonstrates best practices for MCP Apps Kit. The issues found are minor and don't block merging. The code is production-ready with the suggested improvements being nice-to-haves rather than requirements.

Great work! 🎉


Review generated by Claude Code - feedback welcome!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @examples/weather-app/server/index.ts:
- Around line 197-201: The PORT parsing can yield NaN for non-numeric strings;
validate process.env.PORT after parseInt and before calling app.start so you
never pass NaN to app.start({ port }). In the NODE_ENV !== "test" block, check
the parsed port (the local variable port) with Number.isInteger/Number.isFinite
or isNaN, and if invalid either fall back to the default (e.g., 3005) or log an
error and exit; ensure any chosen behavior logs the invalid process.env.PORT
value to aid debugging before calling app.start({ port }).
🧹 Nitpick comments (2)
examples/weather-app/ui/src/App.tsx (1)

158-178: Strengthen type guards with additional field checks.

The type guards perform shallow checks (1-2 fields only), which could lead to false positives if the data shape is similar but incomplete. Since the server is the controlled source, the risk is moderate, but adding more discriminating checks would improve runtime safety.

♻️ Suggested improvements to type guards
 function isCurrentWeather(data: unknown): data is CurrentWeather {
-  return !!data && typeof data === "object" && "temperature" in data && "humidity" in data;
+  return (
+    !!data &&
+    typeof data === "object" &&
+    "location" in data &&
+    "temperature" in data &&
+    "humidity" in data &&
+    "timestamp" in data &&
+    "icon" in data
+  );
 }

 function isForecast(data: unknown): data is WeatherForecast {
   return (
     !!data &&
     typeof data === "object" &&
+    "location" in data &&
     "daily" in data &&
-    Array.isArray((data as WeatherForecast).daily)
+    Array.isArray((data as WeatherForecast).daily) &&
+    "generatedAt" in data
   );
 }

 function isAlerts(data: unknown): data is WeatherAlertsResponse {
   return (
     !!data &&
     typeof data === "object" &&
+    "location" in data &&
     "alerts" in data &&
-    Array.isArray((data as WeatherAlertsResponse).alerts)
+    Array.isArray((data as WeatherAlertsResponse).alerts) &&
+    "lastChecked" in data
   );
 }
examples/weather-app/server/services/weatherService.ts (1)

138-280: Consider logging errors in API fetch helpers for debugging.

The three API fetch helpers (geocodeLocation, fetchCurrentWeather, fetchForecast) intentionally return null on errors to enable fallback to mock data. However, the empty catch blocks silently swallow all errors, which could make debugging API issues difficult in production.

🔍 Suggested error logging

Add error logging in the catch blocks (example for geocodeLocation, apply similarly to the other two):

   } catch (error) {
+    // Log error for debugging, but return null to trigger fallback
+    if (process.env.NODE_ENV !== 'test') {
+      console.error('Geocoding API error:', error);
+    }
     return null;
   }

This preserves the fallback behavior while improving observability in production.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c392a2 and 8bb21ff.

📒 Files selected for processing (4)
  • examples/weather-app/server/index.ts
  • examples/weather-app/server/services/weatherService.ts
  • examples/weather-app/tests/unit/weatherService.test.ts
  • examples/weather-app/ui/src/App.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/weather-app/tests/unit/weatherService.test.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Use Strict TypeScript mode: no any types, use unknown and narrow types instead
Remove all unused variables or prefix them with underscore (_)
Use export type for type-only exports

Files:

  • examples/weather-app/server/index.ts
  • examples/weather-app/server/services/weatherService.ts
**/index.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Export public API only through index.ts files

Files:

  • examples/weather-app/server/index.ts
🧠 Learnings (1)
📚 Learning: 2026-01-09T14:18:43.516Z
Learnt from: CR
Repo: AndurilCode/mcp-apps-kit PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-09T14:18:43.516Z
Learning: Applies to packages/core/**/*.ts : Always use `defineTool` and `defineUI` for type inference when creating tools and UI widgets

Applied to files:

  • examples/weather-app/ui/src/App.tsx
🧬 Code graph analysis (2)
examples/weather-app/server/index.ts (1)
examples/weather-app/server/services/weatherService.ts (5)
  • weatherService (562-562)
  • WeatherService (456-559)
  • CurrentWeather (86-98)
  • WeatherForecast (113-117)
  • WeatherAlertsResponse (129-133)
examples/weather-app/ui/src/App.tsx (2)
examples/weather-app/server/services/weatherService.ts (3)
  • CurrentWeather (86-98)
  • WeatherForecast (113-117)
  • WeatherAlertsResponse (129-133)
examples/weather-app/server/index.ts (1)
  • AppTools (194-194)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: test-and-lint
  • GitHub Check: claude-review
🔇 Additional comments (15)
examples/weather-app/ui/src/App.tsx (3)

1-20: LGTM! Clean imports with proper type-only imports.

The use of import type for type-only imports is correct and aligns with strict TypeScript practices. Importing types directly from the server modules ensures type consistency between UI and server.


23-155: LGTM! Well-structured display components.

The three display components (CurrentWeatherDisplay, ForecastDisplay, AlertsDisplay) are clean, focused presentational components with appropriate formatting and styling.


180-249: LGTM! Solid App component implementation.

The main App component correctly wires MCP Apps Kit hooks, handles both wrapped and unwrapped result formats, provides appropriate waiting/error states, and enables user interactions via follow-up message buttons.

examples/weather-app/server/index.ts (5)

1-20: LGTM! Clean imports and service initialization.

The inline type syntax for type-only imports is correct, and the WeatherService initialization with environment-based mock mode is appropriate for testing and development scenarios.


22-42: LGTM! UI definitions correctly point to single bundle.

All three UI widgets appropriately reference the same HTML bundle, which aligns with the App component's type-based rendering approach.


44-110: LGTM! Comprehensive Zod schemas with proper validation.

The output schemas are well-structured, comprehensive, and correctly use Zod 4 features including enums, nested objects, arrays, and descriptions for AI tool usage.


112-186: LGTM! Well-defined MCP tools with proper schemas and handlers.

The three tools are correctly structured with comprehensive input/output schemas, appropriate visibility and annotations, and handlers that properly delegate to the WeatherService. The readOnlyHint annotation correctly indicates these are read-only operations.


189-204: LGTM! Proper type exports and server startup logic.

The type exports correctly use export type for type-only exports (compliant with coding guidelines), and the conditional server startup appropriately skips in test environments while exporting the app for testing purposes.

examples/weather-app/server/services/weatherService.ts (7)

1-37: LGTM! Comprehensive weather code mappings.

The use of crypto.randomUUID() for generating alert IDs is appropriate, and the WEATHER_CODES mapping provides comprehensive coverage of Open-Meteo weather codes with user-friendly descriptions and icons.


39-76: LGTM! Proper Zod validation for external API responses.

The Zod schemas appropriately validate Open-Meteo API responses at runtime, providing a safety layer against unexpected API changes or malformed data. This is a best practice for external API integration.


78-133: LGTM! Well-structured TypeScript interfaces.

The interfaces are comprehensive and provide clear contracts for the weather data structures, aligning well with the Zod schemas used for validation.


282-340: LGTM! Clever deterministic mock location generation.

The lookup table provides realistic coordinates for common cities, and the hash-based fallback generates varied, deterministic coordinates for arbitrary location queries. The hashCode function correctly handles integer conversion with the & hash operation.


345-445: LGTM! Realistic mock data generators with proper UUID usage.

The mock data generators provide realistic, varied test data. The use of crypto.randomUUID() for alert IDs is appropriate and avoids potential race conditions or collisions that could occur with timestamp-based or sequential IDs.


447-559: LGTM! Robust WeatherService implementation with proper validation and fallbacks.

The WeatherService class demonstrates good practices:

  • Input validation for empty location queries with clear error messages
  • Graceful fallback to mock data when APIs are unavailable
  • Proper day clamping for forecast requests (1-16 days)
  • Clear JSDoc documentation for all public methods
  • Appropriate handling of Open-Meteo's lack of alert data

561-565: LGTM! Convenient default exports for production and testing.

Exporting both a default weatherService instance (real API mode) and a mockWeatherService instance (mock-only mode) provides convenient defaults for different use cases.

Comment on lines +197 to +201
if (process.env.NODE_ENV !== "test") {
const port = parseInt(process.env.PORT || "3005", 10);
await app.start({ port });
console.log(`Weather App MCP server running on http://localhost:${port}`);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Validate PORT environment variable to handle invalid values.

The parseInt call can return NaN if PORT contains a non-numeric string. While unlikely in practice, this could cause confusing startup errors.

🛡️ Suggested PORT validation
-  const port = parseInt(process.env.PORT || "3005", 10);
+  const portEnv = process.env.PORT || "3005";
+  const port = parseInt(portEnv, 10);
+  if (isNaN(port) || port < 1 || port > 65535) {
+    throw new Error(`Invalid PORT: ${portEnv}. Must be a number between 1 and 65535.`);
+  }
   await app.start({ port });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (process.env.NODE_ENV !== "test") {
const port = parseInt(process.env.PORT || "3005", 10);
await app.start({ port });
console.log(`Weather App MCP server running on http://localhost:${port}`);
}
if (process.env.NODE_ENV !== "test") {
const portEnv = process.env.PORT || "3005";
const port = parseInt(portEnv, 10);
if (isNaN(port) || port < 1 || port > 65535) {
throw new Error(`Invalid PORT: ${portEnv}. Must be a number between 1 and 65535.`);
}
await app.start({ port });
console.log(`Weather App MCP server running on http://localhost:${port}`);
}
🤖 Prompt for AI Agents
In @examples/weather-app/server/index.ts around lines 197 - 201, The PORT
parsing can yield NaN for non-numeric strings; validate process.env.PORT after
parseInt and before calling app.start so you never pass NaN to app.start({ port
}). In the NODE_ENV !== "test" block, check the parsed port (the local variable
port) with Number.isInteger/Number.isFinite or isNaN, and if invalid either fall
back to the default (e.g., 3005) or log an error and exit; ensure any chosen
behavior logs the invalid process.env.PORT value to aid debugging before calling
app.start({ port }).

@claude

claude Bot commented Jan 10, 2026

Copy link
Copy Markdown
Contributor

PR Review: Weather App Example

Summary

This is a well-crafted example that demonstrates the MCP Apps Kit framework effectively. The code is clean, well-documented, and follows the project conventions outlined in CLAUDE.md. Overall, this is a solid contribution with only minor suggestions for improvement.

✅ Strengths

Code Quality

  • Excellent TypeScript usage: Strict typing throughout with proper use of Zod schemas for validation
  • Clean architecture: Clear separation of concerns (server, services, UI)
  • Comprehensive documentation: Good inline comments and README
  • Follows project conventions: Properly uses defineTool, defineUI, and the colocated UI pattern

Testing

  • Strong test coverage: 33 tests covering both unit and integration scenarios
  • Uses framework testing utilities: Properly leverages @mcp-apps-kit/testing
  • Mock mode for reliability: Smart use of USE_MOCK_WEATHER for predictable tests
  • Tests cover edge cases: Boundary conditions (min/max days, empty strings, etc.)

Design

  • Graceful degradation: Falls back to mock data when API unavailable
  • Good error handling: Validates inputs and returns meaningful errors
  • React hooks properly used: Correct usage of @mcp-apps-kit/ui-react hooks
  • No external API keys required: Uses free Open-Meteo API

🔍 Issues & Suggestions

1. Potential Runtime Issue in Mock Data Generation (Minor)

Location: weatherService.ts:345-362

The mock weather generation uses Math.random() which creates non-deterministic test data. While this works, it could cause flaky tests if boundary conditions are tested.

const weatherCode = codes[Math.floor(Math.random() * codes.length)];

Suggestion: Consider using a seeded random function or hash-based selection for more deterministic mock data in tests.

2. Missing Input Validation (Minor)

Location: server/index.ts:149-155

The days parameter in getForecast has min/max validation in the schema, but the actual validation happens in the service layer:

days: z
  .number()
  .min(1)
  .max(16)
  .default(7)

Observation: This is actually fine as-is, but worth noting that the service also clamps the value at line 511 of weatherService.ts. This is defensive programming (good), but the double validation could be noted in a comment.

3. Error Messages Could Be More Specific (Minor)

Location: weatherService.ts:471, 508, 544

All three methods throw the same generic error:

throw new Error("Location query cannot be empty");

Suggestion: Consider using the framework's AppError with specific error codes for consistency:

import { AppError, ErrorCode } from "@mcp-apps-kit/core";
throw new AppError({ 
  code: ErrorCode.VALIDATION_ERROR, 
  message: "Location query cannot be empty" 
});

4. Silent Failures in API Calls (Low Priority)

Location: weatherService.ts:138-170, 175-221

The geocoding and weather fetch functions catch all errors silently and return null:

} catch {
  return null;
}

Observation: While the fallback to mock data is a good UX decision, it makes debugging difficult if the API is actually failing. Consider logging these errors in development mode:

} catch (error) {
  if (process.env.NODE_ENV !== 'production') {
    console.error('Geocoding failed:', error);
  }
  return null;
}

5. Type Guard Could Be Stronger (Minor)

Location: ui/src/App.tsx:158-178

The type guards use basic checks:

function isCurrentWeather(data: unknown): data is CurrentWeather {
  return !!data && typeof data === "object" && "temperature" in data && "humidity" in data;
}

Suggestion: Consider using Zod's safeParse for runtime validation to match the schema definitions in the server.

6. Protocol Configuration (Question)

Location: server/index.ts:117-119

config: {
  protocol: "openai",
},

Question: Is this meant to be "openai" or should it support both protocols? The PR description mentions working with both MCP Apps and ChatGPT, but this only specifies OpenAI. According to CLAUDE.md, the framework auto-generates platform-specific metadata, so this might be intentional.

7. Unused Client in UI (Very Minor)

Location: ui/src/App.tsx:181

const client = useAppsClient<AppTools>();

The client is instantiated but only used in button onClick handlers (lines 226-241). This is fine, but worth noting that the client is being initialized on every render.

🔒 Security Considerations

No security issues identified

  • No API keys or secrets committed
  • Input validation present
  • No XSS vulnerabilities (React escapes by default)
  • No SQL injection concerns (no database)
  • External API calls use HTTPS
  • No eval() or dangerous dynamic code execution

⚡ Performance Considerations

Generally good, with minor notes:

  1. API Calls: No caching mechanism for repeated requests to the same location. This is fine for an example, but in production, consider caching geocoding results.

  2. Mock Data Generation: Uses Math.random() and randomUUID() which is fine for the scale of this example.

  3. React Rendering: The UI components are straightforward with no performance issues expected.

📊 Test Coverage Assessment

Strong coverage with 33 tests:

  • ✅ Unit tests for service layer (mock mode, validation, edge cases)
  • ✅ Integration tests for server and tools
  • ✅ Tests cover error conditions
  • ✅ Boundary conditions tested (min/max days, empty strings)

Potential gap: No explicit tests for:

  • Network failures and recovery
  • Real API integration (understandably using mocks)
  • UI component rendering (no React component tests)

For an example project, this coverage is excellent.

📝 Documentation Quality

Excellent:

  • ✅ Comprehensive README with clear setup instructions
  • ✅ API documentation for tools
  • ✅ Code comments explain complex logic
  • ✅ JSDoc comments on public methods
  • ✅ Examples of how to connect to MCP hosts

✨ Recommendations

Must Fix (None)

No blocking issues identified.

Should Consider

  1. Use AppError with error codes instead of generic Error for consistency with framework patterns
  2. Add development logging for API failures to aid debugging
  3. Clarify the protocol configuration intent (OpenAI-only vs dual-protocol)

Nice to Have

  1. Add seeded random for more deterministic mock data
  2. Consider caching geocoding results
  3. Add React component tests using @testing-library/react
  4. Strengthen type guards with Zod validation

🎯 Conclusion

This is ready to merge with only minor suggestions for improvement. The code follows project conventions, has strong test coverage, and provides a clear, working example of the MCP Apps Kit framework. The automatic fallback to mock data and comprehensive testing make it reliable for demonstration purposes.

Rating: ⭐⭐⭐⭐½ (4.5/5)

Great work! 🎉

@gabrypavanello
gabrypavanello merged commit b089ad9 into main Jan 10, 2026
4 checks passed
@gabrypavanello
gabrypavanello deleted the claude/weather-app-example-0Cqai branch January 10, 2026 13:46
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.

3 participants