Skip to content

Conversation

@2witstudios
Copy link
Owner

@2witstudios 2witstudios commented Dec 21, 2025

Summary of Critical Issues Addressed

  1. formatBytes() Consolidation (4 locations → 1)
    Kept canonical version in packages/lib/src/client-safe.ts
    Updated storage-limits.ts, subscription-utils.ts, and web utils to import/re-export
  2. File Security Consolidation
    Canonical version in packages/lib/src/utils/file-security.ts
    Updated apps/processor/src/utils/security.ts to import from @pagespace/lib
  3. getClientIP() Export and Usage (15 files updated)
    Added export in packages/lib/src/server.ts
    Updated all 15 API routes to use getClientIP() instead of inline pattern
  4. MCP Tool Converter
    Created shared module in packages/lib/src/mcp/:
    mcp-types.ts - shared type definitions
    mcp-tool-converter.ts - shared converter functions
    index.ts - exports
    Web app updated to use shared module
    Desktop keeps its own implementation due to zod v3/v4 version incompatibility
    TypeScript Verification
    All packages compile successfully
    Unit tests pass (non-database tests verified)
    The changes reduce code duplication and centralize shared utilities in @pagespace/lib.

Summary by CodeRabbit

Release Notes

  • Refactor

    • Consolidated utility functions into a centralized shared library to reduce code duplication, including security definitions, IP detection logic, and formatting utilities.
    • Updated authentication endpoints to use unified IP detection for consistent rate-limiting behavior across login, signup, and token refresh flows.
  • Chores

    • Added comprehensive audit documentation identifying helper function consolidation opportunities.

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

Identified 42+ issues across the codebase:
- 14 orphaned/unused functions (device fingerprinting, memory monitor)
- 14+ duplicate implementations (formatBytes 4x, file security 2x)
- 10 missing helper patterns (getClientIP, cookie headers)
- 4 cases of underutilized helpers (serializeDates, formatBytes)

Key findings:
- getClientIP() exists but never imported (12 files duplicate inline)
- File security functions duplicated in lib and processor
- MCP tool converter duplicated in web and desktop apps
Critical issues addressed:
1. formatBytes() - consolidated from 4 locations to 1 canonical version in client-safe.ts
2. File security functions - consolidated DANGEROUS_MIME_TYPES and isDangerousMimeType into packages/lib
3. getClientIP() - now exported from @pagespace/lib/server and used in 15 API routes
4. MCP tool converter - created shared module in packages/lib for web app (desktop keeps own version due to zod v3/v4 incompatibility)

Changes:
- packages/lib: Added zod dependency, new mcp/ module with shared types and converter
- packages/lib: Export getClientIP from server.ts
- apps/web: Updated 15 API routes to use getClientIP()
- apps/web: Updated mcp-tool-converter to re-export from @pagespace/lib
- apps/processor: Import file security utilities from @pagespace/lib
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 21, 2025

Walkthrough

This PR consolidates duplicate helper functions and utilities scattered across the codebase into a shared @pagespace/lib library, establishing a single source of truth. Client IP extraction, formatting utilities, security utilities, and MCP tool converters are moved or re-exported from the library. A comprehensive audit document identifies remaining consolidation opportunities.

Changes

Cohort / File(s) Summary
Security & MIME Type Consolidation
apps/processor/src/utils/security.ts
Removes local definitions of DANGEROUS_MIME_TYPES and isDangerousMimeType, replacing them with re-exports from @pagespace/lib.
Client IP Extraction (Auth & API Routes)
apps/web/src/app/api/account/devices/route.ts, apps/web/src/app/api/auth/device/refresh/route.ts, apps/web/src/app/api/auth/google/callback/route.ts, apps/web/src/app/api/auth/google/signin/route.ts, apps/web/src/app/api/auth/login/route.ts, apps/web/src/app/api/auth/logout/route.ts, apps/web/src/app/api/auth/mobile/login/route.ts, apps/web/src/app/api/auth/mobile/oauth/google/exchange/route.ts, apps/web/src/app/api/auth/mobile/refresh/route.ts, apps/web/src/app/api/auth/mobile/signup/route.ts, apps/web/src/app/api/auth/refresh/route.ts, apps/web/src/app/api/auth/signup/route.ts
Replace manual client IP header extraction with centralized getClientIP(req) import from @pagespace/lib/server. Affects IP determination for rate limiting and device tracking across authentication flows.
Client IP Extraction (Utility Routes)
apps/web/src/app/api/contact/route.ts, apps/web/src/app/api/mcp-ws/route.ts, apps/web/src/app/api/track/route.ts
Replace inline IP extraction with getClientIP() helper for contact, MCP WebSocket, and event tracking endpoints.
Formatting Utilities Consolidation
apps/web/src/app/dashboard/storage/page.tsx, apps/web/src/lib/utils/utils.ts
Remove local formatBytes implementations; import from @pagespace/lib/client-safe. Export parseBytes utility through consolidated module.
MCP Tool Converter Consolidation
apps/web/src/lib/ai/core/mcp-tool-converter.ts
Remove all local MCP tool conversion logic; re-export functions and types from @pagespace/lib (validateToolName, validateServerName, createSafeToolName, convertMCPToolSchemaToZod, convertMCPToolsToAISDKSchemas, parseMCPToolName, isMCPTool, MCPTool, ToolExecutionResult).
Library Package Exports & Dependencies
packages/lib/package.json, packages/lib/src/index.ts, packages/lib/src/server.ts, packages/lib/src/services/storage-limits.ts, packages/lib/src/services/subscription-utils.ts
Add zod dependency; export getClientIP, detectPlatform, extractDeviceMetadata from device-fingerprint-utils; re-export MCP utilities; consolidate formatBytes/parseBytes re-exports.
New Shared Library Modules
packages/lib/src/mcp/index.ts, packages/lib/src/mcp/mcp-types.ts, packages/lib/src/mcp/mcp-tool-converter.ts
Introduce centralized MCP types (MCPTool, ToolExecutionResult, MCPServerConfig, MCPConfig, MCPServerStatus, MCPServerStatusInfo) and MCP tool converter utilities with JSON Schema to Zod conversion, name validation/safety, and schema transformation for AI SDK integration.
Audit & Documentation
docs/audits/helper-functions-audit.md
Add comprehensive audit report documenting orphaned/unused helpers, duplicate implementations, missing helper patterns, underutilized utilities, and prioritized consolidation recommendations across the monorepo.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Areas requiring attention:

  • MCP tool converter logic (packages/lib/src/mcp/mcp-tool-converter.ts): Dense utility with JSON Schema to Zod conversion, validation rules, and multiple recursive/nested transformations—verify correctness of schema mapping and error handling.
  • Client IP extraction pattern (12+ files): Verify all routes correctly import and invoke getClientIP(req) and that rate-limiting logic remains intact; check fallback behavior when IP is 'unknown'.
  • Import consolidation chain: Ensure no circular dependencies introduced between utils, storage-limits, subscription-utils, and client-safe modules.
  • Audit document accuracy (docs/audits/helper-functions-audit.md): Validate findings align with actual codebase state; confirm recommendations are actionable.

