Skip to content

Conversation

@ngoiyaeric
Copy link
Collaborator

@ngoiyaeric ngoiyaeric commented Aug 15, 2025

PR Type

Enhancement


Description

  • Pass Mapbox map state to MCP for context

  • Add map center, zoom, pitch tracking

  • Include drawn features in context data

  • Update geospatial tool with map context


Diagram Walkthrough

flowchart LR
  A["Chat Panel"] -- "map data" --> B["Form Data"]
  B -- "submit" --> C["Actions"]
  C -- "mapData" --> D["Researcher"]
  D -- "context" --> E["Geospatial Tool"]
  F["Mapbox Map"] -- "state updates" --> G["Map Data Context"]
  G -- "provides" --> A
Loading

File Walkthrough

Relevant files
Enhancement
actions.tsx
Extract and pass map data to researcher                                   

app/actions.tsx

  • Extract map data from form data and parse JSON
  • Pass mapData to researcher function instead of mcp
+4/-1     
chat-panel.tsx
Integrate map context in chat submissions                               

components/chat-panel.tsx

  • Import and use map data context hook
  • Append map data to form submission
+5/-0     
map-data-context.tsx
Extend MapData interface with map state                                   

components/map/map-data-context.tsx

  • Add center, zoom, and pitch properties to MapData interface
+3/-0     
mapbox-map.tsx
Track and update map state changes                                             

components/map/mapbox-map.tsx

  • Update map data context with center, zoom, pitch on changes
  • Capture map state in captureMapCenter callback
+9/-2     
researcher.tsx
Add map context to researcher system prompt                           

lib/agents/researcher.tsx

  • Replace mcp parameter with mapData parameter
  • Build map context string from mapData properties
  • Include map context in system prompt
+18/-3   
geospatial.tsx
Accept map data in geospatial tool                                             

lib/agents/tools/geospatial.tsx

  • Add mapData parameter to geospatialTool function
  • Import MapData type definition
+2/-1     
index.tsx
Update tools interface for map data                                           

lib/agents/tools/index.tsx

  • Replace mcp with mapData in ToolProps interface
  • Pass mapData to geospatialTool instead of mcp
+6/-7     

Summary by CodeRabbit

  • New Features

    • The app now captures map context (center, zoom, pitch, drawn features) and includes it with chat submissions.
    • The assistant uses current map state to provide more relevant, map-aware answers.
    • Geospatial queries are expanded to cover distance calculations, directions, map-related requests, and general geographic information.
  • Refactor

    • Internal tooling updated to pass map context through the assistant and geospatial tools for better consistency and future extensibility.

@vercel
Copy link
Contributor

vercel bot commented Aug 15, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
qcx Ready Preview Comment Aug 15, 2025 9:59am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 15, 2025

Walkthrough

Introduces MapData into the chat flow: map state (center, zoom, pitch, drawn features) is captured in the map component, stored in context, serialized into form submission, parsed in the server action, and passed to the researcher agent and geospatial tools, replacing the previous MCP-based parameter.

Changes

