mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-26 01:06:23 +02:00
feat: support multiple transport types for MCP server connections, including stdio and HTTP
This commit is contained in:
parent
bb5cb846b3
commit
9625a24475
9 changed files with 435 additions and 191 deletions
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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, {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue