feat: support multiple transport types for MCP server connections, including stdio and HTTP

This commit is contained in:
DESKTOP-RTLN3BA\$punk 2026-01-19 17:44:19 -08:00
parent bb5cb846b3
commit 9625a24475
9 changed files with 435 additions and 191 deletions

View file

@ -25,7 +25,8 @@ export const MCPConnectForm: FC<ConnectFormProps> = ({ onSubmit, isSubmitting })
const [showDetails, setShowDetails] = useState(false);
const [testResult, setTestResult] = useState<MCPConnectionTestResult | null>(null);
const DEFAULT_CONFIG = JSON.stringify(
// Default config for stdio transport (local process)
const DEFAULT_STDIO_CONFIG = JSON.stringify(
{
name: "My MCP Server",
command: "npx",
@ -39,6 +40,22 @@ export const MCPConnectForm: FC<ConnectFormProps> = ({ onSubmit, isSubmitting })
2
);
// Default config for HTTP transport (remote server)
const DEFAULT_HTTP_CONFIG = JSON.stringify(
{
name: "My Remote MCP Server",
url: "https://your-mcp-server.com/mcp",
headers: {
"API_KEY": "your_api_key_here",
},
transport: "streamable-http",
},
null,
2
);
const DEFAULT_CONFIG = DEFAULT_STDIO_CONFIG;
const parseConfig = () => {
const result = parseMCPConfig(configJson);
if (result.error) {
@ -132,7 +149,31 @@ export const MCPConnectForm: FC<ConnectFormProps> = ({ onSubmit, isSubmitting })
<form id="mcp-connect-form" onSubmit={handleSubmit} className="space-y-6">
<div className="rounded-xl border border-border bg-slate-400/5 dark:bg-white/5 p-4 sm:p-6 space-y-4">
<div className="space-y-2">
<Label htmlFor="config">MCP Server Configuration (JSON)</Label>
<div className="flex items-center justify-between flex-wrap gap-2">
<Label htmlFor="config">MCP Server Configuration (JSON)</Label>
{!configJson && (
<div className="flex gap-1">
<Button
type="button"
variant="ghost"
size="sm"
className="h-6 px-2 text-xs text-muted-foreground hover:text-foreground"
onClick={() => handleConfigChange(DEFAULT_STDIO_CONFIG)}
>
Local Example
</Button>
<Button
type="button"
variant="ghost"
size="sm"
className="h-6 px-2 text-xs text-muted-foreground hover:text-foreground"
onClick={() => handleConfigChange(DEFAULT_HTTP_CONFIG)}
>
Remote Example
</Button>
</div>
)}
</div>
<Textarea
id="config"
value={configJson}
@ -143,8 +184,8 @@ export const MCPConnectForm: FC<ConnectFormProps> = ({ onSubmit, isSubmitting })
/>
{jsonError && <p className="text-xs text-red-500">JSON Error: {jsonError}</p>}
<p className="text-[10px] sm:text-xs text-muted-foreground">
Paste a single MCP server configuration. Must include: name, command, args (optional),
env (optional), transport (optional).
<strong>Local (stdio):</strong> command, args, env, transport: "stdio"<br />
<strong>Remote (HTTP):</strong> url, headers, transport: "streamable-http"
</p>
</div>

View file

@ -46,13 +46,28 @@ export const MCPConfig: FC<MCPConfigProps> = ({ connector, onConfigChange, onNam
const serverConfig = connector.config?.server_config as MCPServerConfig | undefined;
if (serverConfig) {
// Convert server config to JSON string for editing (name is in separate field)
const configObj = {
command: serverConfig.command || "",
args: serverConfig.args || [],
env: serverConfig.env || {},
transport: serverConfig.transport || "stdio",
};
const transport = serverConfig.transport || "stdio";
// Build config object based on transport type
let configObj: Record<string, unknown>;
if (transport === "streamable-http" || transport === "http" || transport === "sse") {
// HTTP transport - use url and headers
configObj = {
url: (serverConfig as any).url || "",
headers: (serverConfig as any).headers || {},
transport: transport,
};
} else {
// stdio transport (default) - use command, args, env
configObj = {
command: (serverConfig as any).command || "",
args: (serverConfig as any).args || [],
env: (serverConfig as any).env || {},
transport: transport,
};
}
setConfigJson(JSON.stringify(configObj, null, 2));
}
}, [isValidConnector, connector.name, connector.config?.server_config]);
@ -163,8 +178,8 @@ export const MCPConfig: FC<MCPConfigProps> = ({ connector, onConfigChange, onNam
/>
{jsonError && <p className="text-xs text-red-500">JSON Error: {jsonError}</p>}
<p className="text-[10px] sm:text-xs text-muted-foreground">
Edit your MCP server configuration. Must include: name, command, args (optional), env
(optional), transport (optional).
<strong>Local (stdio):</strong> command, args, env, transport: "stdio"<br />
<strong>Remote (HTTP):</strong> url, headers, transport: "streamable-http"
</p>
</div>

View file

@ -35,20 +35,27 @@ import { connectorsApiService } from "@/lib/apis/connectors-api.service";
/**
* Zod schema for MCP server configuration
* Provides compile-time and runtime type safety
* Supports both stdio (local process) and HTTP (remote server) transports
*
* Exported for advanced use cases (e.g., form builders)
*/
export const MCPServerConfigSchema = z.object({
const StdioConfigSchema = z.object({
name: z.string().optional(),
command: z
.string({ required_error: "Command field is required" })
.min(1, "Command cannot be empty"),
command: z.string().min(1, "Command cannot be empty"),
args: z.array(z.string()).optional().default([]),
env: z.record(z.string(), z.string()).optional().default({}),
transport: z.enum(["stdio", "sse"]).optional().default("stdio"),
transport: z.enum(["stdio"]).optional().default("stdio"),
});
const HttpConfigSchema = z.object({
name: z.string().optional(),
url: z.string().url("URL must be a valid URL"),
headers: z.record(z.string(), z.string()).optional().default({}),
transport: z.enum(["streamable-http", "http", "sse"]),
});
export const MCPServerConfigSchema = z.union([StdioConfigSchema, HttpConfigSchema]);
/**
* Shared MCP configuration validation result
*/
@ -147,12 +154,19 @@ export const parseMCPConfig = (configJson: string): MCPConfigValidationResult =>
};
}
const config: MCPServerConfig = {
command: result.data.command,
args: result.data.args,
env: result.data.env,
transport: result.data.transport,
};
// Build config based on transport type
const config: MCPServerConfig = result.data.transport === "stdio" || !result.data.transport
? {
command: (result.data as z.infer<typeof StdioConfigSchema>).command,
args: (result.data as z.infer<typeof StdioConfigSchema>).args,
env: (result.data as z.infer<typeof StdioConfigSchema>).env,
transport: "stdio" as const,
}
: {
url: (result.data as z.infer<typeof HttpConfigSchema>).url,
headers: (result.data as z.infer<typeof HttpConfigSchema>).headers,
transport: result.data.transport as "streamable-http" | "http" | "sse",
};
// Cache the successfully parsed config
configCache.set(configJson, {