Cohort / File(s) Summary
Map data model
components/map/map-data-context.tsx
Extended MapData interface with optional center [number, number], zoom, and pitch properties.
Map capture & context sync
components/map/mapbox-map.tsx
Captures center/zoom/pitch as a tuple+numbers; updates MapData context in one functional update; effect now depends on setMapData.
Chat submit wiring
components/chat-panel.tsx
Reads mapData from context; conditionally appends map_data JSON to FormData during submit.
Server action parsing
app/actions.tsx
Reads map_data from formData; parses JSON to mapData; updates researcher invocation signature to include mapData and removes mcp.
Researcher agent
lib/agents/researcher.tsx
Signature change: adds dynamicSystemPrompt, mapData; removes mcp. Builds map-aware system prompt and passes mapData into tools.
Tools wiring
lib/agents/tools/index.tsx, lib/agents/tools/geospatial.tsx
ToolProps gains mapData; getTools forwards mapData; geospatialTool signature now accepts optional mapData; expanded tool description; execution flow unchanged.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ChatPanel
  participant MapDataContext
  participant MapboxMap
  participant ServerAction as app/actions.submit
  participant Researcher as researcher()
  participant Tools as getTools()/geospatialTool

  MapboxMap->>MapDataContext: setMapData({ center, zoom, pitch, drawnFeatures })
  User->>ChatPanel: Submit message
  ChatPanel->>MapDataContext: read mapData
  ChatPanel->>ServerAction: FormData(message, map_data?)
  ServerAction->>ServerAction: parse map_data JSON
  ServerAction->>Researcher: researcher(dynamicSystemPrompt, uiStream, streamText, messages, mapData, useSpecificAPI)
  Researcher->>Researcher: build map-aware system prompt (if mapData)
  Researcher->>Tools: getTools({ uiStream, mapData })
  Tools->>Tools: geospatialTool({ uiStream, mapData })
  Tools-->>Researcher: tool result
  Researcher-->>User: streamed response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Review effort 3/5

Poem

A map in my paws, I hop and I plot,
Center and zoom—such a splendid spot!
Pitch in the breeze, features align,
I nibble the data—context so fine.
From chat to the tools, I bound with delight,
Carto-bun guides your queries tonight. 🥕🗺️

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/give-mcp-mapbox-context

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@codiumai-pr-agent-free
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Potential Memory Leak

The captureMapCenter function is now dependent on setMapData but there's no cleanup for the interval that uses this function. This could cause state updates after component unmount.

}, [setMapData])

