Skip to content

Latest commit

 

History

History
463 lines (381 loc) · 12.6 KB

File metadata and controls

463 lines (381 loc) · 12.6 KB

Brave Search MCP Server Documentation

Overview

This guide walks you through setting up and running a Model Context Protocol (MCP) server that provides Brave Search functionality via Streamable HTTP transport. The server offers two search tools: web search and local business search.

Prerequisites

Required Software

  • Bun (JavaScript runtime) - Install from bun.sh
  • Brave Search API Key - Obtain from Brave Search API
  • Basic terminal/command line knowledge

System Requirements

  • Node.js 18+ compatible environment
  • Internet connection for API calls
  • Available port (default: 8000)

Installation & Setup

1. Project Setup

# Clone or download the brave-search MCP server files
# Ensure you have these files:
# - index.ts (main server file)
# - package.json (dependencies)
# - tsconfig.json (TypeScript config)

2. Install Dependencies

# Navigate to the brave-search directory
cd /path/to/brave-search

# Install all required packages
bun install

What this does: Downloads the MCP SDK and other dependencies needed to run the server.

3. API Key Configuration

# Option 1: Environment variable (recommended for production)
export BRAVE_API_KEY="your-brave-api-key-here"

# Option 2: The server has a fallback hardcoded key for testing
# (already configured in the current implementation)

Security Note: Never commit API keys to version control. Use environment variables in production.

Running the Server

Starting the Server

# Start the server on port 8000 with Streamable HTTP transport
bun run index.ts --port 8000

Expected Output:

Listening on http://localhost:8000
Put this in your client config:
{
  "mcpServers": {
    "brave-search": {
      "url": "http://localhost:8000/sse"
    }
  }
}
If your client supports streamable HTTP, you can use the /mcp endpoint instead.

Understanding the Endpoints

The server provides two endpoints:

  1. /sse - Server-Sent Events transport (for backward compatibility)
  2. /mcp - Streamable HTTP transport (recommended for new implementations)

Testing the Server

1. Initialize MCP Session

curl -v -X POST http://localhost:8000/mcp \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {
        "tools": {}
      },
      "clientInfo": {
        "name": "TestClient",
        "version": "1.0.0"
      }
    }
  }'

What to look for:

  • Status: 200 OK
  • Header: mcp-session-id: [uuid] (save this for next steps)
  • Response: JSON with server capabilities

2. Send Initialized Notification

curl -X POST http://localhost:8000/mcp \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: YOUR_SESSION_ID_FROM_STEP_1" \
  -d '{
    "jsonrpc": "2.0",
    "method": "notifications/initialized"
  }'

Critical: Replace YOUR_SESSION_ID_FROM_STEP_1 with the actual session ID from step 1.

3. List Available Tools

curl -X POST http://localhost:8000/mcp \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: YOUR_SESSION_ID" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list"
  }'

Expected Response: List of two tools:

  • brave_web_search - General web search
  • brave_local_search - Local business search

4. Perform Web Search

curl -X POST http://localhost:8000/mcp \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: YOUR_SESSION_ID" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "brave_web_search",
      "arguments": {
        "query": "latest AI news",
        "count": 3
      }
    }
  }'

5. Perform Local Search

curl -X POST http://localhost:8000/mcp \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: YOUR_SESSION_ID" \
  -d '{
    "jsonrpc": "2.0",
    "id": 4,
    "method": "tools/call",
    "params": {
      "name": "brave_local_search",
      "arguments": {
        "query": "pizza near Times Square NYC",
        "count": 2
      }
    }
  }'

Implementation Details: From Stdio to Streamable HTTP

What We Changed

The original brave-search MCP server only supported stdio transport (standard input/output). We modified it to support Streamable HTTP transport for AWS Lambda compatibility and better web integration.

Key Code Changes

1. Added HTTP Transport Imports

// Added these imports
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { createServer } from "http";
import { randomUUID } from "crypto";

2. Command Line Argument Parsing

// Added argument parsing for --port flag
function parseArgs() {
  const args = process.argv.slice(2);
  const options: { port?: number; headless?: boolean } = {};
  
  for (let i = 0; i < args.length; i++) {
    if (args[i] === '--port' && i + 1 < args.length) {
      options.port = parseInt(args[i + 1], 10);
      i++;
    }
  }
  return options;
}

3. HTTP Server Setup

// Added HTTP server with dual transport support
function startHttpServer(port: number) {
  const httpServer = createServer();
  
  httpServer.on('request', async (req, res) => {
    const url = new URL(req.url!, `http://${req.headers.host}`);
    
    if (url.pathname === '/sse') {
      await handleSSE(req, res);
    } else if (url.pathname === '/mcp') {
      await handleStreamable(req, res);
    } else {
      res.writeHead(404, { 'Content-Type': 'text/plain' });
      res.end('Not Found');
    }
  });
}

4. Session Management

// Added session storage for concurrent connections
const streamableSessions = new Map<string, {transport: any, server: any}>();

// Each session gets its own server instance
function createServerInstance() {
  const serverInstance = new Server({...});
  // Set up tool handlers...
  return serverInstance;
}

5. Transport Handlers

// Streamable HTTP transport handler
async function handleStreamable(req: any, res: any) {
  const sessionId = req.headers['mcp-session-id'] as string | undefined;
  
  if (sessionId) {
    // Use existing session
    const session = streamableSessions.get(sessionId);
    return await session.transport.handleRequest(req, res);
  }
  
  // Create new session for initialization
  const serverInstance = createServerInstance();
  const transport = new StreamableHTTPServerTransport({
    sessionIdGenerator: () => randomUUID(),
    onsessioninitialized: (sessionId) => {
      streamableSessions.set(sessionId, { transport, server: serverInstance });
    }
  });
  
  await serverInstance.connect(transport);
  await transport.handleRequest(req, res);
}

Why These Changes Were Necessary

  1. Lambda Compatibility: AWS Lambda doesn't support stdio, requiring HTTP-based communication
  2. Web Integration: HTTP transport enables direct browser and web service integration
  3. Concurrent Sessions: Multiple clients can connect simultaneously with session isolation
  4. Modern Protocol: Streamable HTTP is the modern MCP transport standard

Transport Comparison

Feature Stdio Transport Streamable HTTP Transport
Use Case CLI applications Web services, Lambda
Concurrency Single client Multiple concurrent clients
State Management Process-based Session-based
AWS Lambda ❌ Not supported ✅ Fully supported
Web Browser ❌ Not possible ✅ Direct integration
Debugging Simple Requires HTTP tools

Understanding MCP Concepts

Session Management

  • Each client connection gets a unique session ID
  • Sessions maintain state between requests
  • Sessions are automatically cleaned up when connections close
  • Important: Always include the Mcp-Session-Id header after initialization

Transport Types

  1. Streamable HTTP (/mcp): Modern, efficient, supports streaming responses
  2. SSE (/sse): Backward compatibility, uses Server-Sent Events

Message Flow

  1. Initialize - Establish capabilities and protocol version
  2. Initialized - Confirm initialization complete
  3. Tools/List - Discover available tools
  4. Tools/Call - Execute tool functions

Common Issues & Troubleshooting

"Server not initialized" Error

Cause: Missing or incorrect session ID
Solution: Ensure you're using the session ID from the initialization response

"Session not found" Error

Cause: Session expired or invalid session ID
Solution: Re-initialize with a new session

Port Already in Use

Cause: Another process is using port 8000
Solution:

# Kill process on port 8000
lsof -ti:8000 | xargs kill -9

# Or use a different port
bun run index.ts --port 8001

API Rate Limiting

Cause: Exceeded Brave Search API limits
Solution: The server has built-in rate limiting (1 request/second, 15000/month)

Connection Timeout

Cause: Network issues or server overload
Solution: Check network connectivity and server resources

Concurrent Connection Management

How It Works

  • Each client gets its own server instance and transport
  • Sessions are isolated - no shared state between clients
  • Automatic cleanup when clients disconnect
  • Memory-efficient session storage using Maps

Scaling Considerations

// Current session storage (in-memory)
const streamableSessions = new Map<string, {transport: any, server: any}>();

For production:

  • Consider Redis for session storage across multiple instances
  • Implement session cleanup timeouts
  • Monitor memory usage for session maps
  • Use load balancers for horizontal scaling

Connection Limits

  • Default: No explicit connection limit
  • Memory-bound by available system resources
  • Each session uses ~1-5MB of memory

Production Deployment

Environment Variables

export BRAVE_API_KEY="your-production-api-key"
export PORT="8000"
export NODE_ENV="production"

Process Management

# Using PM2 for process management
npm install -g pm2
pm2 start "bun run index.ts --port 8000" --name brave-search-mcp

Docker Deployment

FROM oven/bun:latest
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install
COPY . .
EXPOSE 8000
CMD ["bun", "run", "index.ts", "--port", "8000"]

AWS Lambda Deployment

The server supports Streamable HTTP, making it suitable for serverless deployment:

  • Configure API Gateway to proxy requests to /mcp
  • Set appropriate timeout values (30s+)
  • Handle cold starts gracefully

Security Best Practices

API Key Security

  • Use environment variables, never hardcode keys
  • Rotate API keys regularly
  • Monitor API usage and set alerts

Network Security

  • Bind to localhost (127.0.0.1) for local development
  • Use HTTPS in production
  • Implement proper authentication for public deployments

Input Validation

  • The server validates JSON-RPC message format
  • Search queries are limited to 400 characters
  • Count parameters are bounded (1-20 results)

Monitoring & Logging

Built-in Logging

The server logs:

  • Session creation/destruction
  • API errors and rate limiting
  • Connection events

Health Checks

# Simple health check
curl -f http://localhost:8000/mcp || echo "Server down"

Metrics to Monitor

  • Active session count
  • API call frequency
  • Response times
  • Error rates
  • Memory usage

Advanced Configuration

Custom Rate Limiting

const RATE_LIMIT = {
  perSecond: 1,     // Requests per second
  perMonth: 15000   // Monthly request limit
};

Timeout Configuration

// Adjust in createServerInstance() if needed
const serverInstance = new Server({
  name: "brave-search",
  version: "1.0.0"
}, {
  capabilities: { tools: {} },
  // Add timeout configs here
});

Support & Further Reading

This server is now ready for production use and can handle multiple concurrent connections efficiently while providing reliable Brave Search functionality through the MCP protocol.