Poem

🐰 Consolidation hops bring order at last,
Duplicate helpers shuffled into the past,
One source of truth now guides every call,
From IP to bytes, the library holds all! 🧡

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.38% 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 'Audit and clean up helper functions' directly describes the main objective of the pull request, which consolidates duplicated utility functions and creates shared modules in the library.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/audit-helper-functions-S8n8o

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
apps/web/src/app/api/auth/google/signin/route.ts (1)

49-54: Verify OAUTH_STATE_SECRET environment variable is set.

The non-null assertion on process.env.OAUTH_STATE_SECRET! (line 52) will fail silently if the environment variable is undefined, potentially creating an insecure HMAC signature. Consider adding validation.

🔎 Suggested validation

Add validation at the start of the POST handler:

 export async function POST(req: Request) {
   try {
+    if (!process.env.OAUTH_STATE_SECRET) {
+      loggers.auth.error('OAUTH_STATE_SECRET environment variable not configured');
+      return Response.json({ error: 'OAuth configuration error.' }, { status: 500 });
+    }
+
     const body = await req.json();
🧹 Nitpick comments (2)
apps/web/src/app/api/account/devices/route.ts (1)

2-2: Avoid calling getClientIP(req) twice on the same line.

Line 145 calls getClientIP(req) twice, which is inefficient. Store the result in a variable first.

🔎 Proposed fix
+      const clientIP = getClientIP(req);
       const newTokenData = await createDeviceTokenRecord(
         userId,
         currentDeviceInfo.deviceId,
         currentDeviceInfo.platform,
         newTokenVersion,
         {
           deviceName: currentDeviceInfo.deviceName || undefined,
           userAgent: req.headers.get('user-agent') ?? undefined,
-          ipAddress: getClientIP(req) !== 'unknown' ? getClientIP(req) : undefined,
+          ipAddress: clientIP !== 'unknown' ? clientIP : undefined,
         }
       );

Also applies to: 145-145

packages/lib/src/mcp/mcp-types.ts (1)

82-90: Consider JSON serialization for Date fields.

startedAt and lastCrashAt are typed as Date, which will serialize to ISO strings when sent over JSON. Ensure consumers handle the string-to-Date conversion when deserializing, or consider using string | Date if the type is used across serialization boundaries.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5583351 and 36da785.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (28)
  • apps/processor/src/utils/security.ts (2 hunks)
  • apps/web/src/app/api/account/devices/route.ts (2 hunks)
  • apps/web/src/app/api/auth/device/refresh/route.ts (2 hunks)
  • apps/web/src/app/api/auth/google/callback/route.ts (2 hunks)
  • apps/web/src/app/api/auth/google/signin/route.ts (2 hunks)
  • apps/web/src/app/api/auth/login/route.ts (2 hunks)
  • apps/web/src/app/api/auth/logout/route.ts (2 hunks)
  • apps/web/src/app/api/auth/mobile/login/route.ts (2 hunks)
  • apps/web/src/app/api/auth/mobile/oauth/google/exchange/route.ts (2 hunks)
  • apps/web/src/app/api/auth/mobile/refresh/route.ts (2 hunks)
  • apps/web/src/app/api/auth/mobile/signup/route.ts (2 hunks)
  • apps/web/src/app/api/auth/refresh/route.ts (2 hunks)
  • apps/web/src/app/api/auth/signup/route.ts (2 hunks)
  • apps/web/src/app/api/contact/route.ts (2 hunks)
  • apps/web/src/app/api/mcp-ws/route.ts (2 hunks)
  • apps/web/src/app/api/track/route.ts (2 hunks)
  • apps/web/src/app/dashboard/storage/page.tsx (1 hunks)
  • apps/web/src/lib/ai/core/mcp-tool-converter.ts (1 hunks)
  • apps/web/src/lib/utils/utils.ts (1 hunks)
  • docs/audits/helper-functions-audit.md (1 hunks)
  • packages/lib/package.json (1 hunks)
  • packages/lib/src/index.ts (2 hunks)
  • packages/lib/src/mcp/index.ts (1 hunks)
  • packages/lib/src/mcp/mcp-tool-converter.ts (1 hunks)
  • packages/lib/src/mcp/mcp-types.ts (1 hunks)
  • packages/lib/src/server.ts (1 hunks)
  • packages/lib/src/services/storage-limits.ts (2 hunks)
  • packages/lib/src/services/subscription-utils.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
apps/web/src/app/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/src/app/**/*.ts: In Next.js 15 route handlers with dynamic routes, you MUST await context.params before destructuring because params are Promise objects
Get request body using const body = await request.json();
Get search params using const { searchParams } = new URL(request.url);
Return JSON responses using return Response.json(data) or return NextResponse.json(data)

Files:

  • apps/web/src/app/api/auth/refresh/route.ts
  • apps/web/src/app/api/auth/logout/route.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • apps/web/src/app/api/auth/login/route.ts
  • apps/web/src/app/api/auth/mobile/login/route.ts
  • apps/web/src/app/api/mcp-ws/route.ts
  • apps/web/src/app/api/auth/mobile/refresh/route.ts
  • apps/web/src/app/api/track/route.ts
  • apps/web/src/app/api/account/devices/route.ts
  • apps/web/src/app/api/auth/device/refresh/route.ts
  • apps/web/src/app/api/auth/mobile/oauth/google/exchange/route.ts
  • apps/web/src/app/api/auth/mobile/signup/route.ts
  • apps/web/src/app/api/contact/route.ts
  • apps/web/src/app/api/auth/google/signin/route.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Never use any types in TypeScript code - always use proper TypeScript types

**/*.{ts,tsx}: No any types - always use proper TypeScript types
Use kebab-case for filenames (e.g., image-processor.ts)
Use camelCase for variables and functions
Use UPPER_SNAKE_CASE for constants
Use PascalCase for types and enums
Use centralized permission logic: import getUserAccessLevel and canUserEditPage from @pagespace/lib/permissions
Use Drizzle client from @pagespace/db for all database access
Always structure message content using the message parts structure: { parts: [{ type: 'text', text: '...' }] }
Use ESM modules and TypeScript strict mode

Files:

  • apps/web/src/app/api/auth/refresh/route.ts
  • apps/web/src/app/api/auth/logout/route.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • packages/lib/src/index.ts
  • apps/web/src/app/api/auth/login/route.ts
  • apps/web/src/app/api/auth/mobile/login/route.ts
  • apps/web/src/app/dashboard/storage/page.tsx
  • apps/web/src/app/api/mcp-ws/route.ts
  • apps/web/src/lib/utils/utils.ts
  • apps/processor/src/utils/security.ts
  • apps/web/src/app/api/auth/mobile/refresh/route.ts
  • apps/web/src/app/api/track/route.ts
  • apps/web/src/app/api/account/devices/route.ts
  • apps/web/src/app/api/auth/device/refresh/route.ts
  • packages/lib/src/server.ts
  • apps/web/src/app/api/auth/mobile/oauth/google/exchange/route.ts
  • apps/web/src/app/api/auth/mobile/signup/route.ts
  • packages/lib/src/mcp/mcp-tool-converter.ts
  • packages/lib/src/mcp/index.ts
  • apps/web/src/app/api/contact/route.ts
  • apps/web/src/app/api/auth/google/signin/route.ts
  • packages/lib/src/mcp/mcp-types.ts
  • apps/web/src/lib/ai/core/mcp-tool-converter.ts
  • packages/lib/src/services/storage-limits.ts
  • packages/lib/src/services/subscription-utils.ts
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/**/*.{ts,tsx}: Use centralized permission functions from @pagespace/lib/permissions for access control, such as getUserAccessLevel() and canUserEditPage()
Always use Drizzle client from @pagespace/db for database access instead of direct database connections
Always use message parts structure with parts array containing objects with type and text fields when constructing messages for AI

Files:

  • apps/web/src/app/api/auth/refresh/route.ts
  • apps/web/src/app/api/auth/logout/route.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • apps/web/src/app/api/auth/login/route.ts
  • apps/web/src/app/api/auth/mobile/login/route.ts
  • apps/web/src/app/dashboard/storage/page.tsx
  • apps/web/src/app/api/mcp-ws/route.ts
  • apps/web/src/lib/utils/utils.ts
  • apps/processor/src/utils/security.ts
  • apps/web/src/app/api/auth/mobile/refresh/route.ts
  • apps/web/src/app/api/track/route.ts
  • apps/web/src/app/api/account/devices/route.ts
  • apps/web/src/app/api/auth/device/refresh/route.ts
  • apps/web/src/app/api/auth/mobile/oauth/google/exchange/route.ts
  • apps/web/src/app/api/auth/mobile/signup/route.ts
  • apps/web/src/app/api/contact/route.ts
  • apps/web/src/app/api/auth/google/signin/route.ts
  • apps/web/src/lib/ai/core/mcp-tool-converter.ts
apps/web/src/app/**/route.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/src/app/**/route.{ts,tsx}: In Next.js 15 dynamic routes, params are Promise objects and must be awaited before destructuring
Get request body using const body = await request.json();
Return JSON responses using Response.json(data) or NextResponse.json(data) in route handlers

Files:

  • apps/web/src/app/api/auth/refresh/route.ts
  • apps/web/src/app/api/auth/logout/route.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • apps/web/src/app/api/auth/login/route.ts
  • apps/web/src/app/api/auth/mobile/login/route.ts
  • apps/web/src/app/api/mcp-ws/route.ts
  • apps/web/src/app/api/auth/mobile/refresh/route.ts
  • apps/web/src/app/api/track/route.ts
  • apps/web/src/app/api/account/devices/route.ts
  • apps/web/src/app/api/auth/device/refresh/route.ts
  • apps/web/src/app/api/auth/mobile/oauth/google/exchange/route.ts
  • apps/web/src/app/api/auth/mobile/signup/route.ts
  • apps/web/src/app/api/contact/route.ts
  • apps/web/src/app/api/auth/google/signin/route.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Format code with Prettier and lint with ESLint using the configuration at apps/web/eslint.config.mjs

Files:

  • apps/web/src/app/api/auth/refresh/route.ts
  • apps/web/src/app/api/auth/logout/route.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • packages/lib/src/index.ts
  • apps/web/src/app/api/auth/login/route.ts
  • apps/web/src/app/api/auth/mobile/login/route.ts
  • apps/web/src/app/dashboard/storage/page.tsx
  • apps/web/src/app/api/mcp-ws/route.ts
  • apps/web/src/lib/utils/utils.ts
  • apps/processor/src/utils/security.ts
  • apps/web/src/app/api/auth/mobile/refresh/route.ts
  • apps/web/src/app/api/track/route.ts
  • apps/web/src/app/api/account/devices/route.ts
  • apps/web/src/app/api/auth/device/refresh/route.ts
  • packages/lib/src/server.ts
  • apps/web/src/app/api/auth/mobile/oauth/google/exchange/route.ts
  • apps/web/src/app/api/auth/mobile/signup/route.ts
  • packages/lib/src/mcp/mcp-tool-converter.ts
  • packages/lib/src/mcp/index.ts
  • apps/web/src/app/api/contact/route.ts
  • apps/web/src/app/api/auth/google/signin/route.ts
  • packages/lib/src/mcp/mcp-types.ts
  • apps/web/src/lib/ai/core/mcp-tool-converter.ts
  • packages/lib/src/services/storage-limits.ts
  • packages/lib/src/services/subscription-utils.ts
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/src/**/*.{ts,tsx}: Use Zustand for client-side state management
Use SWR for server state and caching

Files:

  • apps/web/src/app/api/auth/refresh/route.ts
  • apps/web/src/app/api/auth/logout/route.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • apps/web/src/app/api/auth/login/route.ts
  • apps/web/src/app/api/auth/mobile/login/route.ts
  • apps/web/src/app/dashboard/storage/page.tsx
  • apps/web/src/app/api/mcp-ws/route.ts
  • apps/web/src/lib/utils/utils.ts
  • apps/web/src/app/api/auth/mobile/refresh/route.ts
  • apps/web/src/app/api/track/route.ts
  • apps/web/src/app/api/account/devices/route.ts
  • apps/web/src/app/api/auth/device/refresh/route.ts
  • apps/web/src/app/api/auth/mobile/oauth/google/exchange/route.ts
  • apps/web/src/app/api/auth/mobile/signup/route.ts
  • apps/web/src/app/api/contact/route.ts
  • apps/web/src/app/api/auth/google/signin/route.ts
  • apps/web/src/lib/ai/core/mcp-tool-converter.ts
apps/web/src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/src/**/*.tsx: For document editing, register editing state using useEditingStore.getState().startEditing() and endEditing() to prevent unwanted UI refreshes
For AI streaming operations, register streaming state using useEditingStore.getState().startStreaming() and endStreaming() to prevent unwanted UI refreshes
When using SWR, check useEditingStore state with isAnyActive() and set isPaused to prevent data refreshes during editing or streaming

Files:

  • apps/web/src/app/dashboard/storage/page.tsx
🧠 Learnings (15)
📓 Common learnings
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: PageSpace has 17 specialized domain expert agents covering authentication, database, permissions, real-time collaboration, monitoring, AI systems, content management, file processing, search, frontend architecture, editors, canvas, API routes, and MCP integration
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to **/*.{ts,tsx} : Always use the Drizzle client and database exports from `pagespace/db` (e.g., `import { db, pages } from 'pagespace/db'`) for all database access

Applied to files:

  • apps/web/src/app/api/auth/refresh/route.ts
  • apps/web/src/app/api/auth/logout/route.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • packages/lib/src/index.ts
  • apps/web/src/app/dashboard/storage/page.tsx
  • apps/web/src/lib/utils/utils.ts
  • apps/web/src/app/api/account/devices/route.ts
  • apps/web/src/app/api/contact/route.ts
  • packages/lib/src/services/storage-limits.ts
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to **/*.{ts,tsx} : Use Drizzle client from `pagespace/db` for all database access

Applied to files:

  • apps/web/src/app/api/auth/refresh/route.ts
  • apps/web/src/app/api/auth/logout/route.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • apps/web/src/app/api/account/devices/route.ts
  • apps/web/src/app/api/contact/route.ts
📚 Learning: 2025-12-14T14:54:38.009Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: Applies to apps/**/*.ts : Always use Drizzle client and queries from `pagespace/db` for database access instead of direct queries

Applied to files:

  • apps/web/src/app/api/auth/refresh/route.ts
  • apps/web/src/app/api/auth/logout/route.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • apps/web/src/app/api/account/devices/route.ts
  • packages/lib/src/services/storage-limits.ts
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/**/*.{ts,tsx} : Always use Drizzle client from `pagespace/db` for database access instead of direct database connections

Applied to files:

  • apps/web/src/app/api/auth/refresh/route.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • apps/web/src/app/api/account/devices/route.ts
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Applies to app/**/*.{ts,tsx} : Import and use `getUserAccessLevel()` and `canUserEditPage()` from `pagespace/lib/permissions` for centralized permission logic

Applied to files:

  • apps/web/src/app/api/auth/refresh/route.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • apps/web/src/app/api/auth/login/route.ts
  • apps/web/src/app/api/auth/mobile/login/route.ts
  • apps/web/src/app/dashboard/storage/page.tsx
  • apps/web/src/app/api/account/devices/route.ts
📚 Learning: 2025-12-14T14:54:38.009Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: Applies to apps/**/*.ts : Always use centralized permission functions from `pagespace/lib/permissions` for access control logic (e.g., `getUserAccessLevel`, `canUserEditPage`)

Applied to files:

  • apps/web/src/app/api/auth/refresh/route.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • apps/web/src/app/api/auth/mobile/login/route.ts
  • apps/processor/src/utils/security.ts
  • apps/web/src/app/api/account/devices/route.ts
📚 Learning: 2025-12-14T14:54:15.319Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T14:54:15.319Z
Learning: Applies to apps/**/*.{ts,tsx} : Use centralized permission functions from `pagespace/lib/permissions` for access control, such as `getUserAccessLevel()` and `canUserEditPage()`

Applied to files:

  • apps/web/src/app/api/auth/refresh/route.ts
  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • apps/processor/src/utils/security.ts
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: This is a monorepo using pnpm workspaces with structure: `apps/web` (Next.js frontend/backend), `apps/realtime` (Socket.IO service), `apps/processor` (Express file/OCR pipeline), `packages/db` (Drizzle ORM), `packages/lib` (shared utilities)

Applied to files:

  • packages/lib/package.json
  • packages/lib/src/index.ts
  • docs/audits/helper-functions-audit.md
📚 Learning: 2025-12-14T14:54:38.009Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: The project uses a pnpm monorepo workspace with structure: `apps/web` (Next.js), `apps/realtime` (Socket.IO), `apps/processor` (Express), `packages/db` (Drizzle ORM), `packages/lib` (shared utilities)

Applied to files:

  • packages/lib/package.json
  • packages/lib/src/index.ts
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to **/*.{ts,tsx} : Use centralized permission logic: import `getUserAccessLevel` and `canUserEditPage` from `pagespace/lib/permissions`

Applied to files:

  • apps/web/src/app/api/auth/google/callback/route.ts
  • apps/web/src/app/api/auth/signup/route.ts
  • apps/web/src/lib/utils/utils.ts
  • apps/processor/src/utils/security.ts
📚 Learning: 2025-12-14T14:54:47.122Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:47.122Z
Learning: Applies to apps/web/src/app/**/route.{ts,tsx} : Get request body using `const body = await request.json();`

Applied to files:

  • apps/web/src/app/api/auth/signup/route.ts
  • apps/web/src/app/api/mcp-ws/route.ts
📚 Learning: 2025-12-14T14:54:38.009Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:38.009Z
Learning: The tech stack consists of Next.js 15 with App Router, TypeScript, Tailwind, shadcn/ui, PostgreSQL with Drizzle ORM, Ollama/Vercel AI SDK, custom JWT auth, and Socket.IO for real-time features

Applied to files:

  • apps/web/src/app/api/auth/signup/route.ts
📚 Learning: 2025-12-14T14:54:45.713Z
Learnt from: CR
Repo: 2witstudios/PageSpace PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-14T14:54:45.713Z
Learning: Tech stack: Next.js 15 App Router + TypeScript + Tailwind + shadcn/ui (frontend), PostgreSQL + Drizzle ORM (database), Ollama + Vercel AI SDK + OpenRouter + Google AI SDK (AI), custom JWT auth, local filesystem storage, Socket.IO for real-time, Docker deployment

Applied to files:

  • apps/web/src/app/api/auth/signup/route.ts
📚 Learning: 2025-12-16T19:03:59.870Z
Learnt from: 2witstudios
Repo: 2witstudios/PageSpace PR: 91
File: apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx:253-277
Timestamp: 2025-12-16T19:03:59.870Z
Learning: In apps/web/src/components/ai/shared/chat/tool-calls/CompactToolCallRenderer.tsx (TypeScript/React), use the `getLanguageFromPath` utility from `formatters.ts` to infer syntax highlighting language from file paths instead of hardcoding language values in DocumentRenderer calls.

Applied to files:

  • apps/web/src/lib/ai/core/mcp-tool-converter.ts
🧬 Code graph analysis (13)
apps/web/src/app/api/auth/refresh/route.ts (2)
packages/lib/src/index.ts (1)
  • getClientIP (51-51)
packages/lib/src/server.ts (1)
  • getClientIP (4-4)
apps/web/src/app/api/auth/google/callback/route.ts (2)
packages/lib/src/index.ts (1)
  • getClientIP (51-51)
packages/lib/src/server.ts (1)
  • getClientIP (4-4)
apps/web/src/app/api/auth/login/route.ts (2)
packages/lib/src/index.ts (1)
  • getClientIP (51-51)
packages/lib/src/server.ts (1)
  • getClientIP (4-4)
apps/web/src/app/api/auth/mobile/login/route.ts (2)
packages/lib/src/index.ts (1)
  • getClientIP (51-51)
packages/lib/src/server.ts (1)
  • getClientIP (4-4)
apps/web/src/app/api/mcp-ws/route.ts (2)
packages/lib/src/index.ts (1)
  • getClientIP (51-51)
packages/lib/src/server.ts (1)
  • getClientIP (4-4)
apps/processor/src/utils/security.ts (1)
packages/lib/src/utils/file-security.ts (2)
  • DANGEROUS_MIME_TYPES (39-45)
  • isDangerousMimeType (50-54)
apps/web/src/app/api/auth/mobile/refresh/route.ts (2)
packages/lib/src/index.ts (1)
  • getClientIP (51-51)
packages/lib/src/server.ts (1)
  • getClientIP (4-4)
apps/web/src/app/api/track/route.ts (2)
packages/lib/src/index.ts (1)
  • getClientIP (51-51)
packages/lib/src/server.ts (1)
  • getClientIP (4-4)
apps/web/src/app/api/auth/device/refresh/route.ts (2)
packages/lib/src/index.ts (1)
  • getClientIP (51-51)
packages/lib/src/server.ts (1)
  • getClientIP (4-4)
apps/web/src/app/api/auth/mobile/oauth/google/exchange/route.ts (2)
packages/lib/src/index.ts (1)
  • getClientIP (51-51)
packages/lib/src/server.ts (1)
  • getClientIP (4-4)
apps/web/src/app/api/auth/mobile/signup/route.ts (2)
packages/lib/src/index.ts (1)
  • getClientIP (51-51)
packages/lib/src/server.ts (1)
  • getClientIP (4-4)
apps/web/src/app/api/contact/route.ts (2)
packages/lib/src/index.ts (1)
  • getClientIP (51-51)
packages/lib/src/server.ts (1)
  • getClientIP (4-4)
apps/web/src/app/api/auth/google/signin/route.ts (2)
packages/lib/src/index.ts (1)
  • getClientIP (51-51)
packages/lib/src/server.ts (1)
  • getClientIP (4-4)
🪛 Biome (2.1.2)
packages/lib/src/mcp/mcp-tool-converter.ts

[error] 124-124: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 125-125: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 145-145: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 156-156: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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: Unit Tests
🔇 Additional comments (37)
apps/web/src/lib/ai/core/mcp-tool-converter.ts (1)

1-19: Clean consolidation correctly maintains backward compatibility.

All re-exported functions (validateToolName, validateServerName, createSafeToolName, convertMCPToolSchemaToZod, convertMCPToolsToAISDKSchemas, parseMCPToolName, isMCPTool) and types (MCPToolConversionOptions, MCPTool, ToolExecutionResult) are properly exported from @pagespace/lib through the MCP module. The re-export pattern is well-structured and successfully consolidates the implementation in the shared library while preserving backward compatibility for the web app.

packages/lib/src/services/subscription-utils.ts (1)

93-94: LGTM - Clean consolidation.

The re-export maintains backward compatibility while establishing client-safe.ts as the canonical source for formatBytes. This eliminates duplication as documented in the audit.

packages/lib/src/services/storage-limits.ts (1)

3-3: LGTM - Consistent consolidation pattern.

Importing formatBytes and parseBytes from the canonical source (client-safe) while maintaining backward compatibility through re-exports (lines 357-358) is the correct approach.

apps/processor/src/utils/security.ts (1)

2-7: LGTM - File security utilities successfully consolidated.

The import aliasing pattern cleanly delegates to @pagespace/lib while maintaining the processor's existing API surface. This eliminates the duplication identified in the audit.

docs/audits/helper-functions-audit.md (1)

1-362: Excellent comprehensive audit document.

This document provides strong justification for the consolidation work in this PR. The 42+ identified issues (orphaned functions, duplicates, missing helpers) align well with the implemented fixes:

  • ✅ formatBytes consolidation (section 2.3)
  • ✅ getClientIP centralization (section 3.1)
  • ✅ File security deduplication (section 2.1)

Note: The audit describes the "before" state, so references like "getClientIP...NEVER IMPORTED" (line 171) are now resolved by this PR's changes.

apps/web/src/app/api/mcp-ws/route.ts (1)

30-30: LGTM - Centralized IP extraction improves consistency.

Replacing manual header parsing with getClientIP(request) eliminates code duplication and ensures consistent IP resolution across authentication flows.

Also applies to: 75-75

apps/web/src/app/api/track/route.ts (1)

8-8: LGTM - Consistent IP resolution.

Using the centralized getClientIP helper for tracking metadata ensures consistent IP extraction across the application.

Also applies to: 23-23

apps/web/src/app/api/auth/google/signin/route.ts (1)

2-2: LGTM - Centralized IP resolution for rate limiting.

Using getClientIP from the shared library ensures consistent IP extraction for rate limiting across authentication routes.

Also applies to: 22-22

packages/lib/package.json (1)

258-258: Zod ^3.24.4 is secure and from a well-maintained project, but consider checking for newer v3 releases.

The specified version is safe from known security vulnerabilities: Zod version 3.22.2 allows an attacker to perform a denial of service while validating emails, but upgrading zod to version 3.22.3 or higher resolves this. Since v3.24.4 is newer than 3.22.3, it includes this fix.

The project is actively maintained: Zod demonstrates sustainable maintenance with at least one new version released in the past 3 months, and Zod 3 will continue to receive bug fixes & stability improvements. However, note that libraries should use zod@^3.25.0 for optimal v3/v4 compatibility support, suggesting newer v3 versions may exist beyond 3.24.4.

apps/web/src/app/api/auth/login/route.ts (2)

14-14: LGTM! Centralized IP extraction.

The import of getClientIP from the shared library promotes code reuse and maintains a single source of truth for IP resolution logic.


45-45: LGTM! Consistent IP resolution.

The usage of getClientIP(req) replaces manual header parsing and provides consistent IP extraction across all authentication routes.

apps/web/src/app/dashboard/storage/page.tsx (1)

39-39: LGTM! Centralized formatting utility.

The import of formatBytes from @pagespace/lib/client-safe eliminates code duplication and ensures consistent byte formatting across the application.

apps/web/src/app/api/auth/device/refresh/route.ts (2)

14-14: LGTM! Centralized IP extraction.

The import consolidates IP resolution logic into the shared library, consistent with other authentication routes in this PR.


39-39: LGTM! Consistent IP handling.

The usage of getClientIP(req) follows the same pattern as other authentication routes and maintains backward compatibility with the IP normalization logic (handling 'unknown' values).

apps/web/src/app/api/auth/mobile/login/route.ts (2)

12-12: LGTM! Centralized IP extraction.

The import follows the same consolidation pattern applied across all authentication routes in this PR.


42-42: LGTM! Consistent IP resolution for mobile.

The usage of getClientIP(req) ensures mobile authentication routes use the same IP extraction logic as web routes, maintaining consistency across platforms.

apps/web/src/app/api/auth/mobile/signup/route.ts (2)

13-13: LGTM! Centralized IP extraction.

The import aligns with the broader refactoring effort to consolidate IP resolution logic across all authentication routes.


46-46: LGTM! Consistent IP resolution.

The usage of getClientIP(req) provides a consistent approach to IP extraction for mobile signup flows, matching the pattern used in other authentication routes.

apps/web/src/app/api/auth/signup/route.ts (2)

4-4: LGTM! Centralized IP extraction.

The addition of getClientIP to the import list consolidates IP resolution logic, consistent with the PR's goal to centralize helper functions.


41-41: LGTM! Consistent IP resolution.

The usage of getClientIP(req) ensures web signup routes use the same IP extraction logic as mobile routes, promoting consistency across the codebase.

apps/web/src/app/api/auth/mobile/refresh/route.ts (2)

13-13: LGTM! Centralized IP extraction.

The import follows the consolidation pattern applied across all authentication routes in this PR.


35-35: LGTM! Consistent IP resolution for mobile refresh.

The usage of getClientIP(req) ensures mobile token refresh routes use the same IP extraction logic as other authentication routes, with proper handling of the 'unknown' case.

apps/web/src/app/api/contact/route.ts (2)

4-4: LGTM! Centralized IP extraction.

The addition of getClientIP to the import list extends the centralized IP resolution pattern to non-authentication routes, promoting consistency across the entire API surface.


17-17: LGTM! Consistent IP resolution.

The usage of getClientIP(request) provides consistent IP extraction for contact form submissions, used appropriately for logging purposes.

apps/web/src/app/api/auth/refresh/route.ts (1)

3-3: LGTM! Clean centralization of IP extraction.

The import and usage of getClientIP(req) properly replaces manual header parsing, aligning with the PR's consolidation objectives.

Also applies to: 19-19

apps/web/src/app/api/auth/logout/route.ts (1)

4-4: LGTM! Consistent IP extraction centralization.

The change properly adopts the centralized getClientIP helper, maintaining consistency with other auth routes in this PR.

Also applies to: 19-19

packages/lib/src/index.ts (2)

50-51: LGTM! Well-documented export addition.

The explicit export of getClientIP, detectPlatform, and extractDeviceMetadata with clarifying comments properly supports the centralization effort across API routes.


106-107: LGTM! Clear MCP module export.

The export of MCP utilities with documentation noting "shared across web and desktop" provides good context for the new centralized module.

apps/web/src/app/api/auth/mobile/oauth/google/exchange/route.ts (1)

61-61: LGTM! Consistent centralization.

The adoption of getClientIP(req) aligns with the broader refactor across all auth routes.

Also applies to: 105-105

packages/lib/src/server.ts (1)

4-4: LGTM! Proper re-export of device fingerprint utilities.

The explicit export enables centralized IP extraction across the codebase, supporting the consolidation objectives.

packages/lib/src/mcp/index.ts (1)

1-7: LGTM! Clean centralized MCP module entry point.

This new index properly consolidates MCP types and tool conversion utilities into a single, well-documented entry point shared across web and desktop packages.

apps/web/src/app/api/auth/google/callback/route.ts (1)

4-4: LGTM! Consistent IP extraction centralization.

The change properly replaces manual header-based IP extraction with the centralized getClientIP(req) helper, maintaining consistency across all auth routes.

Also applies to: 97-97

apps/web/src/lib/utils/utils.ts (1)

4-5: LGTM! Good consolidation of shared utilities.

Re-exporting from @pagespace/lib/client-safe maintains backward compatibility for existing consumers while establishing a single source of truth. This aligns well with the PR's objective of centralizing shared utilities.

packages/lib/src/mcp/mcp-types.ts (1)

1-44: Well-structured shared type definitions.

The documentation is thorough, especially the naming conventions and security considerations in the JSDoc comments. The inputSchema type constraint with type: 'object' literal provides good type safety.

packages/lib/src/mcp/mcp-tool-converter.ts (3)

28-42: LGTM! Thorough validation implementation.

The validation functions properly check for empty strings, length limits, and character restrictions. Throwing errors with descriptive messages is appropriate for security-critical validation.


268-302: Good backward compatibility handling for legacy format.

The parseMCPToolName function correctly supports both the new mcp:server:tool format and the legacy mcp__server__tool format. The logic to rejoin remaining parts handles tool names that might contain the separator character.


224-259: LGTM! Robust batch conversion with proper error handling.

The function gracefully handles conversion failures by logging warnings and skipping problematic tools rather than failing the entire batch. The callback pattern for warnings/info provides flexibility for different logging contexts.

Comment on lines +92 to +163
switch (type) {
case 'string':
zodSchema = z.string();
if (schema.enum && Array.isArray(schema.enum)) {
// Handle string enums
zodSchema = z.enum(schema.enum as [string, ...string[]]);
}
break;

case 'number':
case 'integer':
zodSchema = z.number();
if (typeof schema.minimum === 'number') {
zodSchema = (zodSchema as z.ZodNumber).min(schema.minimum);
}
if (typeof schema.maximum === 'number') {
zodSchema = (zodSchema as z.ZodNumber).max(schema.maximum);
}
if (type === 'integer') {
// Integer validation must come after min/max for proper error messages
zodSchema = zodSchema.refine((n: number) => Number.isInteger(n), {
message: 'Must be an integer',
});
}
break;

case 'boolean':
zodSchema = z.boolean();
break;

case 'object':
// Recursively convert nested object properties
const properties = schema.properties as Record<string, Record<string, unknown>> | undefined;
const required = (schema.required as string[]) || [];

if (properties) {
const zodProperties: Record<string, z.ZodTypeAny> = {};
for (const [propName, propSchema] of Object.entries(properties)) {
zodProperties[propName] = jsonSchemaToZod(propSchema, propName, onWarning);
// Make optional if not in required array
if (!required.includes(propName)) {
zodProperties[propName] = zodProperties[propName].optional();
}
}
zodSchema = z.object(zodProperties);
} else {
// Fallback for objects without defined properties
// z.record requires explicit key type (z.string())
zodSchema = z.record(z.string(), z.unknown());
}
break;

case 'array':
const items = schema.items as Record<string, unknown> | undefined;
if (items) {
const itemSchema = jsonSchemaToZod(items, `${propertyName}Item`, onWarning);
zodSchema = z.array(itemSchema);
} else {
zodSchema = z.array(z.unknown());
}
break;

default:
// Fallback for unsupported types
const warnMsg = `Unsupported JSON Schema type "${type}" for property "${propertyName}", using z.unknown()`;
if (onWarning) {
onWarning(warnMsg);
} else {
console.warn(warnMsg);
}
zodSchema = z.unknown();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Wrap switch case declarations in blocks to prevent scope leakage.

The const declarations inside switch cases (properties, required, items, warnMsg) can leak to other cases. Biome correctly flags this as a potential issue.

🔎 Proposed fix
     case 'object':
+    {
       // Recursively convert nested object properties
       const properties = schema.properties as Record<string, Record<string, unknown>> | undefined;
       const required = (schema.required as string[]) || [];

       if (properties) {
         const zodProperties: Record<string, z.ZodTypeAny> = {};
         for (const [propName, propSchema] of Object.entries(properties)) {
           zodProperties[propName] = jsonSchemaToZod(propSchema, propName, onWarning);
           // Make optional if not in required array
           if (!required.includes(propName)) {
             zodProperties[propName] = zodProperties[propName].optional();
           }
         }
         zodSchema = z.object(zodProperties);
       } else {
         // Fallback for objects without defined properties
         // z.record requires explicit key type (z.string())
         zodSchema = z.record(z.string(), z.unknown());
       }
       break;
+    }

-    case 'array':
+    case 'array': {
       const items = schema.items as Record<string, unknown> | undefined;
       if (items) {
         const itemSchema = jsonSchemaToZod(items, `${propertyName}Item`, onWarning);
         zodSchema = z.array(itemSchema);
       } else {
         zodSchema = z.array(z.unknown());
       }
       break;
+    }

-    default:
+    default: {
       // Fallback for unsupported types
       const warnMsg = `Unsupported JSON Schema type "${type}" for property "${propertyName}", using z.unknown()`;
       if (onWarning) {
         onWarning(warnMsg);
       } else {
         console.warn(warnMsg);
       }
       zodSchema = z.unknown();
+    }
🧰 Tools
🪛 Biome (2.1.2)

[error] 124-124: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 125-125: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 145-145: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 156-156: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🤖 Prompt for AI Agents
In packages/lib/src/mcp/mcp-tool-converter.ts around lines 92 to 163, the switch
cases declare const variables (properties, required, items, warnMsg) that can
leak across cases; wrap the bodies of the affected case labels ('object',
'array', and 'default', and any other case that declares block-scoped variables)
in explicit {} blocks so each case has its own lexical scope, keep the existing
breaks, and ensure any variables used only inside the case remain inside those
new blocks.

Comment on lines +101 to +116
case 'number':
case 'integer':
zodSchema = z.number();
if (typeof schema.minimum === 'number') {
zodSchema = (zodSchema as z.ZodNumber).min(schema.minimum);
}
if (typeof schema.maximum === 'number') {
zodSchema = (zodSchema as z.ZodNumber).max(schema.maximum);
}
if (type === 'integer') {
// Integer validation must come after min/max for proper error messages
zodSchema = zodSchema.refine((n: number) => Number.isInteger(n), {
message: 'Must be an integer',
});
}
break;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Consider using z.int() for built-in integer validation in Zod 4.

Zod 4 provides built-in integer validation with z.int() or z.number().int(). Note that these restrict to the safe integer range (Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER), so if your use case requires all integers outside this range, the current refine approach is appropriate.

🤖 Prompt for AI Agents
In packages/lib/src/mcp/mcp-tool-converter.ts around lines 101 to 116, replace
the manual .refine(Number.isInteger) integer check with Zod's built-in integer
validator (z.number().int() or z.int()) to use the native integer error and
avoid a custom refine; keep applying .min()/.max() as before so numeric range
errors remain and then call .int() (or use z.int() instead of z.number() when
creating the base) for integer-specific validation, but retain the original
refine approach only if you must support integers outside
Number.MIN_SAFE_INTEGER..Number.MAX_SAFE_INTEGER (document that caveat in a
comment).

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

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.

ℹ️ 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 +7 to +11
export {
validateToolName,
validateServerName,
createSafeToolName,
convertMCPToolSchemaToZod,

Choose a reason for hiding this comment

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

P1 Badge MCP tools now emit wrong Zod runtime

The web converter now re-exports convertMCPToolsToAISDKSchemas from @pagespace/lib, but that shared package pulls in zod@^3.24.4 (packages/lib/package.json lines 257-259) while the web app and the AI SDK resolve zod@^4.0.16 (apps/web/package.json line 103). As a result, MCP tool schemas produced in the web API (e.g. chat route when mcpTools are present) are created with the v3 Zod classes and then handed to the AI SDK, which checks against its v4 Zod runtime. Those schemas will fail the SDK’s schema guards and MCP tools will be rejected at runtime. The converter here should use the same Zod version as the web/AI stack or the dependency should be aligned to v4 to avoid the cross-version type mismatch.

Useful? React with 👍 / 👎.

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