Skip to content
This repository was archived by the owner on May 21, 2026. It is now read-only.

Commit 6a1e392

Browse files
committed
feat(inspector): enhance routing and server connection handling
- Update Router in App component to use basename for inspector path - Modify InspectorDashboard to simplify server URL input placeholder - Improve error display for connection issues with clearer formatting - Ensure server IDs are properly encoded in links across components - Add URL decoding for server IDs in Layout and ServerDetail components - Implement favicon proxy endpoint in middleware for improved asset handling - Clean up invalid connections from localStorage in McpProvider - Remove outdated React example files and configurations from mcp-use
1 parent 0f7a05f commit 6a1e392

19 files changed

Lines changed: 99 additions & 1286 deletions

packages/inspector/src/client/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { McpProvider } from './context/McpContext'
88
function App() {
99
return (
1010
<McpProvider>
11-
<Router>
11+
<Router basename="/inspector">
1212
<Layout>
1313
<Routes>
1414
<Route path="/" element={<InspectorDashboard />} />

packages/inspector/src/client/components/InspectorDashboard.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export function InspectorDashboard() {
130130
/>
131131
<div className="flex space-x-2">
132132
<Input
133-
placeholder="Enter server URL (e.g., https://mcp.linear.app/sse)"
133+
placeholder="Enter server URL"
134134
value={newServerUrl}
135135
onChange={e => setNewServerUrl(e.target.value)}
136136
onKeyPress={e => e.key === 'Enter' && handleAddConnection()}
@@ -199,7 +199,9 @@ export function InspectorDashboard() {
199199
</div>
200200
{connection.error && (
201201
<div className="text-sm text-red-600 mt-2">
202-
Error: {connection.error}
202+
Error:
203+
{' '}
204+
{connection.error}
203205
</div>
204206
)}
205207
{connection.state === 'pending_auth' && connection.authUrl && (
@@ -236,7 +238,7 @@ export function InspectorDashboard() {
236238
)}
237239
</div>
238240
<Button asChild variant="outline" size="sm">
239-
<Link to={`/servers/${connection.id}`}>
241+
<Link to={`/servers/${encodeURIComponent(connection.id)}`}>
240242
Inspect
241243
</Link>
242244
</Button>

packages/inspector/src/client/components/Layout.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export function Layout({ children }: LayoutProps) {
9797

9898
const handleServerSelect = (serverId: string) => {
9999
setSelectedServerId(serverId)
100-
navigate(`/servers/${serverId}`)
100+
navigate(`/servers/${encodeURIComponent(serverId)}`)
101101
}
102102

103103
const selectedServer = connections.find(c => c.id === selectedServerId)
@@ -116,10 +116,7 @@ export function Layout({ children }: LayoutProps) {
116116
const existing = connections.find(c => c.url === config.autoConnectUrl)
117117
if (!existing) {
118118
// Auto-connect to the local server
119-
addConnection({
120-
name: 'Local MCP Server',
121-
url: config.autoConnectUrl,
122-
})
119+
addConnection(config.autoConnectUrl, 'Local MCP Server')
123120
}
124121
}
125122
})
@@ -134,8 +131,9 @@ export function Layout({ children }: LayoutProps) {
134131
const serverIdFromRoute = location.pathname.split('/servers/')[1]
135132

136133
if (isServerRoute && serverIdFromRoute) {
137-
// If we're on a server route, set the selected server
138-
setSelectedServerId(serverIdFromRoute)
134+
// Decode the server ID from the route
135+
const decodedServerId = decodeURIComponent(serverIdFromRoute)
136+
setSelectedServerId(decodedServerId)
139137
}
140138
else if (!isServerRoute) {
141139
// If we're not on a server route, clear the selected server

packages/inspector/src/client/components/ServerDetail.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import { useMcpContext } from '../context/McpContext'
99
export function ServerDetail() {
1010
const { serverId } = useParams()
1111
const { getConnection } = useMcpContext()
12-
const connection = getConnection(serverId || '')
12+
const decodedServerId = serverId ? decodeURIComponent(serverId) : ''
13+
const connection = getConnection(decodedServerId)
1314

1415
const [selectedTool, setSelectedTool] = useState<string | null>(null)
1516
const [toolInput, setToolInput] = useState('{}')

packages/inspector/src/client/components/ServerIcon.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export function ServerIcon({
3737

3838
try {
3939
const encodedUrl = encodeURIComponent(serverUrl)
40-
const proxyUrl = `/api/favicon/${encodedUrl}`
40+
const proxyUrl = `/inspector/api/favicon/${encodedUrl}`
4141

4242
// Test if favicon exists
4343
const response = await fetch(proxyUrl)

packages/inspector/src/client/components/ServerList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export function ServerList() {
124124
</div>
125125
<div className="flex items-center space-x-2">
126126
<Button asChild variant="outline" size="sm">
127-
<Link to={`/servers/${connection.id}`}>Inspect</Link>
127+
<Link to={`/servers/${encodeURIComponent(connection.id)}`}>Inspect</Link>
128128
</Button>
129129
<Button
130130
variant="outline"

packages/inspector/src/client/context/McpContext.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,30 @@ export function McpProvider({ children }: { children: ReactNode }) {
104104
if (saved) {
105105
try {
106106
const parsed = JSON.parse(saved)
107-
setSavedConnections(parsed)
107+
// Validate and filter out invalid connections
108+
const validConnections = Array.isArray(parsed)
109+
? parsed.filter((conn: any) => {
110+
// Ensure connection has valid structure with string url and id
111+
return conn
112+
&& typeof conn === 'object'
113+
&& typeof conn.id === 'string'
114+
&& typeof conn.url === 'string'
115+
&& typeof conn.name === 'string'
116+
})
117+
: []
118+
119+
// If we filtered out any invalid connections, update localStorage
120+
if (validConnections.length !== parsed.length) {
121+
console.warn('Cleaned up invalid connections from localStorage')
122+
localStorage.setItem('mcp-inspector-connections', JSON.stringify(validConnections))
123+
}
124+
125+
setSavedConnections(validConnections)
108126
}
109127
catch (error) {
110128
console.error('Failed to parse saved connections:', error)
129+
// Clear corrupted localStorage
130+
localStorage.removeItem('mcp-inspector-connections')
111131
}
112132
}
113133
}, [])

packages/inspector/src/server/middleware.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,69 @@ export function mountInspector(app: Express, path: string = '/inspector', mcpSer
4343
})
4444
})
4545

46+
// Favicon proxy endpoint
47+
app.get(`${basePath}/api/favicon/:url`, async (req: Request, res: Response) => {
48+
const url = req.params.url
49+
50+
if (!url) {
51+
res.status(400).json({ error: 'URL parameter is required' })
52+
return
53+
}
54+
55+
try {
56+
// Decode the URL
57+
const decodedUrl = decodeURIComponent(url)
58+
59+
// Add protocol if missing
60+
let fullUrl = decodedUrl
61+
if (!decodedUrl.startsWith('http://') && !decodedUrl.startsWith('https://')) {
62+
fullUrl = `https://${decodedUrl}`
63+
}
64+
65+
// Validate URL
66+
const urlObj = new URL(fullUrl)
67+
68+
// Try to fetch favicon from common locations
69+
const faviconUrls = [
70+
`${urlObj.origin}/favicon.ico`,
71+
`${urlObj.origin}/favicon.png`,
72+
`${urlObj.origin}/apple-touch-icon.png`,
73+
]
74+
75+
for (const faviconUrl of faviconUrls) {
76+
try {
77+
const response = await fetch(faviconUrl, {
78+
headers: {
79+
'User-Agent': 'Mozilla/5.0 (compatible; MCP-Inspector/1.0)',
80+
},
81+
})
82+
83+
if (response.ok) {
84+
const contentType = response.headers.get('content-type') || 'image/x-icon'
85+
const buffer = await response.arrayBuffer()
86+
87+
res.setHeader('Content-Type', contentType)
88+
res.setHeader('Cache-Control', 'public, max-age=86400')
89+
res.setHeader('Access-Control-Allow-Origin', '*')
90+
res.send(Buffer.from(buffer))
91+
return
92+
}
93+
}
94+
catch {
95+
// Continue to next URL
96+
continue
97+
}
98+
}
99+
100+
// If no favicon found, return 404
101+
res.status(404).json({ error: 'No favicon found' })
102+
}
103+
catch (error) {
104+
console.error('Favicon proxy error:', error)
105+
res.status(400).json({ error: 'Invalid URL or fetch failed' })
106+
}
107+
})
108+
46109
// Serve static assets
47110
app.use(`${basePath}/assets`, (_req: Request, res: Response) => {
48111
const assetPath = join(clientDistPath, 'assets', _req.path)

packages/mcp-use/examples/react/README.md

Lines changed: 0 additions & 132 deletions
This file was deleted.

packages/mcp-use/examples/react/index.html

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)