mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-04-20 12:55:21 +02:00
Updated Brave
This commit is contained in:
61
package-lock.json
generated
61
package-lock.json
generated
@@ -12,6 +12,7 @@
|
|||||||
"src/*"
|
"src/*"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@modelcontextprotocol/server-brave-search": "*",
|
||||||
"@modelcontextprotocol/server-duckduckgo": "*",
|
"@modelcontextprotocol/server-duckduckgo": "*",
|
||||||
"@modelcontextprotocol/server-everything": "*",
|
"@modelcontextprotocol/server-everything": "*",
|
||||||
"@modelcontextprotocol/server-filesystem": "*",
|
"@modelcontextprotocol/server-filesystem": "*",
|
||||||
@@ -162,6 +163,10 @@
|
|||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@modelcontextprotocol/server-brave-search": {
|
||||||
|
"resolved": "src/brave-search",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/@modelcontextprotocol/server-duckduckgo": {
|
"node_modules/@modelcontextprotocol/server-duckduckgo": {
|
||||||
"resolved": "src/duckduckgo",
|
"resolved": "src/duckduckgo",
|
||||||
"link": true
|
"link": true
|
||||||
@@ -3607,6 +3612,62 @@
|
|||||||
"zod": "^3.23.3"
|
"zod": "^3.23.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"src/brave-search": {
|
||||||
|
"name": "@modelcontextprotocol/server-brave-search",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@modelcontextprotocol/sdk": "0.5.0",
|
||||||
|
"jsdom": "^24.1.3",
|
||||||
|
"node-fetch": "^3.3.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"mcp-server-brave-search": "dist/index.js"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jsdom": "^21.1.6",
|
||||||
|
"@types/node": "^20.10.0",
|
||||||
|
"shx": "^0.3.4",
|
||||||
|
"typescript": "^5.6.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"src/brave-search/node_modules/@types/node": {
|
||||||
|
"version": "20.17.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz",
|
||||||
|
"integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~6.19.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"src/brave-search/node_modules/data-uri-to-buffer": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"src/brave-search/node_modules/node-fetch": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"data-uri-to-buffer": "^4.0.0",
|
||||||
|
"fetch-blob": "^3.1.4",
|
||||||
|
"formdata-polyfill": "^4.0.10"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/node-fetch"
|
||||||
|
}
|
||||||
|
},
|
||||||
"src/duckduckgo": {
|
"src/duckduckgo": {
|
||||||
"name": "@modelcontextprotocol/server-duckduckgo",
|
"name": "@modelcontextprotocol/server-duckduckgo",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Brave Search MCP Server
|
# Brave Search MCP Server
|
||||||
|
|
||||||
An MCP server implementation that integrates the Brave Search API, providing both web and local search capabilities through the Model Context Protocol.
|
An MCP server implementation that integrates the Brave Search API, providing both web and local search capabilities.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -9,86 +9,38 @@ An MCP server implementation that integrates the Brave Search API, providing bot
|
|||||||
- **Flexible Filtering**: Control result types, safety levels, and content freshness
|
- **Flexible Filtering**: Control result types, safety levels, and content freshness
|
||||||
- **Smart Fallbacks**: Local search automatically falls back to web when no results are found
|
- **Smart Fallbacks**: Local search automatically falls back to web when no results are found
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
- **brave_web_search**
|
||||||
|
- Execute web searches with pagination and filtering
|
||||||
|
- Inputs:
|
||||||
|
- `query` (string): Search terms
|
||||||
|
- `count` (number, optional): Results per page (max 20)
|
||||||
|
- `offset` (number, optional): Pagination offset (max 9)
|
||||||
|
|
||||||
|
- **brave_local_search**
|
||||||
|
- Search for local businesses and services
|
||||||
|
- Inputs:
|
||||||
|
- `query` (string): Local search terms
|
||||||
|
- `count` (number, optional): Number of results (max 20)
|
||||||
|
- Automatically falls back to web search if no local results found
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### Client Configuration
|
### Getting an API Key
|
||||||
Add this to your MCP client config:
|
1. Sign up for a [Brave Search API account](https://brave.com/search/api/)
|
||||||
|
2. Choose a plan (Free tier available with 2,000 queries/month)
|
||||||
|
3. Generate your API key [from the developer dashboard](https://api.search.brave.com/app/keys)
|
||||||
|
|
||||||
|
### Usage with Claude Desktop
|
||||||
|
Add this to your `claude_desktop_config.json`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"brave-search": {
|
"mcp-server-brave-search": {
|
||||||
"command": "mcp-server-brave-search",
|
"command": "mcp-server-brave-search",
|
||||||
"env": {
|
"env": {
|
||||||
"BRAVE_API_KEY": "YOUR_API_KEY_HERE"
|
"BRAVE_API_KEY": "YOUR_API_KEY_HERE"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, you can set the API key as an environment variable:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export BRAVE_API_KEY='your_actual_api_key_here'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Getting an API Key
|
|
||||||
1. Sign up for a Brave Search API account
|
|
||||||
2. Choose a plan (Free tier available)
|
|
||||||
3. Generate your API key from the developer dashboard
|
|
||||||
|
|
||||||
## Tools
|
|
||||||
|
|
||||||
### brave_web_search
|
|
||||||
Performs general web searches:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"name": "brave_web_search",
|
|
||||||
"arguments": {
|
|
||||||
"query": "latest AI developments",
|
|
||||||
"count": 10,
|
|
||||||
"freshness": "pw", // Past week
|
|
||||||
"safesearch": "moderate"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### brave_local_search
|
|
||||||
Finds local businesses and services:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"name": "brave_local_search",
|
|
||||||
"arguments": {
|
|
||||||
"query": "pizza near Central Park",
|
|
||||||
"count": 5,
|
|
||||||
"units": "imperial"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Implementation Details
|
|
||||||
|
|
||||||
- Rate limiting to respect API quotas (1 request/second, 15000/month)
|
|
||||||
- Parallel fetching of POI details and descriptions for local search
|
|
||||||
- Type-safe argument validation
|
|
||||||
- Comprehensive error handling and logging
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install dependencies
|
|
||||||
npm install
|
|
||||||
|
|
||||||
# Build the server
|
|
||||||
npm run build
|
|
||||||
|
|
||||||
# Run the server
|
|
||||||
mcp-server-brave-search
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Contributions welcome! Please check the issues tab or submit a PR.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT - see [LICENSE](LICENSE) file for details.
|
|
||||||
|
|||||||
@@ -4,26 +4,18 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|||||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||||
import {
|
import {
|
||||||
CallToolRequestSchema,
|
CallToolRequestSchema,
|
||||||
ListResourcesRequestSchema,
|
|
||||||
ListToolsRequestSchema,
|
ListToolsRequestSchema,
|
||||||
ReadResourceRequestSchema,
|
|
||||||
Tool,
|
Tool,
|
||||||
} from "@modelcontextprotocol/sdk/types.js";
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
|
|
||||||
// Define tool schemas
|
|
||||||
const WEB_SEARCH_TOOL: Tool = {
|
const WEB_SEARCH_TOOL: Tool = {
|
||||||
name: "brave_web_search",
|
name: "brave_web_search",
|
||||||
description:
|
description:
|
||||||
"Performs a web search using the Brave Search API, ideal for general queries, news, articles, and online content. " +
|
"Performs a web search using the Brave Search API, ideal for general queries, news, articles, and online content. " +
|
||||||
"Use this for broad information gathering, recent events, or when you need diverse web sources. " +
|
"Use this for broad information gathering, recent events, or when you need diverse web sources. " +
|
||||||
"Supports pagination, content filtering, and freshness controls. " +
|
"Supports pagination, content filtering, and freshness controls. " +
|
||||||
"Maximum 20 results per request, with offset for pagination. " +
|
"Maximum 20 results per request, with offset for pagination. ",
|
||||||
"Additional features:\n" +
|
|
||||||
"- Safesearch: moderate (default), strict, or off\n" +
|
|
||||||
"- Freshness: filter by recency (past day/week/month/year)\n" +
|
|
||||||
"- Result types: web, news, videos, discussions\n" +
|
|
||||||
"- Spell check and query alteration support",
|
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: "object",
|
type: "object",
|
||||||
properties: {
|
properties: {
|
||||||
@@ -41,37 +33,6 @@ const WEB_SEARCH_TOOL: Tool = {
|
|||||||
description: "Pagination offset (max 9, default 0)",
|
description: "Pagination offset (max 9, default 0)",
|
||||||
default: 0
|
default: 0
|
||||||
},
|
},
|
||||||
freshness: {
|
|
||||||
type: "string",
|
|
||||||
description: "Filter by recency: pd (past day), pw (past week), pm (past month), or custom date range",
|
|
||||||
enum: ["pd", "pw", "pm", "py"]
|
|
||||||
},
|
|
||||||
safesearch: {
|
|
||||||
type: "string",
|
|
||||||
description: "Content filtering level",
|
|
||||||
enum: ["off", "moderate", "strict"],
|
|
||||||
default: "moderate"
|
|
||||||
},
|
|
||||||
country: {
|
|
||||||
type: "string",
|
|
||||||
description: "2-letter country code for localized results",
|
|
||||||
default: "US"
|
|
||||||
},
|
|
||||||
search_lang: {
|
|
||||||
type: "string",
|
|
||||||
description: "Search language (2+ char code)",
|
|
||||||
default: "en"
|
|
||||||
},
|
|
||||||
ui_lang: {
|
|
||||||
type: "string",
|
|
||||||
description: "UI language preference",
|
|
||||||
default: "en-US"
|
|
||||||
},
|
|
||||||
result_filter: {
|
|
||||||
type: "string",
|
|
||||||
description: "Comma-separated result types: web, news, videos, discussions, locations",
|
|
||||||
default: null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
required: ["query"],
|
required: ["query"],
|
||||||
},
|
},
|
||||||
@@ -86,7 +47,6 @@ const LOCAL_SEARCH_TOOL: Tool = {
|
|||||||
"- Business names and addresses\n" +
|
"- Business names and addresses\n" +
|
||||||
"- Ratings and review counts\n" +
|
"- Ratings and review counts\n" +
|
||||||
"- Phone numbers and opening hours\n" +
|
"- Phone numbers and opening hours\n" +
|
||||||
"- AI-generated descriptions\n" +
|
|
||||||
"Use this when the query implies 'near me' or mentions specific locations. " +
|
"Use this when the query implies 'near me' or mentions specific locations. " +
|
||||||
"Automatically falls back to web search if no local results are found.",
|
"Automatically falls back to web search if no local results are found.",
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
@@ -101,26 +61,6 @@ const LOCAL_SEARCH_TOOL: Tool = {
|
|||||||
description: "Number of results (1-20, default 5)",
|
description: "Number of results (1-20, default 5)",
|
||||||
default: 5
|
default: 5
|
||||||
},
|
},
|
||||||
units: {
|
|
||||||
type: "string",
|
|
||||||
description: "Measurement system for distances",
|
|
||||||
enum: ["metric", "imperial"]
|
|
||||||
},
|
|
||||||
country: {
|
|
||||||
type: "string",
|
|
||||||
description: "2-letter country code for localized results",
|
|
||||||
default: "US"
|
|
||||||
},
|
|
||||||
search_lang: {
|
|
||||||
type: "string",
|
|
||||||
description: "Search language (2+ char code)",
|
|
||||||
default: "en"
|
|
||||||
},
|
|
||||||
ui_lang: {
|
|
||||||
type: "string",
|
|
||||||
description: "UI language preference",
|
|
||||||
default: "en-US"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
required: ["query"]
|
required: ["query"]
|
||||||
}
|
}
|
||||||
@@ -135,7 +75,6 @@ const server = new Server(
|
|||||||
{
|
{
|
||||||
capabilities: {
|
capabilities: {
|
||||||
tools: {},
|
tools: {},
|
||||||
resources: {},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -221,7 +160,6 @@ interface BraveDescription {
|
|||||||
descriptions: {[id: string]: string};
|
descriptions: {[id: string]: string};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type guard functions for arguments
|
|
||||||
function isBraveWebSearchArgs(args: unknown): args is { query: string; count?: number } {
|
function isBraveWebSearchArgs(args: unknown): args is { query: string; count?: number } {
|
||||||
return (
|
return (
|
||||||
typeof args === "object" &&
|
typeof args === "object" &&
|
||||||
@@ -240,19 +178,12 @@ function isBraveLocalSearchArgs(args: unknown): args is { query: string; count?:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// API functions
|
|
||||||
async function performWebSearch(query: string, count: number = 10, offset: number = 0) {
|
async function performWebSearch(query: string, count: number = 10, offset: number = 0) {
|
||||||
checkRateLimit();
|
checkRateLimit();
|
||||||
const url = new URL('https://api.search.brave.com/res/v1/web/search');
|
const url = new URL('https://api.search.brave.com/res/v1/web/search');
|
||||||
url.searchParams.set('q', query);
|
url.searchParams.set('q', query);
|
||||||
url.searchParams.set('search_lang', 'en');
|
|
||||||
url.searchParams.set('count', Math.min(count, 20).toString()); // API limit
|
url.searchParams.set('count', Math.min(count, 20).toString()); // API limit
|
||||||
url.searchParams.set('offset', offset.toString());
|
url.searchParams.set('offset', offset.toString());
|
||||||
url.searchParams.set('result_filter', 'web');
|
|
||||||
url.searchParams.set('text_decorations', '0');
|
|
||||||
url.searchParams.set('spellcheck', '0');
|
|
||||||
url.searchParams.set('safesearch', 'moderate');
|
|
||||||
url.searchParams.set('freshness', 'pw'); // Past week results
|
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -377,32 +308,6 @@ Description: ${descData.descriptions[poi.id] || 'No description available'}
|
|||||||
}).join('\n---\n') || 'No local results found';
|
}).join('\n---\n') || 'No local results found';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resource handlers
|
|
||||||
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
||||||
resources: [
|
|
||||||
{
|
|
||||||
uri: "brave://search",
|
|
||||||
mimeType: "text/plain",
|
|
||||||
name: "Brave Search Interface",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
|
|
||||||
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
||||||
if (request.params.uri.toString() === "brave://search") {
|
|
||||||
return {
|
|
||||||
contents: [
|
|
||||||
{
|
|
||||||
uri: "brave://search",
|
|
||||||
mimeType: "text/plain",
|
|
||||||
text: "Brave Search API interface",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
throw new Error("Resource not found");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Tool handlers
|
// Tool handlers
|
||||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||||
tools: [WEB_SEARCH_TOOL, LOCAL_SEARCH_TOOL],
|
tools: [WEB_SEARCH_TOOL, LOCAL_SEARCH_TOOL],
|
||||||
|
|||||||
Reference in New Issue
Block a user