-
-
Notifications
You must be signed in to change notification settings - Fork 7
feat: Connect generated map to Mapbox MCP workflow #231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,7 +4,7 @@ import { useEffect, useState, useRef } from 'react' | |||||||||||||||||||||||||||||||||||||
| import { useRouter } from 'next/navigation' | ||||||||||||||||||||||||||||||||||||||
| import type { AI, UIState } from '@/app/actions' | ||||||||||||||||||||||||||||||||||||||
| import { useUIState, useActions } from 'ai/rsc' | ||||||||||||||||||||||||||||||||||||||
| // Removed import of useGeospatialToolMcp as it's no longer used/available | ||||||||||||||||||||||||||||||||||||||
| import { useMCPMapClient } from '@/mapbox_mcp/hooks' | ||||||||||||||||||||||||||||||||||||||
| import { cn } from '@/lib/utils' | ||||||||||||||||||||||||||||||||||||||
| import { UserMessage } from './user-message' | ||||||||||||||||||||||||||||||||||||||
| import { Button } from './ui/button' | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -21,7 +21,7 @@ interface ChatPanelProps { | |||||||||||||||||||||||||||||||||||||
| export function ChatPanel({ messages, input, setInput }: ChatPanelProps) { | ||||||||||||||||||||||||||||||||||||||
| const [, setMessages] = useUIState<typeof AI>() | ||||||||||||||||||||||||||||||||||||||
| const { submit } = useActions() | ||||||||||||||||||||||||||||||||||||||
| // Removed mcp instance as it's no longer passed to submit | ||||||||||||||||||||||||||||||||||||||
| const { processLocationQuery } = useMCPMapClient() | ||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Establish MCP connection lifecycle to avoid "MCP client not connected" errors.
Apply this diff to pull in - const { processLocationQuery } = useMCPMapClient()
+ const { processLocationQuery, connect } = useMCPMapClient()Add this effect (outside the changed hunk): useEffect(() => {
let mounted = true
;(async () => {
try {
await connect()
} catch (e) {
console.warn('MCP connect failed, continuing without map location:', e)
}
})()
return () => {
mounted = false
}
}, [connect])🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| const [isButtonPressed, setIsButtonPressed] = useState(false) | ||||||||||||||||||||||||||||||||||||||
| const [isMobile, setIsMobile] = useState(false) | ||||||||||||||||||||||||||||||||||||||
| const inputRef = useRef<HTMLTextAreaElement>(null) | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -57,9 +57,11 @@ export function ChatPanel({ messages, input, setInput }: ChatPanelProps) { | |||||||||||||||||||||||||||||||||||||
| component: <UserMessage message={input} /> | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| ]) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const { mapLocation } = await processLocationQuery(input) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const formData = new FormData(e.currentTarget) | ||||||||||||||||||||||||||||||||||||||
| // Removed mcp argument from submit call | ||||||||||||||||||||||||||||||||||||||
| const responseMessage = await submit(formData) | ||||||||||||||||||||||||||||||||||||||
| const responseMessage = await submit(formData, mapLocation) | ||||||||||||||||||||||||||||||||||||||
| setMessages(currentMessages => [...currentMessages, responseMessage as any]) | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+61
to
65
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Guard MCP failures and gracefully fall back to submit without mapLocation. If MCP isn’t ready or processing fails, current code will throw and block the chat flow. Apply this diff: - const { mapLocation } = await processLocationQuery(input)
+ let mapLocation: any = undefined
+ try {
+ const res = await processLocationQuery(input)
+ mapLocation = res?.mapLocation
+ } catch (e) {
+ console.warn('processLocationQuery failed, submitting without mapLocation:', e)
+ }
const formData = new FormData(e.currentTarget)
const responseMessage = await submit(formData, mapLocation)📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,65 @@ | ||||||||||||
| 'use client' | ||||||||||||
|
|
||||||||||||
| import { useEffect, useRef } from 'react' | ||||||||||||
| import mapboxgl from 'mapbox-gl' | ||||||||||||
| import 'mapbox-gl/dist/mapbox-gl.css' | ||||||||||||
|
|
||||||||||||
| mapboxgl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN as string | ||||||||||||
|
|
||||||||||||
| export const GeneratedMap: React.FC<{ | ||||||||||||
| position: { latitude: number; longitude: number } | ||||||||||||
| }> = ({ position }) => { | ||||||||||||
| const mapContainer = useRef<HTMLDivElement>(null) | ||||||||||||
| const map = useRef<mapboxgl.Map | null>(null) | ||||||||||||
|
|
||||||||||||
|
Comment on lines
+9
to
+14
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix duplicate/unfinished component declaration (parsing/build will fail). There’s a half-defined Apply this diff: -export const GeneratedMap: React.FC<{
- position: { latitude: number; longitude: number }
-}> = ({ position }) => {
- const mapContainer = useRef<HTMLDivElement>(null)
- const map = useRef<mapboxgl.Map | null>(null)
-📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
| export const GeneratedMap: React.FC<{ | ||||||||||||
| position: { latitude: number; longitude: number; zoom?: number } | ||||||||||||
| }> = ({ position }) => { | ||||||||||||
| const mapContainer = useRef<HTMLDivElement>(null) | ||||||||||||
| const map = useRef<mapboxgl.Map | null>(null) | ||||||||||||
| const markerRef = useRef<mapboxgl.Marker | null>(null) | ||||||||||||
|
|
||||||||||||
| // Initialize map and marker once, and clean up on unmount | ||||||||||||
| useEffect(() => { | ||||||||||||
| if (!mapContainer.current || map.current) return | ||||||||||||
| if (!mapboxgl.accessToken) { | ||||||||||||
| console.warn('GeneratedMap: NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN is not set.') | ||||||||||||
| return | ||||||||||||
| } | ||||||||||||
| map.current = new mapboxgl.Map({ | ||||||||||||
| container: mapContainer.current, | ||||||||||||
| style: 'mapbox://styles/mapbox/streets-v12', | ||||||||||||
| center: [position.longitude, position.latitude], | ||||||||||||
| zoom: position.zoom ?? 12 | ||||||||||||
| }) | ||||||||||||
| markerRef.current = new mapboxgl.Marker() | ||||||||||||
| .setLngLat([position.longitude, position.latitude]) | ||||||||||||
| .addTo(map.current) | ||||||||||||
|
|
||||||||||||
| return () => { | ||||||||||||
| markerRef.current?.remove() | ||||||||||||
| markerRef.current = null | ||||||||||||
| map.current?.remove() | ||||||||||||
| map.current = null | ||||||||||||
| } | ||||||||||||
| }, []) | ||||||||||||
|
|
||||||||||||
| // Update map center/zoom and marker when position changes | ||||||||||||
| useEffect(() => { | ||||||||||||
| if (!map.current) return | ||||||||||||
| const lngLat: [number, number] = [position.longitude, position.latitude] | ||||||||||||
| map.current.flyTo({ | ||||||||||||
| center: lngLat, | ||||||||||||
| zoom: position.zoom ?? map.current.getZoom(), | ||||||||||||
| essential: true | ||||||||||||
| }) | ||||||||||||
| if (markerRef.current) { | ||||||||||||
| markerRef.current.setLngLat(lngLat) | ||||||||||||
| } else { | ||||||||||||
| markerRef.current = new mapboxgl.Marker() | ||||||||||||
| .setLngLat(lngLat) | ||||||||||||
| .addTo(map.current) | ||||||||||||
| } | ||||||||||||
| }, [position]) | ||||||||||||
| return <div ref={mapContainer} className="h-full w-full" /> | ||||||||||||
| } | ||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Confirm all submit(...) call sites updated for the new signature
submit now accepts (formData?: FormData, mapLocation?: object, skip?: boolean). Ensure all callers (e.g., chat-panel) pass mapLocation as the second arg. If any call site still passes skip as second arg, behavior will be off.
Run this to find submit invocations and quickly spot incorrect arity/ordering:
🏁 Script executed:
Length of output: 74
Let’s retry searching without relying on
--type. This will scan all.tsand.tsxfiles:🏁 Script executed:
Length of output: 2179
Ensure submit call in components/copilot.tsx passes
mapLocationbeforeskipThe only incorrect invocation is in components/copilot.tsx (line 83), which currently does:
This assigns your boolean
skipto themapLocationparameter, leavingskipundefined. Update it to explicitly pass a (possibly undefined)mapLocationas the second argument:components/copilot.tsx:83
All other call sites either only pass
formDataor correctly pass(formData, mapLocation).🤖 Prompt for AI Agents