Skip to content

Commit 42dde5c

Browse files
committed
fix(mcp): add type guards for MCP config override support
Add type guard functions to safely distinguish between full MCP configs (local/remote) and override-only configs which only have the 'enabled' field. - Add isFullMcpConfig, isLocalMcpConfig, isRemoteMcpConfig helpers - Update MCP CLI commands to use type guards - Update OAuth flow to use type guards - Filter override configs when adding MCP servers via ACP Fixes type errors introduced by McpOverrideConfig in the per-project MCP config overrides feature.
1 parent 4e42c69 commit 42dde5c

File tree

4 files changed

+50
-21
lines changed

4 files changed

+50
-21
lines changed

packages/opencode/src/acp/agent.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -495,20 +495,24 @@ export namespace ACP {
495495
}
496496

497497
await Promise.all(
498-
Object.entries(mcpServers).map(async ([key, mcp]) => {
499-
await this.sdk.mcp
500-
.add(
501-
{
502-
directory,
503-
name: key,
504-
config: mcp,
505-
},
506-
{ throwOnError: true },
507-
)
508-
.catch((error) => {
509-
log.error("failed to add mcp server", { name: key, error })
510-
})
511-
}),
498+
Object.entries(mcpServers)
499+
.filter((entry): entry is [string, Config.McpLocalConfig | Config.McpRemoteConfig] =>
500+
Config.isFullMcpConfig(entry[1]),
501+
)
502+
.map(async ([key, mcp]) => {
503+
await this.sdk.mcp
504+
.add(
505+
{
506+
directory,
507+
name: key,
508+
config: mcp,
509+
},
510+
{ throwOnError: true },
511+
)
512+
.catch((error) => {
513+
log.error("failed to add mcp server", { name: key, error })
514+
})
515+
}),
512516
)
513517

514518
setTimeout(() => {

packages/opencode/src/cli/cmd/mcp.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const McpListCommand = cmd({
4646

4747
for (const [name, serverConfig] of Object.entries(mcpServers)) {
4848
const status = statuses[name]
49-
const hasOAuth = serverConfig.type === "remote" && !!serverConfig.oauth
49+
const hasOAuth = Config.isRemoteMcpConfig(serverConfig) && !!serverConfig.oauth
5050
const hasStoredTokens = await MCP.hasStoredTokens(name)
5151

5252
let statusIcon: string
@@ -78,7 +78,11 @@ export const McpListCommand = cmd({
7878
hint = "\n " + status.error
7979
}
8080

81-
const typeHint = serverConfig.type === "remote" ? serverConfig.url : serverConfig.command.join(" ")
81+
const typeHint = Config.isRemoteMcpConfig(serverConfig)
82+
? serverConfig.url
83+
: Config.isLocalMcpConfig(serverConfig)
84+
? serverConfig.command.join(" ")
85+
: "(override)"
8286
prompts.log.info(
8387
`${statusIcon} ${name} ${UI.Style.TEXT_DIM}${statusText}${hint}\n ${UI.Style.TEXT_DIM}${typeHint}`,
8488
)
@@ -109,7 +113,9 @@ export const McpAuthCommand = cmd({
109113
const mcpServers = config.mcp ?? {}
110114

111115
// Get OAuth-enabled servers
112-
const oauthServers = Object.entries(mcpServers).filter(([_, cfg]) => cfg.type === "remote" && !!cfg.oauth)
116+
const oauthServers = Object.entries(mcpServers).filter(
117+
([_, cfg]) => Config.isRemoteMcpConfig(cfg) && !!cfg.oauth,
118+
)
113119

114120
if (oauthServers.length === 0) {
115121
prompts.log.warn("No OAuth-enabled MCP servers configured")
@@ -135,7 +141,7 @@ export const McpAuthCommand = cmd({
135141
options: oauthServers.map(([name, cfg]) => ({
136142
label: name,
137143
value: name,
138-
hint: cfg.type === "remote" ? cfg.url : undefined,
144+
hint: Config.isRemoteMcpConfig(cfg) ? cfg.url : undefined,
139145
})),
140146
})
141147
if (prompts.isCancel(selected)) throw new UI.CancelledError()
@@ -149,7 +155,7 @@ export const McpAuthCommand = cmd({
149155
return
150156
}
151157

152-
if (serverConfig.type !== "remote" || !serverConfig.oauth) {
158+
if (!Config.isRemoteMcpConfig(serverConfig) || !serverConfig.oauth) {
153159
prompts.log.error(`MCP server ${serverName} does not have OAuth configured`)
154160
prompts.outro("Done")
155161
return

packages/opencode/src/config/config.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,25 @@ export namespace Config {
379379
export const Mcp = z.union([z.discriminatedUnion("type", [McpLocal, McpRemote]), McpOverride])
380380
export type Mcp = z.infer<typeof Mcp>
381381

382+
export type McpLocalConfig = z.infer<typeof McpLocal>
383+
export type McpRemoteConfig = z.infer<typeof McpRemote>
384+
export type McpOverrideConfig = z.infer<typeof McpOverride>
385+
386+
/** Type guard to check if MCP config is a full config (local or remote) vs an override */
387+
export function isFullMcpConfig(config: Mcp): config is McpLocalConfig | McpRemoteConfig {
388+
return "type" in config
389+
}
390+
391+
/** Type guard to check if MCP config is a local config */
392+
export function isLocalMcpConfig(config: Mcp): config is McpLocalConfig {
393+
return "type" in config && config.type === "local"
394+
}
395+
396+
/** Type guard to check if MCP config is a remote config */
397+
export function isRemoteMcpConfig(config: Mcp): config is McpRemoteConfig {
398+
return "type" in config && config.type === "remote"
399+
}
400+
382401
export const Permission = z.enum(["ask", "allow", "deny"])
383402
export type Permission = z.infer<typeof Permission>
384403

packages/opencode/src/mcp/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ export namespace MCP {
435435
throw new Error(`MCP server not found: ${mcpName}`)
436436
}
437437

438-
if (mcpConfig.type !== "remote") {
438+
if (!Config.isRemoteMcpConfig(mcpConfig)) {
439439
throw new Error(`MCP server ${mcpName} is not a remote server`)
440440
}
441441

@@ -573,7 +573,7 @@ export namespace MCP {
573573
export async function supportsOAuth(mcpName: string): Promise<boolean> {
574574
const cfg = await Config.get()
575575
const mcpConfig = cfg.mcp?.[mcpName]
576-
return mcpConfig?.type === "remote" && mcpConfig.oauth !== false
576+
return !!mcpConfig && Config.isRemoteMcpConfig(mcpConfig) && mcpConfig.oauth !== false
577577
}
578578

579579
/**

0 commit comments

Comments
 (0)