brave-search-mcp
Version:
MCP Server that uses Brave Search API to search for images, general web search, video, news and points of interest.
96 lines (95 loc) • 4.57 kB
JavaScript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { BraveSearch } from 'brave-search';
import { BraveImageSearchTool } from './tools/BraveImageSearchTool.js';
import { BraveLocalSearchTool } from './tools/BraveLocalSearchTool.js';
import { BraveNewsSearchTool } from './tools/BraveNewsSearchTool.js';
import { BraveVideoSearchTool } from './tools/BraveVideoSearchTool.js';
import { BraveWebSearchTool } from './tools/BraveWebSearchTool.js';
export class BraveMcpServer {
braveSearchApiKey;
server;
braveSearch;
imageSearchTool;
webSearchTool;
localSearchTool;
newsSearchTool;
videoSearchTool;
constructor(braveSearchApiKey) {
this.braveSearchApiKey = braveSearchApiKey;
this.server = new McpServer({
name: 'Brave Search MCP Server',
description: 'A server that provides tools for searching the web, images, videos, and local businesses using the Brave Search API.',
version: '0.6.0',
}, {
capabilities: {
resources: {},
tools: {},
logging: {},
},
});
this.braveSearch = new BraveSearch(braveSearchApiKey);
this.imageSearchTool = new BraveImageSearchTool(this, this.braveSearch);
this.webSearchTool = new BraveWebSearchTool(this, this.braveSearch);
this.localSearchTool = new BraveLocalSearchTool(this, this.braveSearch, this.webSearchTool, braveSearchApiKey);
this.newsSearchTool = new BraveNewsSearchTool(this, this.braveSearch);
this.videoSearchTool = new BraveVideoSearchTool(this, this.braveSearch, braveSearchApiKey);
this.setupTools();
this.setupResourceListener();
}
setupTools() {
this.server.tool(this.imageSearchTool.name, this.imageSearchTool.description, this.imageSearchTool.inputSchema.shape, this.imageSearchTool.execute.bind(this.imageSearchTool));
this.server.tool(this.webSearchTool.name, this.webSearchTool.description, this.webSearchTool.inputSchema.shape, this.webSearchTool.execute.bind(this.webSearchTool));
this.server.tool(this.localSearchTool.name, this.localSearchTool.description, this.localSearchTool.inputSchema.shape, this.localSearchTool.execute.bind(this.localSearchTool));
this.server.tool(this.newsSearchTool.name, this.newsSearchTool.description, this.newsSearchTool.inputSchema.shape, this.newsSearchTool.execute.bind(this.newsSearchTool));
this.server.tool(this.videoSearchTool.name, this.videoSearchTool.description, this.videoSearchTool.inputSchema.shape, this.videoSearchTool.execute.bind(this.videoSearchTool));
}
setupResourceListener() {
this.server.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
resources: [
...Array.from(this.imageSearchTool.imageByTitle.keys()).map(title => ({
uri: `brave-image://${title}`,
mimeType: 'image/png',
name: `${title}`,
})),
],
}));
this.server.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const uri = request.params.uri.toString();
if (uri.startsWith('brave-image://')) {
const title = uri.split('://')[1];
const image = this.imageSearchTool.imageByTitle.get(title);
if (image) {
return {
contents: [{
uri,
mimeType: 'image/png',
blob: image,
}],
};
}
}
return {
content: [{ type: 'text', text: `Resource not found: ${uri}` }],
isError: true,
};
});
}
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
this.log('Server is running with Stdio transport');
}
resourceChangedNotification() {
this.server.server.notification({
method: 'notifications/resources/list_changed',
});
}
log(message, level = 'info') {
this.server.server.sendLoggingMessage({
level,
message,
});
}
}