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
This commit is contained in:
LiuVaayne 2025-04-14 17:17:13 +08:00 committed by GitHub
parent c7ef2c5791
commit 1d211ee9f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 76 additions and 20 deletions

View File

@ -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')
}

View File

@ -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",

View File

@ -1071,6 +1071,8 @@
"editServer": "サーバーを編集",
"env": "環境変数",
"envTooltip": "形式: KEY=value, 1行に1つ",
"headers": "ヘッダー",
"headersTooltip": "HTTP リクエストのカスタムヘッダー",
"findMore": "MCP を見つける",
"searchNpx": "MCP を検索",
"install": "インストール",

View File

@ -1071,6 +1071,8 @@
"editServer": "Редактировать сервер",
"env": "Переменные окружения",
"envTooltip": "Формат: KEY=value, по одной на строку",
"headers": "Заголовки",
"headersTooltip": "Пользовательские заголовки для HTTP-запросов",
"findMore": "Найти больше MCP",
"searchNpx": "Найти MCP",
"install": "Установить",

View File

@ -1072,6 +1072,8 @@
"editServer": "编辑服务器",
"env": "环境变量",
"envTooltip": "格式KEY=value每行一个",
"headers": "请求头",
"headersTooltip": "HTTP 请求的自定义请求头",
"findMore": "更多 MCP",
"searchNpx": "搜索 MCP",
"install": "安装",

View File

@ -1071,6 +1071,8 @@
"editServer": "編輯伺服器",
"env": "環境變數",
"envTooltip": "格式KEY=value每行一個",
"headers": "請求標頭",
"headersTooltip": "HTTP 請求的自定義標頭",
"findMore": "更多 MCP",
"searchNpx": "搜索 MCP",
"install": "安裝",

View File

@ -27,6 +27,7 @@ interface MCPFormValues {
args?: string
env?: string
isActive: boolean
headers?: string
}
interface Registry {
@ -101,6 +102,11 @@ const McpSettings: React.FC<Props> = ({ 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<Props> = ({ server }) => {
mcpServer.env = env
}
if (values.headers) {
const headers: Record<string, string> = {}
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<Props> = ({ server }) => {
</Form.Item>
)}
{serverType === 'sse' && (
<Form.Item
name="baseUrl"
label={t('settings.mcp.url')}
rules={[{ required: serverType === 'sse', message: '' }]}
tooltip={t('settings.mcp.baseUrlTooltip')}>
<Input placeholder="http://localhost:3000/sse" />
</Form.Item>
<>
<Form.Item
name="baseUrl"
label={t('settings.mcp.url')}
rules={[{ required: serverType === 'sse', message: '' }]}
tooltip={t('settings.mcp.baseUrlTooltip')}>
<Input placeholder="http://localhost:3000/sse" />
</Form.Item>
<Form.Item name="headers" label={t('settings.mcp.headers')} tooltip={t('settings.mcp.headersTooltip')}>
<TextArea
rows={3}
placeholder={`Content-Type=application/json\nAuthorization=Bearer token`}
style={{ fontFamily: 'monospace' }}
/>
</Form.Item>
</>
)}
{serverType === 'streamableHttp' && (
<Form.Item
name="baseUrl"
label={t('settings.mcp.url')}
rules={[{ required: serverType === 'streamableHttp', message: '' }]}
tooltip={t('settings.mcp.baseUrlTooltip')}>
<Input placeholder="http://localhost:3000/mcp" />
</Form.Item>
<>
<Form.Item
name="baseUrl"
label={t('settings.mcp.url')}
rules={[{ required: serverType === 'streamableHttp', message: '' }]}
tooltip={t('settings.mcp.baseUrlTooltip')}>
<Input placeholder="http://localhost:3000/mcp" />
</Form.Item>
<Form.Item name="headers" label={t('settings.mcp.headers')} tooltip={t('settings.mcp.headersTooltip')}>
<TextArea
rows={3}
placeholder={`Content-Type=application/json\nAuthorization=Bearer token`}
style={{ fontFamily: 'monospace' }}
/>
</Form.Item>
</>
)}
{serverType === 'stdio' && (
<>

View File

@ -386,6 +386,7 @@ export interface MCPServer {
env?: Record<string, string>
isActive: boolean
disabledTools?: string[] // List of tool names that are disabled for this server
headers?: Record<string, string> // Custom headers to be sent with requests to this server
}
export interface MCPToolInputSchema {