@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.
@@ -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
- console.error('Invalid transport');
21
- process.exit(1);
15
+ // default to http server
16
+ httpServer.start();
22
17
  }
23
18
  main().catch((error) => {
24
19
  console.error(error);
@@ -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 { mcpServer } from '../server.js';
4
+ import createMcpServer from '../server.js';
5
5
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
6
- import { isInitializeRequest, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
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
- export const getTransport = async (request) => {
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.method === ListToolsRequestSchema.shape.method.value) {
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
- throw new Error('Invalid request: must be an initialization request, include a valid session ID, or be a ListTools method request');
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
- export const createApp = () => {
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
- export const start = () => {
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.error(`Server is running on http://${config.host}:${config.port}/mcp`);
69
+ console.log(`Server is running on http://${config.host}:${config.port}/mcp`);
70
70
  });
71
71
  };
72
72
  export default { start, createApp };
@@ -1,11 +1,11 @@
1
- import { mcpServer } from '../server.js';
1
+ import newMcpServer from '../server.js';
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
- export const createTransport = () => {
3
+ const createTransport = () => {
4
4
  return new StdioServerTransport();
5
5
  };
6
- export const start = async () => {
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 ClientLogger, { maybeRegisterCustomSetLevelRequestHandler } from './ClientLogger.js';
4
- export const mcpServer = new McpServer({
5
- version: '0.1.0',
6
- name: 'brave-search-mcp-server',
7
- title: 'Brave Search MCP Server',
8
- }, {
9
- capabilities: {
10
- logging: {},
11
- tools: { listChanged: false },
12
- },
13
- instructions: `Use this server to search the Web for various types of data via the Brave Search API.`,
14
- });
15
- ClientLogger.setServer(mcpServer);
16
- // https://github.com/modelcontextprotocol/typescript-sdk/issues/871
17
- maybeRegisterCustomSetLevelRequestHandler(mcpServer.server);
18
- for (const tool of Object.values(tools)) {
19
- mcpServer.tool(tool.name, tool.description, tool.inputSchema, tool.annotations, tool.execute);
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.5",
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.2",
36
+ "@modelcontextprotocol/sdk": "1.17.3",
37
37
  "commander": "14.0.0",
38
38
  "dotenv": "^17.2.1",
39
39
  "express": "5.1.0",
@@ -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 };