// Set up idle rotation checker
useEffect(() => {
  const checkIdle = setInterval(() => {
    const idleTime = Date.now() - lastInteractionRef.current
Data Serialization

The code directly stringifies drawn features which may contain complex geometry objects. This could lead to oversized payloads or serialization issues when handling complex map data.

if (mapData.drawnFeatures && mapData.drawnFeatures.length > 0) {
  mapContext += `- Drawn features on map: ${JSON.stringify(mapData.drawnFeatures.map(f => ({ type: f.type, measurement: f.measurement })))} \n`;
}

@qodo-code-review
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 No relevant tests
🔒 Security concerns

Prompt injection / data overexposure:
Including raw map context (especially drawnFeatures via JSON.stringify) in the system prompt could leak sensitive geometry details and expand prompt size for downstream model calls. Consider limiting the fields, truncating arrays, and redacting precise coordinates when not essential.

⚡ Recommended focus areas for review

Possible Runtime Error

Parsing map data with JSON.parse on untrusted form input can throw; add try/catch or validation before use.

const mapDataString = formData?.get('map_data') as string | undefined;
const mapData = mapDataString ? JSON.parse(mapDataString) : undefined;

const content = skip
Type Consistency

Storing center as [lng, lat] tuple; ensure all consumers expect this order and not [lat, lng] to avoid geospatial mix-ups.

  const newCenter: [number, number] = [center.lng, center.lat];
  currentMapCenterRef.current = { center: newCenter, zoom, pitch };
  setMapData(prevData => ({
    ...prevData,
    center: newCenter,
    zoom: zoom,
    pitch: pitch,
  }));
}
Prompt Injection Risk

Directly embedding JSON.stringified drawnFeatures into the system prompt may allow large or noisy content; consider bounding size and sanitizing content included.

let mapContext = '';
if (mapData) {
  mapContext += 'The user is currently viewing a map with the following properties:\n';
  if (mapData.center) {
    mapContext += `- Center: [${mapData.center.join(', ')}]\n`;
  }
  if (mapData.zoom) {
    mapContext += `- Zoom level: ${mapData.zoom}\n`;
  }
  if (mapData.drawnFeatures && mapData.drawnFeatures.length > 0) {
    mapContext += `- Drawn features on map: ${JSON.stringify(mapData.drawnFeatures.map(f => ({ type: f.type, measurement: f.measurement })))} \n`;
  }
}

@qodo-code-review
Copy link
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Validate and sanitize map data

The PR directly parses and forwards mapData from the client (JSON.parse on
formData and inclusion in prompts/tools) without validation or size limits,
which risks runtime errors, prompt bloat, or injection-like prompt manipulation
via crafted drawnFeatures. Introduce a Zod schema (or similar) to validate and
clamp center/zoom/pitch ranges and restrict drawnFeatures shape/length, and cap
or summarize large feature arrays before embedding them into the system prompt
or sending to tools.

Examples:

app/actions.tsx [66-67]
const mapDataString = formData?.get('map_data') as string | undefined;
const mapData = mapDataString ? JSON.parse(mapDataString) : undefined;
lib/agents/researcher.tsx [33-45]
let mapContext = '';
if (mapData) {
  mapContext += 'The user is currently viewing a map with the following properties:\n';
  if (mapData.center) {
    mapContext += `- Center: [${mapData.center.join(', ')}]\n`;
  }
  if (mapData.zoom) {
    mapContext += `- Zoom level: ${mapData.zoom}\n`;
  }
  if (mapData.drawnFeatures && mapData.drawnFeatures.length > 0) {

 ... (clipped 3 lines)

Solution Walkthrough:

Before:

// In 'app/actions.tsx'
const mapDataString = formData.get('map_data');
const mapData = mapDataString ? JSON.parse(mapDataString) : undefined;

// In 'lib/agents/researcher.tsx'
if (mapData) {
  // Directly use mapData properties to build a context string for the prompt
  mapContext += `- Zoom level: ${mapData.zoom}\n`;
  mapContext += `- Drawn features: ${JSON.stringify(mapData.drawnFeatures)}\n`;
}

After:

// Define a Zod schema for map data
const mapDataSchema = z.object({
  center: z.array(z.number()).length(2),
  zoom: z.number().min(0).max(22),
  drawnFeatures: z.array(featureSchema).max(10), // Limit array size
  // ... other properties
});

// In 'app/actions.tsx'
const mapDataString = formData.get('map_data');
const rawMapData = mapDataString ? JSON.parse(mapDataString) : undefined;
const validationResult = mapDataSchema.safeParse(rawMapData);
const mapData = validationResult.success ? validationResult.data : undefined;

// In 'lib/agents/researcher.tsx', mapData is now validated
Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a critical security and robustness flaw where unvalidated client-side mapData is parsed and directly used in the system prompt, risking crashes and prompt injection.

High
Possible issue
Safely parse map_data JSON

Guard JSON parsing to prevent unhandled exceptions if map_data is malformed or
truncated. Wrap parsing in try/catch and ignore or log invalid payloads so chat
submission doesn't crash.

app/actions.tsx [66-67]

 const mapDataString = formData?.get('map_data') as string | undefined;
-const mapData = mapDataString ? JSON.parse(mapDataString) : undefined;
+let mapData: unknown = undefined;
+if (mapDataString) {
+  try {
+    mapData = JSON.parse(mapDataString);
+  } catch {
+    mapData = undefined;
+  }
+}
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that JSON.parse can throw an error on malformed input, and wrapping it in a try-catch block makes the submit action more robust by preventing potential crashes.

Medium
Fix falsy numeric check

Use explicit nullish checks when incorporating numeric fields like zoom. A valid
zoom of 0 would be skipped by a truthy check, leading to incorrect context.

lib/agents/researcher.tsx [39-41]

-if (mapData.zoom) {
+if (mapData.zoom !== undefined && mapData.zoom !== null) {
   mapContext += `- Zoom level: ${mapData.zoom}\n`;
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: This is a valid bug fix, as the original code would incorrectly handle a valid zoom level of 0 as a falsy value, leading to incomplete context information being sent to the AI model.

Medium
  • More

@codiumai-pr-agent-free
Copy link
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Potential security risk in map data

The PR passes raw map data from the client to the AI system without validation
or sanitization. This could allow injection attacks if a malicious user
manipulates the map_data parameter in form submissions. Consider implementing
validation for the map data before passing it to the researcher agent.

Examples:

app/actions.tsx [66-67]
const mapDataString = formData?.get('map_data') as string | undefined;
const mapData = mapDataString ? JSON.parse(mapDataString) : undefined;
lib/agents/researcher.tsx [33-45]
let mapContext = '';
if (mapData) {
  mapContext += 'The user is currently viewing a map with the following properties:\n';
  if (mapData.center) {
    mapContext += `- Center: [${mapData.center.join(', ')}]\n`;
  }
  if (mapData.zoom) {
    mapContext += `- Zoom level: ${mapData.zoom}\n`;
  }
  if (mapData.drawnFeatures && mapData.drawnFeatures.length > 0) {

 ... (clipped 3 lines)

Solution Walkthrough:

Before:

// In actions.tsx
const mapDataString = formData.get('map_data');
const mapData = mapDataString ? JSON.parse(mapDataString) : undefined;

// In researcher.tsx
let mapContext = '';
if (mapData) {
  // ... construct context string directly from mapData properties
  mapContext += `- Drawn features on map: ${JSON.stringify(mapData.drawnFeatures...)} \n`;
}
const systemPrompt = `... ${mapContext} ...`;

After:

// In actions.tsx
const mapDataString = formData.get('map_data');
const rawMapData = mapDataString ? JSON.parse(mapDataString) : undefined;
const mapData = validateAndSanitizeMapData(rawMapData); // New validation step

// In researcher.tsx
let mapContext = '';
if (mapData) {
  // ... construct context string from validated mapData
  mapContext += `- Drawn features on map: ${JSON.stringify(mapData.drawnFeatures...)} \n`;
}
const systemPrompt = `... ${mapContext} ...`;
Suggestion importance[1-10]: 9

__

Why: The suggestion identifies a critical prompt injection vulnerability by highlighting that unsanitized mapData from the client is directly used in the system prompt, which is a significant security risk.

High
General
Improve data serialization

The code directly stringifies complex objects which could lead to overly verbose
or malformed output in the system prompt. Implement a more controlled
serialization of drawn features with proper error handling.

lib/agents/researcher.tsx [33-45]

 let mapContext = '';
 if (mapData) {
   mapContext += 'The user is currently viewing a map with the following properties:\n';
   if (mapData.center) {
     mapContext += `- Center: [${mapData.center.join(', ')}]\n`;
   }
   if (mapData.zoom) {
     mapContext += `- Zoom level: ${mapData.zoom}\n`;
   }
   if (mapData.drawnFeatures && mapData.drawnFeatures.length > 0) {
-    mapContext += `- Drawn features on map: ${JSON.stringify(mapData.drawnFeatures.map(f => ({ type: f.type, measurement: f.measurement })))} \n`;
+    try {
+      const featuresDescription = mapData.drawnFeatures.map(f => 
+        `${f.type} (${f.measurement})`
+      ).join(', ');
+      mapContext += `- Drawn features on map: ${featuresDescription}\n`;
+    } catch (error) {
+      console.error('Error formatting drawn features:', error);
+      mapContext += `- Drawn features on map: ${mapData.drawnFeatures.length} items\n`;
+    }
   }
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion improves the serialization of drawnFeatures for the AI prompt, making it more concise and readable, which can lead to better AI responses. It also adds error handling as a good practice.

Medium
Prevent unnecessary state updates

The map center capture function is called frequently but doesn't check if values
have actually changed before updating state. This can cause unnecessary
re-renders. Add a comparison to only update the state when values have changed.

components/map/mapbox-map.tsx [316-330]

 const captureMapCenter = useCallback(() => {
   if (map.current) {
     const center = map.current.getCenter();
     const zoom = map.current.getZoom();
     const pitch = map.current.getPitch();
     const newCenter: [number, number] = [center.lng, center.lat];
     currentMapCenterRef.current = { center: newCenter, zoom, pitch };
-    setMapData(prevData => ({
-      ...prevData,
-      center: newCenter,
-      zoom: zoom,
-      pitch: pitch,
-    }));
+    
+    setMapData(prevData => {
+      if (prevData.center?.[0] === newCenter[0] && 
+          prevData.center?.[1] === newCenter[1] && 
+          prevData.zoom === zoom && 
+          prevData.pitch === pitch) {
+        return prevData;
+      }
+      return {
+        ...prevData,
+        center: newCenter,
+        zoom: zoom,
+        pitch: pitch,
+      };
+    });
   }
 }, [setMapData])

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies a potential performance issue by preventing unnecessary state updates and re-renders, which is a valuable optimization for a function called within a setInterval.

Low
Possible issue
Add error handling

The code attempts to JSON stringify the entire mapData object which may contain
circular references or non-serializable elements. Add error handling to prevent
form submission failures if serialization fails.

components/chat-panel.tsx [63-65]

-if (mapData) {
-  formData.append('map_data', JSON.stringify(mapData))
+try {
+  if (mapData) {
+    formData.append('map_data', JSON.stringify(mapData))
+  }
+} catch (error) {
+  console.error('Failed to serialize map data:', error);
+  // Still allow form submission without map data
 }
  • Apply / Chat
Suggestion importance[1-10]: 4

__

Why: The suggestion adds error handling for JSON.stringify, which is a good practice. However, the mapData object has a well-defined structure that is unlikely to cause serialization errors, making this a minor improvement.

Low
  • More

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

🔭 Outside diff range comments (2)
lib/agents/tools/geospatial.tsx (1)

145-270: Unused mapData parameter — use it or remove it

geospatialTool currently accepts mapData but never reads it in execute. Verified references show mapData is passed into the tool and is used elsewhere as map context, so either wire it into the tool or remove the param/calls.

Files to review:

  • lib/agents/tools/geospatial.tsx — signature at line ~145 accepts { uiStream, mapData } but mapData is unused.
  • lib/agents/tools/index.tsx — passes mapData into geospatialTool (lines ~24–26).
  • lib/agents/researcher.tsx — builds mapContext from mapData (lines ~34–44) (center, zoom, drawnFeatures).
  • components/map/map-data-context.tsx — MapData shape and provider (defines targetPosition, drawnFeatures, etc.).
  • components/map/map-query-handler.tsx — consumes geospatialTool output and sets mapData (lines ~36–40); geospatialTool could return mapFeature/targetPosition to populate this.

Suggested actions (pick one):

  • Preferred: Use mapData inside geospatialTool.execute to improve results (examples: if params lack coordinates use mapData.center as proximity/default; use mapData.zoom to set search radius; include drawnFeatures as bounding polygon or radius when present; return mapFeature or targetPosition in mcp_response).
  • Alternative: If mapData is intentionally unused, remove it from geospatialTool signature and stop passing it from index.tsx to avoid confusion.
lib/agents/researcher.tsx (1)

64-69: Make dynamicSystemPrompt optional or give it a default to match the fallback logic

The function currently declares dynamicSystemPrompt as a required string but the code treats empty/absent prompts by falling back to default_system_prompt. Either make the parameter optional or give it a default value for clarity/type-safety.

  • Findings:

    • Caller(s): app/actions.tsx calls researcher(currentSystemPrompt, uiStream, streamText, ...) — no callers found that omit the parameter.
    • Declarations that should be aligned with the fallback behavior:
      • lib/agents/researcher.tsx
      • lib/agents/writer.tsx
  • Suggested minimal fixes (example diffs):

lib/agents/researcher.tsx

  • export async function researcher(
  • dynamicSystemPrompt: string, // New parameter
  • export async function researcher(
  • dynamicSystemPrompt: string = '', // New parameter (default to empty)

lib/agents/writer.tsx

  • export async function writer(
  • dynamicSystemPrompt: string, // New parameter
  • export async function writer(
  • dynamicSystemPrompt: string = '', // New parameter (default to empty)

Apply one of the two options:

  • Prefer dynamicSystemPrompt: string = '' to keep the type non-optional and ensure .trim() is safe.
  • Or use dynamicSystemPrompt?: string if you want it explicitly optional.
🧹 Nitpick comments (5)
components/map/map-data-context.tsx (1)

17-19: Document tuple order and consider adding 'bearing' for completeness

  • Add a short comment to clarify the center tuple is [lng, lat] to avoid confusion.
  • If you intend to preserve full camera state like you do with pitch/zoom, consider adding bearing as well.

Apply this small doc tweak within the selected range:

-  center?: [number, number];
+  center?: [number, number]; // [lng, lat]
   zoom?: number;
   pitch?: number;

If you decide to capture bearing too, update the interface accordingly (outside the selected range):

export interface MapData {
  // ...
  center?: [number, number]; // [lng, lat]
  zoom?: number;
  pitch?: number;
  bearing?: number; // optional: map camera bearing in degrees
}
components/chat-panel.tsx (1)

63-65: Be defensive when serializing LngLatLike and avoid redundant check

  • targetPosition is typed as LngLatLike, which could be a class instance. While you currently store arrays, a future update could pass a LngLat object and lead to unexpected JSON output. Consider a replacer to normalize LngLat-like objects to [lng, lat] and keep the payload predictable.
  • Minor: mapData is always defined by the provider, so the if (mapData) guard is redundant.

Here’s an inline replacer to normalize any LngLat-like object and keep the change scoped:

-    if (mapData) {
-      formData.append('map_data', JSON.stringify(mapData))
-    }
+    if (mapData) {
+      const replacer = (key: string, value: any) => {
+        // Normalize Mapbox's LngLat-like objects to [lng, lat]
+        if (key === 'targetPosition' && value && typeof value === 'object' && 'lng' in value && 'lat' in value) {
+          return [value.lng, value.lat]
+        }
+        return value
+      }
+      formData.append('map_data', JSON.stringify(mapData, replacer))
+    }

Optional: Consider excluding map_data from the user-visible message content on the server to avoid bloating message history with large map state (see app/actions.tsx suggestion).

components/map/mapbox-map.tsx (1)

321-331: Good: persist camera state; consider also capturing bearing and avoiding no-op updates

  • LGTM capturing center/zoom/pitch and pushing to context. This enables downstream consumers to tailor behavior based on current view.
  • Enhancement: capture bearing as well (map.current.getBearing()) to preserve full camera state.
  • Minor optimization: compare against currentMapCenterRef before calling setMapData to avoid no-op context updates on small moves.

Apply the bearing addition and a minimal change-detection (bearing requires interface update in MapData):

-      const newCenter: [number, number] = [center.lng, center.lat];
-      currentMapCenterRef.current = { center: newCenter, zoom, pitch };
-      setMapData(prevData => ({
-        ...prevData,
-        center: newCenter,
-        zoom: zoom,
-        pitch: pitch,
-      }));
+      const newCenter: [number, number] = [center.lng, center.lat]
+      const bearing = map.current.getBearing()
+      const prev = currentMapCenterRef.current
+      const changed =
+        prev.center[0] !== newCenter[0] ||
+        prev.center[1] !== newCenter[1] ||
+        prev.zoom !== zoom ||
+        prev.pitch !== pitch
+      if (changed) {
+        currentMapCenterRef.current = { center: newCenter, zoom, pitch }
+        setMapData(prevData => ({
+          ...prevData,
+          center: newCenter,
+          zoom,
+          pitch,
+          // bearing, // uncomment after adding to MapData
+        }))
+      }
lib/agents/tools/index.tsx (1)

11-12: Plumbing looks correct

  • ToolProps growth to include mapData and wiring it through to geospatialQueryTool is consistent.
  • After switching to a type-only import, this remains type-safe and server-friendly.

If geospatialTool doesn’t need fullResponse, you can drop it from ToolProps and callers for minimal surface area. Not a blocker.

Also applies to: 14-15, 25-27

lib/agents/researcher.tsx (1)

42-44: Consider improving drawnFeatures display format.

While the current JSON.stringify approach works, the output might be verbose and less readable in the system prompt. Consider a more concise format.

-    if (mapData.drawnFeatures && mapData.drawnFeatures.length > 0) {
-      mapContext += `- Drawn features on map: ${JSON.stringify(mapData.drawnFeatures.map(f => ({ type: f.type, measurement: f.measurement })))} \n`;
-    }
+    if (mapData.drawnFeatures && mapData.drawnFeatures.length > 0) {
+      const features = mapData.drawnFeatures.map(f => `${f.type} (${f.measurement})`).join(', ');
+      mapContext += `- Drawn features on map: ${features}\n`;
+    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 96c0b81 and 0d72b67.

📒 Files selected for processing (7)
  • app/actions.tsx (2 hunks)
  • components/chat-panel.tsx (3 hunks)
  • components/map/map-data-context.tsx (1 hunks)
  • components/map/mapbox-map.tsx (1 hunks)
  • lib/agents/researcher.tsx (3 hunks)
  • lib/agents/tools/geospatial.tsx (2 hunks)
  • lib/agents/tools/index.tsx (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (7)
components/chat-panel.tsx (4)
components/map/map-data-context.tsx (1)
  • useMapData (39-45)
components/chat.tsx (2)
  • Chat (20-123)
  • id (67-72)
components/map/map-query-handler.tsx (1)
  • useMapData (30-83)
app/page.tsx (1)
  • Page (9-18)
components/map/map-data-context.tsx (1)
components/map/map-query-handler.tsx (2)
  • prevData (39-49)
  • MapQueryHandlerProps (25-28)
components/map/mapbox-map.tsx (1)
components/map/map-query-handler.tsx (2)
  • useMapData (30-83)
  • prevData (39-49)
app/actions.tsx (1)
components/map/map-query-handler.tsx (4)
  • prevData (39-49)
  • McpResponseData (8-16)
  • toolOutput (33-74)
  • prevData (53-57)
lib/agents/tools/index.tsx (2)
components/map/map-data-context.tsx (1)
  • MapData (7-20)
components/map/map-query-handler.tsx (4)
  • MapQueryHandlerProps (25-28)
  • GeospatialToolOutput (18-23)
  • useMapData (30-83)
  • toolOutput (33-74)
lib/agents/tools/geospatial.tsx (2)
components/map/map-data-context.tsx (1)
  • MapData (7-20)
components/map/map-query-handler.tsx (3)
  • GeospatialToolOutput (18-23)
  • toolOutput (33-74)
  • prevData (39-49)
lib/agents/researcher.tsx (2)
components/map/map-data-context.tsx (1)
  • MapData (7-20)
components/map/map-query-handler.tsx (4)
  • prevData (39-49)
  • useMapData (30-83)
  • toolOutput (33-74)
  • prevData (53-57)
🔇 Additional comments (9)
app/actions.tsx (1)

155-156: LGTM: researcher receives mapData

Passing mapData down the stack aligns with the PR’s goal and matches the updated researcher signature.

lib/agents/tools/geospatial.tsx (3)

5-5: LGTM - Good addition of MapData import.

The import of MapData interface provides proper type safety for the new mapData parameter.


145-145: LGTM - Function signature updated appropriately.

The function signature now accepts mapData as an optional parameter with proper typing. The destructuring pattern is clean and maintains backwards compatibility.


146-152: Enhanced tool description looks comprehensive.

The expanded description covers a broader range of geospatial capabilities including distance calculations, direction queries, and geographic information lookup, which aligns well with the tool's actual functionality.

lib/agents/researcher.tsx (5)

14-14: LGTM - Appropriate import addition.

The MapData import is correctly added to support the new parameter.


16-23: Function signature update looks good.

The signature changes are well-structured:

  • Added dynamicSystemPrompt: string parameter for flexibility
  • Added mapData: MapData | undefined parameter with proper optional typing
  • Parameters are logically ordered and maintain backwards compatibility where possible

33-45: Well-implemented mapContext construction.

The logic for building map context from mapData is clean and handles optional properties appropriately:

  • Properly checks for mapData existence before processing
  • Handles optional properties (center, zoom, drawnFeatures) with individual checks
  • Uses appropriate formatting for coordinate display and feature serialization

50-62: Comprehensive tool usage guide with good examples.

The updated system prompt provides clear guidance on when to use the geospatialQueryTool with relevant examples. The integration of mapContext at the beginning ensures the AI has proper map state awareness.


71-75: Proper mapData propagation to tools.

The mapData parameter is correctly passed through to the getTools function, ensuring tools have access to the current map context.

Comment on lines +66 to +68
const mapDataString = formData?.get('map_data') as string | undefined;
const mapData = mapDataString ? JSON.parse(mapDataString) : undefined;

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

Untrusted JSON.parse on client-provided map_data can crash the action

map_data comes from the browser. A malformed payload will throw and break the request. Wrap in try/catch and default to undefined.

Apply this defensive parsing:

-  const mapDataString = formData?.get('map_data') as string | undefined;
-  const mapData = mapDataString ? JSON.parse(mapDataString) : undefined;
+  const mapDataString = formData?.get('map_data') as string | undefined
+  let mapData: unknown = undefined
+  if (mapDataString) {
+    try {
+      mapData = JSON.parse(mapDataString)
+    } catch (e) {
+      console.warn('submit(): Ignoring invalid map_data JSON in formData', e)
+      mapData = undefined
+    }
+  }

Optional follow-up: avoid including the raw map_data blob in the persisted user message content (JSON.stringify(Object.fromEntries(formData))) to prevent inflating message history and storage. Instead, construct the user-visible content from a filtered copy of FormData that excludes map_data. I can provide a patch if you want that change now.

📝 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
const mapDataString = formData?.get('map_data') as string | undefined;
const mapData = mapDataString ? JSON.parse(mapDataString) : undefined;
const mapDataString = formData?.get('map_data') as string | undefined
let mapData: unknown = undefined
if (mapDataString) {
try {
mapData = JSON.parse(mapDataString)
} catch (e) {
console.warn('submit(): Ignoring invalid map_data JSON in formData', e)
mapData = undefined
}
}
🤖 Prompt for AI Agents
In app/actions.tsx around lines 66 to 68, the code directly calls JSON.parse on
client-provided map_data which can throw on malformed input; update the code to
wrap the JSON.parse call in a try/catch and set mapData to undefined on parse
errors (optionally log/debug the error), and when building persisted user
message content (JSON.stringify(Object.fromEntries(formData))) exclude the raw
map_data entry by creating a filtered copy of FormData that omits 'map_data' so
the large blob isn't stored.

import { videoSearchTool } from './video-search'
import { geospatialTool } from './geospatial' // Removed useGeospatialToolMcp import
import { geospatialTool } from './geospatial'
import { MapData } from '@/components/map/map-data-context'
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

Import MapData as a type to avoid bundling a client module on the server

MapData is declared in a 'use client' file under components. Importing it as a value risks pulling a client-only module (and its deps like mapbox-gl) into server bundles. Use a type-only import, or better, move the type to a shared, non-client module (e.g., lib/types/map.ts) and import from there.

Apply the minimal fix:

-import { MapData } from '@/components/map/map-data-context'
+import type { MapData } from '@/components/map/map-data-context'

Longer-term, extract MapData into a server-safe shared types module (e.g., lib/types/map.ts) and update all imports in lib/* to reference that instead of a client component file.

📝 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
import { MapData } from '@/components/map/map-data-context'
import type { MapData } from '@/components/map/map-data-context'
🤖 Prompt for AI Agents
In lib/agents/tools/index.tsx around line 6, the file imports MapData from a
client-side component which can pull client-only modules into server bundles;
update the import to be type-only by replacing the value import with a type
import (e.g., "import type { MapData } from
'@/components/map/map-data-context'") or, preferably for a longer-term fix, move
the MapData type to a shared server-safe module like lib/types/map.ts and import
the type from there across lib/* so no client component is bundled on the
server.

@ngoiyaeric ngoiyaeric closed this Oct 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants