From 1d211ee9f77ff2a03fe9632d271fbf0f4476889b Mon Sep 17 00:00:00 2001 From: LiuVaayne <10231735+vaayne@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:17:13 +0800 Subject: [PATCH] feat: mcp custom headers (#4800) * Add support for custom HTTP headers in MCP servers Allow users to configure custom HTTP headers for SSE and streamable HTTP MCP server connections. This enables authentication and other API requirements. * Add custom headers i18n strings --- src/main/services/MCPService.ts | 19 ++++-- src/renderer/src/i18n/locales/en-us.json | 2 + src/renderer/src/i18n/locales/ja-jp.json | 2 + src/renderer/src/i18n/locales/ru-ru.json | 2 + src/renderer/src/i18n/locales/zh-cn.json | 2 + src/renderer/src/i18n/locales/zh-tw.json | 2 + .../settings/MCPSettings/McpSettings.tsx | 66 +++++++++++++++---- src/renderer/src/types/index.ts | 1 + 8 files changed, 76 insertions(+), 20 deletions(-) diff --git a/src/main/services/MCPService.ts b/src/main/services/MCPService.ts index 9165eb8544..e43ce55654 100644 --- a/src/main/services/MCPService.ts +++ b/src/main/services/MCPService.ts @@ -7,7 +7,7 @@ import { createInMemoryMCPServer } from '@main/mcpServers/factory' import { makeSureDirExists } from '@main/utils' import { getBinaryName, getBinaryPath } from '@main/utils/process' import { Client } from '@modelcontextprotocol/sdk/client/index.js' -import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js' +import { SSEClientTransport, SSEClientTransportOptions } from '@modelcontextprotocol/sdk/client/sse.js' import { getDefaultEnvironment, StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js' import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory' import { nanoid } from '@reduxjs/toolkit' @@ -137,12 +137,19 @@ class McpService { transport = clientTransport } else if (server.baseUrl) { if (server.type === 'streamableHttp') { - transport = new StreamableHTTPClientTransport( - new URL(server.baseUrl!), - {} as StreamableHTTPClientTransportOptions - ) + const options: StreamableHTTPClientTransportOptions = { + requestInit: { + headers: server.headers || {} + } + } + transport = new StreamableHTTPClientTransport(new URL(server.baseUrl!), options) } else if (server.type === 'sse') { - transport = new SSEClientTransport(new URL(server.baseUrl!)) + const options: SSEClientTransportOptions = { + requestInit: { + headers: server.headers || {} + } + } + transport = new SSEClientTransport(new URL(server.baseUrl!), options) } else { throw new Error('Invalid server type') } diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 0088d8420b..d0edf69fe1 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -1072,6 +1072,8 @@ "editServer": "Edit Server", "env": "Environment Variables", "envTooltip": "Format: KEY=value, one per line", + "headers": "Headers", + "headersTooltip": "Custom headers for HTTP requests", "findMore": "Find More MCP", "searchNpx": "Search MCP", "install": "Install", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index cda00b6946..26706708da 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -1071,6 +1071,8 @@ "editServer": "サーバーを編集", "env": "環境変数", "envTooltip": "形式: KEY=value, 1行に1つ", + "headers": "ヘッダー", + "headersTooltip": "HTTP リクエストのカスタムヘッダー", "findMore": "MCP を見つける", "searchNpx": "MCP を検索", "install": "インストール", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index bfb734abde..e9338100ff 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -1071,6 +1071,8 @@ "editServer": "Редактировать сервер", "env": "Переменные окружения", "envTooltip": "Формат: KEY=value, по одной на строку", + "headers": "Заголовки", + "headersTooltip": "Пользовательские заголовки для HTTP-запросов", "findMore": "Найти больше MCP", "searchNpx": "Найти MCP", "install": "Установить", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index c6d5eb2d9b..4002bbd18b 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -1072,6 +1072,8 @@ "editServer": "编辑服务器", "env": "环境变量", "envTooltip": "格式:KEY=value,每行一个", + "headers": "请求头", + "headersTooltip": "HTTP 请求的自定义请求头", "findMore": "更多 MCP", "searchNpx": "搜索 MCP", "install": "安装", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 21e07e0720..6b9e3bcdb4 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -1071,6 +1071,8 @@ "editServer": "編輯伺服器", "env": "環境變數", "envTooltip": "格式:KEY=value,每行一個", + "headers": "請求標頭", + "headersTooltip": "HTTP 請求的自定義標頭", "findMore": "更多 MCP", "searchNpx": "搜索 MCP", "install": "安裝", diff --git a/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx b/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx index 45a06953d6..2673df85be 100644 --- a/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx @@ -27,6 +27,7 @@ interface MCPFormValues { args?: string env?: string isActive: boolean + headers?: string } interface Registry { @@ -101,6 +102,11 @@ const McpSettings: React.FC = ({ server }) => { ? Object.entries(server.env) .map(([key, value]) => `${key}=${value}`) .join('\n') + : '', + headers: server.headers + ? Object.entries(server.headers) + .map(([key, value]) => `${key}=${value}`) + .join('\n') : '' }) }, [server, form]) @@ -218,6 +224,20 @@ const McpSettings: React.FC = ({ server }) => { mcpServer.env = env } + if (values.headers) { + const headers: Record = {} + values.headers.split('\n').forEach((line) => { + if (line.trim()) { + const [key, ...chunks] = line.split(':') + const value = chunks.join(':') + if (key && value) { + headers[key.trim()] = value.trim() + } + } + }) + mcpServer.headers = headers + } + try { await window.api.mcp.restartServer(mcpServer) updateMCPServer({ ...mcpServer, isActive: true }) @@ -400,22 +420,40 @@ const McpSettings: React.FC = ({ server }) => { )} {serverType === 'sse' && ( - - - + <> + + + + +