Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions packages/opencode/src/mcp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ function isMcpConfigured(entry: McpEntry): entry is ConfigMCP.Info {

const sanitize = (s: string) => s.replace(/[^a-zA-Z0-9_-]/g, "_")

function remoteURL(key: string, value: string) {
if (URL.canParse(value)) return new URL(value)
log.warn("invalid remote mcp url", { key })
}

// Convert MCP tool definition to AI SDK Tool type
function convertMcpTool(mcpTool: MCPToolDef, client: MCPClient, timeout?: number): Tool {
const inputSchema = mcpTool.inputSchema
Expand Down Expand Up @@ -267,6 +272,13 @@ export const layer = Layer.effect(
) {
const oauthDisabled = mcp.oauth === false
const oauthConfig = typeof mcp.oauth === "object" ? mcp.oauth : undefined
const url = remoteURL(key, mcp.url)
if (!url) {
return {
client: undefined as MCPClient | undefined,
status: { status: "failed" as const, error: `Invalid MCP URL for "${key}"` },
}
}
let authProvider: McpOAuthProvider | undefined

if (!oauthDisabled) {
Expand All @@ -291,14 +303,14 @@ export const layer = Layer.effect(
const transports: Array<{ name: string; transport: TransportWithAuth }> = [
{
name: "StreamableHTTP",
transport: new StreamableHTTPClientTransport(new URL(mcp.url), {
transport: new StreamableHTTPClientTransport(url, {
authProvider,
requestInit: mcp.headers ? { headers: mcp.headers } : undefined,
}),
},
{
name: "SSE",
transport: new SSEClientTransport(new URL(mcp.url), {
transport: new SSEClientTransport(url, {
authProvider,
requestInit: mcp.headers ? { headers: mcp.headers } : undefined,
}),
Expand Down Expand Up @@ -722,6 +734,8 @@ export const layer = Layer.effect(
if (!mcpConfig) throw new Error(`MCP server ${mcpName} not found or disabled`)
if (mcpConfig.type !== "remote") throw new Error(`MCP server ${mcpName} is not a remote server`)
if (mcpConfig.oauth === false) throw new Error(`MCP server ${mcpName} has OAuth explicitly disabled`)
const url = remoteURL(mcpName, mcpConfig.url)
if (!url) throw new Error(`Invalid MCP URL for "${mcpName}"`)

// OAuth config is optional - if not provided, we'll use auto-discovery
const oauthConfig = typeof mcpConfig.oauth === "object" ? mcpConfig.oauth : undefined
Expand Down Expand Up @@ -751,7 +765,7 @@ export const layer = Layer.effect(
auth,
)

const transport = new StreamableHTTPClientTransport(new URL(mcpConfig.url), { authProvider })
const transport = new StreamableHTTPClientTransport(url, { authProvider })

return yield* Effect.tryPromise({
try: () => {
Expand Down
Loading