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:
parent
c7ef2c5791
commit
1d211ee9f7
@ -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')
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -1071,6 +1071,8 @@
|
||||
"editServer": "サーバーを編集",
|
||||
"env": "環境変数",
|
||||
"envTooltip": "形式: KEY=value, 1行に1つ",
|
||||
"headers": "ヘッダー",
|
||||
"headersTooltip": "HTTP リクエストのカスタムヘッダー",
|
||||
"findMore": "MCP を見つける",
|
||||
"searchNpx": "MCP を検索",
|
||||
"install": "インストール",
|
||||
|
||||
@ -1071,6 +1071,8 @@
|
||||
"editServer": "Редактировать сервер",
|
||||
"env": "Переменные окружения",
|
||||
"envTooltip": "Формат: KEY=value, по одной на строку",
|
||||
"headers": "Заголовки",
|
||||
"headersTooltip": "Пользовательские заголовки для HTTP-запросов",
|
||||
"findMore": "Найти больше MCP",
|
||||
"searchNpx": "Найти MCP",
|
||||
"install": "Установить",
|
||||
|
||||
@ -1072,6 +1072,8 @@
|
||||
"editServer": "编辑服务器",
|
||||
"env": "环境变量",
|
||||
"envTooltip": "格式:KEY=value,每行一个",
|
||||
"headers": "请求头",
|
||||
"headersTooltip": "HTTP 请求的自定义请求头",
|
||||
"findMore": "更多 MCP",
|
||||
"searchNpx": "搜索 MCP",
|
||||
"install": "安装",
|
||||
|
||||
@ -1071,6 +1071,8 @@
|
||||
"editServer": "編輯伺服器",
|
||||
"env": "環境變數",
|
||||
"envTooltip": "格式:KEY=value,每行一個",
|
||||
"headers": "請求標頭",
|
||||
"headersTooltip": "HTTP 請求的自定義標頭",
|
||||
"findMore": "更多 MCP",
|
||||
"searchNpx": "搜索 MCP",
|
||||
"install": "安裝",
|
||||
|
||||
@ -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' && (
|
||||
<>
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user