Skip to Content
FeaturesMCP Servers

Configuring MCP Servers

The Model Context Protocol (MCP) is an open standard that allows AI assistants to securely connect to external data sources and services. MCP enables your chat assistant to access real-time information, interact with APIs, query databases, and perform actions across various systems.

AI Chat Bootstrap provides full support for MCP through the useMCPServer hook and createMcpToolsHandler server handler.

How MCP Works

MCP servers expose tools (functions) that the AI can discover and invoke. When you configure an MCP server:

  1. The client registers the server with useMCPServer
  2. The hook fetches available tools from your backend via /api/mcp-discovery
  3. The backend connects to the MCP server and returns tool summaries
  4. Tools are automatically included in the AI’s system prompt
  5. When the AI needs to use a tool, it calls your chat API
  6. The backend executes the tool via MCP and returns the result

Quick Start

1. Set up the MCP Discovery Endpoint

The scaffold automatically creates /api/mcp-discovery/route.ts:

import { createMcpToolsHandler } from "ai-chat-bootstrap/server"; const handler = createMcpToolsHandler({ onError: (error) => { console.error("[mcp-api]", error); }, }); export { handler as POST };

This endpoint receives MCP server configurations from the client and returns available tools.

2. Register an MCP Server on the Client

Use useMCPServer to connect to an MCP server:

"use client"; import { ChatContainer, useMCPServer } from "ai-chat-bootstrap"; export function ChatWithMCP() { const mcpServer = useMCPServer({ url: "http://localhost:3030/mcp", name: "Local Tools", transportType: "sse", // or "streamable-http" }); return ( <div> {mcpServer.isLoading && <p>Loading MCP tools...</p>} {mcpServer.error && <p>Error: {mcpServer.error}</p>} {mcpServer.tools.length > 0 && ( <p>{mcpServer.tools.length} tools available</p> )} <ChatContainer transport={{ api: "/api/chat" }} mcp={{ enabled: true, api: "/api/mcp-discovery" }} /> </div> ); }

3. Enable MCP in Your Chat Handler

Make sure your chat handler has MCP enabled:

import { createAIChatHandler } from "ai-chat-bootstrap/server"; import { openai } from "@ai-sdk/openai"; const handler = createAIChatHandler({ model: openai("gpt-4o"), mcpEnabled: true, // Important! }); export { handler as POST };

Configuration Options

useMCPServer Options

OptionTypeDefaultDescription
urlstringMCP server URL (required)
namestringundefinedFriendly server name shown in UI
transportType"sse" | "streamable-http""sse"Protocol for connecting to MCP server
headersRecord<string, string>{}Extra headers forwarded to the MCP server
apistring"/api/mcp-discovery"Your backend discovery endpoint
autoFetchToolsbooleantrueAutomatically fetch tools on mount
idstringurlOverride the server identifier

Multiple MCP Servers

You can connect to multiple MCP servers simultaneously:

function MultiMCPChat() { const weatherServer = useMCPServer({ url: "http://localhost:3030/mcp", name: "Weather Tools", }); const databaseServer = useMCPServer({ url: "http://localhost:3031/mcp", name: "Database Tools", }); return ( <div> <div className="space-y-2 mb-4"> <ServerStatus server={weatherServer} /> <ServerStatus server={databaseServer} /> </div> <ChatContainer transport={{ api: "/api/chat" }} mcp={{ enabled: true }} /> </div> ); } function ServerStatus({ server }) { return ( <div className="flex items-center gap-2 text-sm"> <span className="font-medium">{server.name}:</span> {server.isLoading ? ( <span>Loading...</span> ) : server.error ? ( <span className="text-red-500">{server.error}</span> ) : ( <span className="text-green-500"> {server.tools.length} tools available </span> )} {server.error && ( <button onClick={server.refresh} className="underline"> Retry </button> )} </div> ); }

Authentication & Headers

Client-Side Headers

Pass headers that are safe to expose on the client:

useMCPServer({ url: "https://api.example.com/mcp", headers: { "X-Client-Version": "1.0.0", }, });

Server-Side Headers (Secure)

For sensitive credentials, use forwardHeaders in your MCP handler to inject server-side environment variables:

// /api/mcp-discovery/route.ts import { createMcpToolsHandler } from "ai-chat-bootstrap/server"; const handler = createMcpToolsHandler({ forwardHeaders: ["authorization"], onError: (error) => { console.error("[mcp-api]", error); }, }); export { handler as POST };

Then set the header in your request middleware or pass it from your chat API.

Environment Variables

MCP server URLs can be configured via environment variables:

# .env.local NEXT_PUBLIC_MCP_SERVER_URL=http://localhost:3030/mcp MCP_API_KEY=your-secret-key
useMCPServer({ url: process.env.NEXT_PUBLIC_MCP_SERVER_URL!, name: "Local Tools", });

Tool Discovery & Prompts

When MCP servers are registered, their tools are automatically:

  1. Listed in the system prompt with [MCP] badges
  2. Available for the AI to invoke via the standard tool calling mechanism
  3. Executed server-side through the MCP transport

The AI receives information like:

## Tools **Model Context Protocol (MCP)**: Some tools are provided via MCP, an open standard that allows AI assistants to securely connect to external data sources and services. MCP tools enable you to access real-time information, interact with APIs, query databases, and perform actions across various systems. **Available MCP Tools:** - **weather_get_forecast**: Get weather forecast for a location - **database_query**: Execute a read-only database query - **file_read**: Read contents of a file

Error Handling

The useMCPServer hook provides error state management:

const server = useMCPServer({ url: "..." }); if (server.error) { return ( <div className="error-banner"> <p>Failed to connect to MCP server: {server.error}</p> <button onClick={server.refresh}>Retry Connection</button> </div> ); }

Partial Tool Loading

Even if some tools fail to load, successfully loaded tools will still be available:

// Server returns: // { tools: [tool1, tool2], errors: [{ message: "tool3 failed" }] } // The hook will: // - Set tools to [tool1, tool2] // - Set error to "tool3 failed" // - Allow the chat to continue with available tools

Dynamic Server Configuration

Let users configure MCP servers at runtime:

"use client"; import { useState } from "react"; import { ChatContainer, useMCPServer } from "ai-chat-bootstrap"; export function DynamicMCPChat() { const [serverUrl, setServerUrl] = useState(""); const [enabledServers, setEnabledServers] = useState<string[]>([]); return ( <div> <div className="mb-4"> <input type="text" value={serverUrl} onChange={(e) => setServerUrl(e.target.value)} placeholder="Enter MCP server URL" /> <button onClick={() => { if (serverUrl) { setEnabledServers([...enabledServers, serverUrl]); setServerUrl(""); } }} > Add Server </button> </div> {enabledServers.map((url) => ( <MCPServerConnection key={url} url={url} /> ))} <ChatContainer transport={{ api: "/api/chat" }} mcp={{ enabled: true }} /> </div> ); } function MCPServerConnection({ url }: { url: string }) { const server = useMCPServer({ url }); return <div>Connected to {url}</div>; }

Custom Tool Rendering

MCP tools support custom React rendering, allowing you to display rich, interactive UI components for tool results instead of plain text. This works similarly to frontend tool rendering, but for tools provided by MCP servers.

Basic Usage

Pass toolRenderers to the mcp prop to register custom renderers:

"use client"; import { ChatContainer } from "ai-chat-bootstrap"; export function ChatWithMCPRendering() { return ( <ChatContainer transport={{ api: "/api/chat" }} mcp={{ enabled: true, servers: [ { id: "weather-server", transport: { type: "streamable-http", url: "http://localhost:3030/mcp", }, }, ], toolRenderers: [ { serverUrl: "http://localhost:3030/mcp", toolName: "get_weather", render: (result) => { // Parse MCP response format const data = JSON.parse(result.content[0].text); return ( <div className="p-4 bg-card rounded-lg border"> <div className="flex items-center justify-between"> <h3 className="text-lg font-semibold">{data.location}</h3> <span className="text-3xl font-bold">{data.temperature}</span> </div> <p className="text-sm text-muted-foreground">{data.conditions}</p> {data.humidity && ( <div className="text-xs text-muted-foreground mt-2"> Humidity: {data.humidity}% </div> )} </div> ); }, }, ], }} /> ); }

Understanding MCP Response Format

MCP tools return results in a standardized format:

{ content: [ { type: "text", text: '{"location":"San Francisco","temperature":"72°F","conditions":"Sunny"}' } ] }

Your renderer receives this entire object and should parse the JSON from content[0].text:

render: (result) => { // Extract and parse JSON from MCP response let data: Record<string, unknown> = {}; try { const mcpResult = result as { content?: Array<{ type: string; text: string }> }; if (mcpResult.content?.[0]?.text) { data = JSON.parse(mcpResult.content[0].text); } } catch (error) { console.error("Failed to parse MCP result:", error); // Fallback to raw result data = result as Record<string, unknown>; } return <YourComponent {...data} />; }

Multiple Tool Renderers

Register renderers for multiple tools from different servers:

<ChatContainer mcp={{ enabled: true, servers: [ { id: "weather-server", transport: { type: "sse", url: "http://localhost:3030/mcp" }, }, { id: "calendar-server", transport: { type: "sse", url: "http://localhost:3031/mcp" }, }, ], toolRenderers: [ { serverUrl: "http://localhost:3030/mcp", toolName: "get_weather", render: (result) => <WeatherCard data={parseResult(result)} />, }, { serverUrl: "http://localhost:3031/mcp", toolName: "list_events", render: (result) => <CalendarEvents events={parseResult(result)} />, }, { serverUrl: "http://localhost:3031/mcp", toolName: "create_meeting", render: (result) => <MeetingConfirmation meeting={parseResult(result)} />, }, ], }} />

Tool Uniqueness

Tool renderers are matched using a composite key of serverUrl + toolName. This guarantees uniqueness even when multiple MCP servers expose tools with the same name:

// These are treated as different tools: // Server 1: http://localhost:3030/mcp -> "search" // Server 2: http://localhost:3031/mcp -> "search" toolRenderers: [ { serverUrl: "http://localhost:3030/mcp", toolName: "search", render: (result) => <WebSearchResults {...parseResult(result)} />, }, { serverUrl: "http://localhost:3031/mcp", toolName: "search", render: (result) => <DatabaseSearchResults {...parseResult(result)} />, }, ]

Complete Example

Here’s a full example with error handling and multiple renderers:

"use client"; import React from "react"; import { ChatContainer, useMCPServer } from "ai-chat-bootstrap"; // Helper to parse MCP results function parseMCPResult(result: unknown): Record<string, unknown> { try { const mcpResult = result as { content?: Array<{ type: string; text: string }> }; if (mcpResult.content?.[0]?.text) { return JSON.parse(mcpResult.content[0].text); } } catch (error) { console.error("Failed to parse MCP result:", error); } return result as Record<string, unknown>; } // Weather component function WeatherCard({ location, temperature, conditions, humidity }: any) { return ( <div className="space-y-2 p-4 bg-card rounded-lg border"> <div className="flex items-center justify-between"> <h3 className="text-lg font-semibold">{location}</h3> <span className="text-3xl font-bold">{temperature}</span> </div> <p className="text-sm text-muted-foreground capitalize">{conditions}</p> {humidity && ( <div className="text-xs text-muted-foreground">Humidity: {humidity}%</div> )} </div> ); } // Meeting component function MeetingAgenda({ title, date, attendees, items }: any) { return ( <div className="space-y-3 p-4 bg-card rounded-lg border"> <div> <h3 className="text-lg font-semibold">{title}</h3> <p className="text-sm text-muted-foreground">{date}</p> </div> {attendees && ( <div className="text-sm"> <span className="font-medium">Attendees: </span> {attendees.join(", ")} </div> )} {items && items.length > 0 && ( <ol className="list-decimal list-inside space-y-1 text-sm"> {items.map((item: string, idx: number) => ( <li key={idx}>{item}</li> ))} </ol> )} </div> ); } export function MCPWithCustomRendering() { const mcpServer = useMCPServer({ url: process.env.NEXT_PUBLIC_MCP_SERVER_URL!, name: "Demo Tools", }); return ( <div> {mcpServer.error && ( <div className="mb-4 p-3 bg-destructive/10 text-destructive rounded"> Error: {mcpServer.error} </div> )} <ChatContainer transport={{ api: "/api/chat" }} mcp={{ enabled: true, toolRenderers: [ { serverUrl: process.env.NEXT_PUBLIC_MCP_SERVER_URL!, toolName: "demo_weather_forecast", render: (result) => <WeatherCard {...parseMCPResult(result)} />, }, { serverUrl: process.env.NEXT_PUBLIC_MCP_SERVER_URL!, toolName: "create_meeting_agenda", render: (result) => <MeetingAgenda {...parseMCPResult(result)} />, }, ], }} header={{ title: "MCP with Custom Rendering" }} ui={{ placeholder: "Ask about weather or create a meeting agenda" }} /> </div> ); }

Best Practices for MCP Rendering

  1. Always parse MCP response format - Extract JSON from content[0].text
  2. Handle parsing errors gracefully - Provide fallback rendering
  3. Type your data - Create interfaces for expected tool responses
  4. Use environment variables - Keep server URLs configurable
  5. Test with real servers - Verify parsing logic with actual MCP responses
  6. Match exact tool names - Tool names must match exactly (case-sensitive)
  7. Provide meaningful fallbacks - Show useful error messages when parsing fails

Debugging Tips

If your custom renderer isn’t working:

  1. Check tool names match exactly - Case-sensitive matching
  2. Verify serverUrl matches - Must be identical to server configuration
  3. Inspect the result object - Log what your renderer receives
  4. Test JSON parsing - Ensure MCP server returns valid JSON
  5. Check console for errors - Parsing errors are logged

Best Practices

  1. Use environment variables for MCP server URLs in production
  2. Handle errors gracefully - show retry buttons and clear error messages
  3. Keep credentials server-side - use forwardHeaders for sensitive data
  4. Test tool discovery - verify tools load correctly before deploying
  5. Monitor performance - MCP calls add latency to tool execution
  6. Document available tools - help users understand what MCP tools can do
  • useMCPServer Hook
  • createMcpToolsHandler Server Handler
  • createAIChatHandler (mcpEnabled option)

Learn More

Last updated on