@brave/brave-search-mcp-server 1.3.5 → 1.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BraveAPI/index.js +0 -6
- package/dist/index.js +2 -7
- package/dist/protocols/http.js +19 -19
- package/dist/protocols/stdio.js +4 -4
- package/dist/server.js +29 -18
- package/dist/tools/images/index.js +0 -3
- package/dist/tools/summarizer/index.js +0 -2
- package/package.json +2 -2
- package/dist/ClientLogger.js +0 -63
package/dist/BraveAPI/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import config from '../config.js';
|
|
2
2
|
import { stringify } from '../utils.js';
|
|
3
|
-
import ClientLogger from '../ClientLogger.js';
|
|
4
3
|
const typeToPathMap = {
|
|
5
4
|
images: '/res/v1/images/search',
|
|
6
5
|
localPois: '/res/v1/local/pois',
|
|
@@ -23,7 +22,6 @@ requestHeaders = {}) {
|
|
|
23
22
|
// Determine URL, and setup parameters
|
|
24
23
|
const url = new URL(`https://api.search.brave.com${typeToPathMap[endpoint]}`);
|
|
25
24
|
const queryParams = new URLSearchParams();
|
|
26
|
-
await ClientLogger.log('info', `Preparing to issue request to ${url.toString()}`);
|
|
27
25
|
// TODO (Sampson): Move param-construction/validation to modules
|
|
28
26
|
for (const [key, value] of Object.entries(parameters)) {
|
|
29
27
|
// The 'ids' parameter is expected to appear multiple times for multiple IDs
|
|
@@ -65,12 +63,10 @@ requestHeaders = {}) {
|
|
|
65
63
|
queryParams.set(key === 'query' ? 'q' : key, value.toString());
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
|
-
await ClientLogger.log('debug', `Using parameters: ${queryParams.toString()}`);
|
|
69
66
|
// Issue Request
|
|
70
67
|
const urlWithParams = url.toString() + '?' + queryParams.toString();
|
|
71
68
|
const headers = { ...defaultRequestHeaders, ...requestHeaders };
|
|
72
69
|
const response = await fetch(urlWithParams, { headers });
|
|
73
|
-
await ClientLogger.log('debug', `Received response from ${urlWithParams}`);
|
|
74
70
|
// Handle Error
|
|
75
71
|
if (!response.ok) {
|
|
76
72
|
let errorMessage = `${response.status} ${response.statusText}`;
|
|
@@ -81,13 +77,11 @@ requestHeaders = {}) {
|
|
|
81
77
|
catch (error) {
|
|
82
78
|
errorMessage += `\n${await response.text()}`;
|
|
83
79
|
}
|
|
84
|
-
await ClientLogger.log('error', errorMessage);
|
|
85
80
|
// TODO (Sampson): Setup proper error handling, updating state, etc.
|
|
86
81
|
throw new Error(errorMessage);
|
|
87
82
|
}
|
|
88
83
|
// Return Response
|
|
89
84
|
const responseBody = await response.json();
|
|
90
|
-
await ClientLogger.log('debug', `Returning response: ${stringify(responseBody, true)}`);
|
|
91
85
|
return responseBody;
|
|
92
86
|
}
|
|
93
87
|
export default {
|
package/dist/index.js
CHANGED
|
@@ -7,18 +7,13 @@ async function main() {
|
|
|
7
7
|
console.error('Invalid configuration');
|
|
8
8
|
process.exit(1);
|
|
9
9
|
}
|
|
10
|
-
// default to http server
|
|
11
|
-
if (!options.transport || options.transport === 'http') {
|
|
12
|
-
httpServer.start();
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
10
|
// stdio requires explicit request
|
|
16
11
|
if (options.transport === 'stdio') {
|
|
17
12
|
await stdioServer.start();
|
|
18
13
|
return;
|
|
19
14
|
}
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
// default to http server
|
|
16
|
+
httpServer.start();
|
|
22
17
|
}
|
|
23
18
|
main().catch((error) => {
|
|
24
19
|
console.error(error);
|
package/dist/protocols/http.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import express from 'express';
|
|
3
3
|
import config from '../config.js';
|
|
4
|
-
import
|
|
4
|
+
import createMcpServer from '../server.js';
|
|
5
5
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
6
|
-
import {
|
|
6
|
+
import { ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
7
7
|
const yieldGenericServerError = (res) => {
|
|
8
8
|
res.status(500).json({
|
|
9
9
|
id: null,
|
|
@@ -12,34 +12,34 @@ const yieldGenericServerError = (res) => {
|
|
|
12
12
|
});
|
|
13
13
|
};
|
|
14
14
|
const transports = new Map();
|
|
15
|
-
|
|
15
|
+
const isListToolsRequest = (value) => ListToolsRequestSchema.safeParse(value).success;
|
|
16
|
+
const getTransport = async (request) => {
|
|
16
17
|
// Check for an existing session
|
|
17
18
|
const sessionId = request.headers['mcp-session-id'];
|
|
18
19
|
if (sessionId && transports.has(sessionId)) {
|
|
19
20
|
return transports.get(sessionId);
|
|
20
21
|
}
|
|
21
|
-
// Is the client attempting to initialize a new session?
|
|
22
|
-
if (isInitializeRequest(request.body)) {
|
|
23
|
-
const transport = new StreamableHTTPServerTransport({
|
|
24
|
-
sessionIdGenerator: () => randomUUID(),
|
|
25
|
-
onsessioninitialized: (sessionId) => {
|
|
26
|
-
transports.set(sessionId, transport);
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
await mcpServer.connect(transport);
|
|
30
|
-
return transport;
|
|
31
|
-
}
|
|
32
22
|
// We have a special case where we'll permit ListToolsRequest w/o a session ID
|
|
33
|
-
if (request.body
|
|
23
|
+
if (!sessionId && isListToolsRequest(request.body)) {
|
|
34
24
|
const transport = new StreamableHTTPServerTransport({
|
|
35
25
|
sessionIdGenerator: undefined,
|
|
36
26
|
});
|
|
27
|
+
const mcpServer = createMcpServer();
|
|
37
28
|
await mcpServer.connect(transport);
|
|
38
29
|
return transport;
|
|
39
30
|
}
|
|
40
|
-
|
|
31
|
+
// Otherwise, start a new transport/session
|
|
32
|
+
const transport = new StreamableHTTPServerTransport({
|
|
33
|
+
sessionIdGenerator: () => randomUUID(),
|
|
34
|
+
onsessioninitialized: (sessionId) => {
|
|
35
|
+
transports.set(sessionId, transport);
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const mcpServer = createMcpServer();
|
|
39
|
+
await mcpServer.connect(transport);
|
|
40
|
+
return transport;
|
|
41
41
|
};
|
|
42
|
-
|
|
42
|
+
const createApp = () => {
|
|
43
43
|
const app = express();
|
|
44
44
|
app.use(express.json());
|
|
45
45
|
app.all('/mcp', async (req, res) => {
|
|
@@ -59,14 +59,14 @@ export const createApp = () => {
|
|
|
59
59
|
});
|
|
60
60
|
return app;
|
|
61
61
|
};
|
|
62
|
-
|
|
62
|
+
const start = () => {
|
|
63
63
|
if (!config.ready) {
|
|
64
64
|
console.error('Invalid configuration');
|
|
65
65
|
process.exit(1);
|
|
66
66
|
}
|
|
67
67
|
const app = createApp();
|
|
68
68
|
app.listen(config.port, config.host, () => {
|
|
69
|
-
console.
|
|
69
|
+
console.log(`Server is running on http://${config.host}:${config.port}/mcp`);
|
|
70
70
|
});
|
|
71
71
|
};
|
|
72
72
|
export default { start, createApp };
|
package/dist/protocols/stdio.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import newMcpServer from '../server.js';
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
|
|
3
|
+
const createTransport = () => {
|
|
4
4
|
return new StdioServerTransport();
|
|
5
5
|
};
|
|
6
|
-
|
|
6
|
+
const start = async () => {
|
|
7
7
|
const transport = createTransport();
|
|
8
|
+
const mcpServer = newMcpServer();
|
|
8
9
|
await mcpServer.connect(transport);
|
|
9
|
-
console.error('Stdio server started');
|
|
10
10
|
};
|
|
11
11
|
export default { start, createTransport };
|
package/dist/server.js
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
1
|
import tools from './tools/index.js';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { SetLevelRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
import pkg from '../package.json' with { type: 'json' };
|
|
5
|
+
export default function createMcpServer() {
|
|
6
|
+
const mcpServer = new McpServer({
|
|
7
|
+
version: pkg.version,
|
|
8
|
+
name: 'brave-search-mcp-server',
|
|
9
|
+
title: 'Brave Search MCP Server',
|
|
10
|
+
}, {
|
|
11
|
+
capabilities: {
|
|
12
|
+
logging: {},
|
|
13
|
+
tools: { listChanged: false },
|
|
14
|
+
},
|
|
15
|
+
instructions: `Use this server to search the Web for various types of data via the Brave Search API.`,
|
|
16
|
+
});
|
|
17
|
+
try {
|
|
18
|
+
mcpServer.server.assertCanSetRequestHandler(SetLevelRequestSchema.shape.method.value);
|
|
19
|
+
mcpServer.server.setRequestHandler(SetLevelRequestSchema, () => ({}));
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
/**
|
|
23
|
+
* An error here signifies native-handling of the SetLevel request.
|
|
24
|
+
* See https://github.com/modelcontextprotocol/typescript-sdk/issues/871.
|
|
25
|
+
*/
|
|
26
|
+
}
|
|
27
|
+
for (const tool of Object.values(tools)) {
|
|
28
|
+
mcpServer.tool(tool.name, tool.description, tool.inputSchema, tool.annotations, tool.execute);
|
|
29
|
+
}
|
|
30
|
+
return mcpServer;
|
|
20
31
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import params from './params.js';
|
|
2
2
|
import API from '../../BraveAPI/index.js';
|
|
3
3
|
import { stringify } from '../../utils.js';
|
|
4
|
-
import ClientLogger from '../../ClientLogger.js';
|
|
5
4
|
export const name = 'brave_image_search';
|
|
6
5
|
export const annotations = {
|
|
7
6
|
title: 'Brave Image Search',
|
|
@@ -28,7 +27,6 @@ export const execute = async (params) => {
|
|
|
28
27
|
return { content, isError: false };
|
|
29
28
|
};
|
|
30
29
|
async function fetchImage(url) {
|
|
31
|
-
await ClientLogger.log('info', `Fetching image data from ${url}`);
|
|
32
30
|
try {
|
|
33
31
|
const response = await fetch(url);
|
|
34
32
|
const buffer = await response.arrayBuffer();
|
|
@@ -38,7 +36,6 @@ async function fetchImage(url) {
|
|
|
38
36
|
};
|
|
39
37
|
}
|
|
40
38
|
catch (error) {
|
|
41
|
-
await ClientLogger.log('error', `Error fetching image data from ${url}: ${error}`);
|
|
42
39
|
return null;
|
|
43
40
|
}
|
|
44
41
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { summarizerQueryParams } from './params.js';
|
|
2
2
|
import API from '../../BraveAPI/index.js';
|
|
3
|
-
import ClientLogger from '../../ClientLogger.js';
|
|
4
3
|
export const name = 'brave_summarizer';
|
|
5
4
|
export const annotations = {
|
|
6
5
|
title: 'Brave Summarizer',
|
|
@@ -70,7 +69,6 @@ const pollForSummary = async (params, pollInterval = 50, attempts = 20) => {
|
|
|
70
69
|
}
|
|
71
70
|
}
|
|
72
71
|
catch (error) {
|
|
73
|
-
await ClientLogger.log('error', `Error polling for summarizer results: ${error}`);
|
|
74
72
|
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
75
73
|
}
|
|
76
74
|
attempts--;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brave/brave-search-mcp-server",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.6",
|
|
4
4
|
"description": "MCP server for Brave Search. Uses the Brave Search API to return results from the web, including ranked links, images, and videos, as well as AI summaries of pages, rich results, and more.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"inspector:stdio": "npx @modelcontextprotocol/inspector --transport stdio"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@modelcontextprotocol/sdk": "1.17.
|
|
36
|
+
"@modelcontextprotocol/sdk": "1.17.3",
|
|
37
37
|
"commander": "14.0.0",
|
|
38
38
|
"dotenv": "^17.2.1",
|
|
39
39
|
"express": "5.1.0",
|
package/dist/ClientLogger.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { LoggingLevelSchema, SetLevelRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
-
import config from './config.js';
|
|
3
|
-
let mcpServer;
|
|
4
|
-
const setServer = (server) => {
|
|
5
|
-
mcpServer = server;
|
|
6
|
-
};
|
|
7
|
-
let customLogger;
|
|
8
|
-
let currentLevel = LoggingLevelSchema.options.indexOf(config.loggingLevel);
|
|
9
|
-
/**
|
|
10
|
-
* Attempt to register a custom set level request handler.
|
|
11
|
-
* If the method is already handled by the SDK/Server, we'll ignore the error.
|
|
12
|
-
* @see https://github.com/modelcontextprotocol/typescript-sdk/issues/871
|
|
13
|
-
* @param server {Server} The server to register the handler on.
|
|
14
|
-
*/
|
|
15
|
-
export const maybeRegisterCustomSetLevelRequestHandler = (server) => {
|
|
16
|
-
try {
|
|
17
|
-
server.assertCanSetRequestHandler(SetLevelRequestSchema.shape.method.value);
|
|
18
|
-
server.setRequestHandler(SetLevelRequestSchema, async (request) => {
|
|
19
|
-
await log('info', `Setting logging level to ${request.params.level}`);
|
|
20
|
-
currentLevel = LoggingLevelSchema.options.indexOf(request.params.level);
|
|
21
|
-
return {};
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
console.error(`Failed to register custom SetLevelRequest handler. The SDK may now provide its own handler. See https://github.com/modelcontextprotocol/typescript-sdk/issues/871 for more information.`, error);
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
const log = async (level, message) => {
|
|
29
|
-
// If a custom logger exists, call it, and let it handle it's own log-level filtering
|
|
30
|
-
if (customLogger) {
|
|
31
|
-
await customLogger(level, message);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
// If the log-level is less than the current log-level, skip it
|
|
35
|
-
if (LoggingLevelSchema.options.indexOf(level) < currentLevel) {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
// Fall back to default logger if no custom logger is set
|
|
39
|
-
const time = new Date().toISOString();
|
|
40
|
-
if (!mcpServer?.isConnected()) {
|
|
41
|
-
console.error(`${time} [${level}] ${message}`);
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
await mcpServer.server.sendLoggingMessage({ level, data: { message, time } });
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
console.error(`Error sending logging message: ${error}`);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
const setLogger = (logger) => {
|
|
52
|
-
customLogger = logger;
|
|
53
|
-
};
|
|
54
|
-
const setLoggingLevel = (desiredLevel) => {
|
|
55
|
-
const desiredLevelIndex = LoggingLevelSchema.options.indexOf(desiredLevel);
|
|
56
|
-
if (desiredLevelIndex === -1) {
|
|
57
|
-
console.error(`Invalid logging level: ${desiredLevel}. Must be one of: ${LoggingLevelSchema.options.join(', ')}`);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
currentLevel = desiredLevelIndex;
|
|
61
|
-
};
|
|
62
|
-
const getLoggingLevel = () => LoggingLevelSchema.options[currentLevel];
|
|
63
|
-
export default { log, setServer, setLogger, setLoggingLevel, getLoggingLevel };
|