refactor(mcp): remove async facade exports (#22324)
This commit is contained in:
@@ -15,7 +15,8 @@ import { Global } from "../../global"
|
||||
import { modify, applyEdits } from "jsonc-parser"
|
||||
import { Filesystem } from "../../util/filesystem"
|
||||
import { Bus } from "../../bus"
|
||||
import { AppRuntime } from "@/effect/app-runtime"
|
||||
import { AppRuntime } from "../../effect/app-runtime"
|
||||
import { Effect } from "effect"
|
||||
|
||||
function getAuthStatusIcon(status: MCP.AuthStatus): string {
|
||||
switch (status) {
|
||||
@@ -51,6 +52,47 @@ function isMcpRemote(config: McpEntry): config is McpRemote {
|
||||
return isMcpConfigured(config) && config.type === "remote"
|
||||
}
|
||||
|
||||
function configuredServers(config: Config.Info) {
|
||||
return Object.entries(config.mcp ?? {}).filter((entry): entry is [string, McpConfigured] => isMcpConfigured(entry[1]))
|
||||
}
|
||||
|
||||
function oauthServers(config: Config.Info) {
|
||||
return configuredServers(config).filter(
|
||||
(entry): entry is [string, McpRemote] => isMcpRemote(entry[1]) && entry[1].oauth !== false,
|
||||
)
|
||||
}
|
||||
|
||||
async function listState() {
|
||||
return AppRuntime.runPromise(
|
||||
Effect.gen(function* () {
|
||||
const cfg = yield* Config.Service
|
||||
const mcp = yield* MCP.Service
|
||||
const config = yield* cfg.get()
|
||||
const statuses = yield* mcp.status()
|
||||
const stored = yield* Effect.all(
|
||||
Object.fromEntries(configuredServers(config).map(([name]) => [name, mcp.hasStoredTokens(name)])),
|
||||
{ concurrency: "unbounded" },
|
||||
)
|
||||
return { config, statuses, stored }
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
async function authState() {
|
||||
return AppRuntime.runPromise(
|
||||
Effect.gen(function* () {
|
||||
const cfg = yield* Config.Service
|
||||
const mcp = yield* MCP.Service
|
||||
const config = yield* cfg.get()
|
||||
const auth = yield* Effect.all(
|
||||
Object.fromEntries(oauthServers(config).map(([name]) => [name, mcp.getAuthStatus(name)])),
|
||||
{ concurrency: "unbounded" },
|
||||
)
|
||||
return { config, auth }
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
export const McpCommand = cmd({
|
||||
command: "mcp",
|
||||
describe: "manage MCP (Model Context Protocol) servers",
|
||||
@@ -76,13 +118,8 @@ export const McpListCommand = cmd({
|
||||
UI.empty()
|
||||
prompts.intro("MCP Servers")
|
||||
|
||||
const config = await AppRuntime.runPromise(Config.Service.use((cfg) => cfg.get()))
|
||||
const mcpServers = config.mcp ?? {}
|
||||
const statuses = await MCP.status()
|
||||
|
||||
const servers = Object.entries(mcpServers).filter((entry): entry is [string, McpConfigured] =>
|
||||
isMcpConfigured(entry[1]),
|
||||
)
|
||||
const { config, statuses, stored } = await listState()
|
||||
const servers = configuredServers(config)
|
||||
|
||||
if (servers.length === 0) {
|
||||
prompts.log.warn("No MCP servers configured")
|
||||
@@ -93,7 +130,7 @@ export const McpListCommand = cmd({
|
||||
for (const [name, serverConfig] of servers) {
|
||||
const status = statuses[name]
|
||||
const hasOAuth = isMcpRemote(serverConfig) && !!serverConfig.oauth
|
||||
const hasStoredTokens = await MCP.hasStoredTokens(name)
|
||||
const hasStoredTokens = stored[name]
|
||||
|
||||
let statusIcon: string
|
||||
let statusText: string
|
||||
@@ -153,15 +190,11 @@ export const McpAuthCommand = cmd({
|
||||
UI.empty()
|
||||
prompts.intro("MCP OAuth Authentication")
|
||||
|
||||
const config = await AppRuntime.runPromise(Config.Service.use((cfg) => cfg.get()))
|
||||
const { config, auth } = await authState()
|
||||
const mcpServers = config.mcp ?? {}
|
||||
const servers = oauthServers(config)
|
||||
|
||||
// Get OAuth-capable servers (remote servers with oauth not explicitly disabled)
|
||||
const oauthServers = Object.entries(mcpServers).filter(
|
||||
(entry): entry is [string, McpRemote] => isMcpRemote(entry[1]) && entry[1].oauth !== false,
|
||||
)
|
||||
|
||||
if (oauthServers.length === 0) {
|
||||
if (servers.length === 0) {
|
||||
prompts.log.warn("No OAuth-capable MCP servers configured")
|
||||
prompts.log.info("Remote MCP servers support OAuth by default. Add a remote server in opencode.json:")
|
||||
prompts.log.info(`
|
||||
@@ -178,19 +211,17 @@ export const McpAuthCommand = cmd({
|
||||
let serverName = args.name
|
||||
if (!serverName) {
|
||||
// Build options with auth status
|
||||
const options = await Promise.all(
|
||||
oauthServers.map(async ([name, cfg]) => {
|
||||
const authStatus = await MCP.getAuthStatus(name)
|
||||
const icon = getAuthStatusIcon(authStatus)
|
||||
const statusText = getAuthStatusText(authStatus)
|
||||
const url = cfg.url
|
||||
return {
|
||||
label: `${icon} ${name} (${statusText})`,
|
||||
value: name,
|
||||
hint: url,
|
||||
}
|
||||
}),
|
||||
)
|
||||
const options = servers.map(([name, cfg]) => {
|
||||
const authStatus = auth[name]
|
||||
const icon = getAuthStatusIcon(authStatus)
|
||||
const statusText = getAuthStatusText(authStatus)
|
||||
const url = cfg.url
|
||||
return {
|
||||
label: `${icon} ${name} (${statusText})`,
|
||||
value: name,
|
||||
hint: url,
|
||||
}
|
||||
})
|
||||
|
||||
const selected = await prompts.select({
|
||||
message: "Select MCP server to authenticate",
|
||||
@@ -214,7 +245,8 @@ export const McpAuthCommand = cmd({
|
||||
}
|
||||
|
||||
// Check if already authenticated
|
||||
const authStatus = await MCP.getAuthStatus(serverName)
|
||||
const authStatus =
|
||||
auth[serverName] ?? (await AppRuntime.runPromise(MCP.Service.use((mcp) => mcp.getAuthStatus(serverName))))
|
||||
if (authStatus === "authenticated") {
|
||||
const confirm = await prompts.confirm({
|
||||
message: `${serverName} already has valid credentials. Re-authenticate?`,
|
||||
@@ -241,7 +273,7 @@ export const McpAuthCommand = cmd({
|
||||
})
|
||||
|
||||
try {
|
||||
const status = await MCP.authenticate(serverName)
|
||||
const status = await AppRuntime.runPromise(MCP.Service.use((mcp) => mcp.authenticate(serverName)))
|
||||
|
||||
if (status.status === "connected") {
|
||||
spinner.stop("Authentication successful!")
|
||||
@@ -290,22 +322,17 @@ export const McpAuthListCommand = cmd({
|
||||
UI.empty()
|
||||
prompts.intro("MCP OAuth Status")
|
||||
|
||||
const config = await AppRuntime.runPromise(Config.Service.use((cfg) => cfg.get()))
|
||||
const mcpServers = config.mcp ?? {}
|
||||
const { config, auth } = await authState()
|
||||
const servers = oauthServers(config)
|
||||
|
||||
// Get OAuth-capable servers
|
||||
const oauthServers = Object.entries(mcpServers).filter(
|
||||
(entry): entry is [string, McpRemote] => isMcpRemote(entry[1]) && entry[1].oauth !== false,
|
||||
)
|
||||
|
||||
if (oauthServers.length === 0) {
|
||||
if (servers.length === 0) {
|
||||
prompts.log.warn("No OAuth-capable MCP servers configured")
|
||||
prompts.outro("Done")
|
||||
return
|
||||
}
|
||||
|
||||
for (const [name, serverConfig] of oauthServers) {
|
||||
const authStatus = await MCP.getAuthStatus(name)
|
||||
for (const [name, serverConfig] of servers) {
|
||||
const authStatus = auth[name]
|
||||
const icon = getAuthStatusIcon(authStatus)
|
||||
const statusText = getAuthStatusText(authStatus)
|
||||
const url = serverConfig.url
|
||||
@@ -313,7 +340,7 @@ export const McpAuthListCommand = cmd({
|
||||
prompts.log.info(`${icon} ${name} ${UI.Style.TEXT_DIM}${statusText}\n ${UI.Style.TEXT_DIM}${url}`)
|
||||
}
|
||||
|
||||
prompts.outro(`${oauthServers.length} OAuth-capable server(s)`)
|
||||
prompts.outro(`${servers.length} OAuth-capable server(s)`)
|
||||
},
|
||||
})
|
||||
},
|
||||
@@ -335,7 +362,7 @@ export const McpLogoutCommand = cmd({
|
||||
prompts.intro("MCP OAuth Logout")
|
||||
|
||||
const authPath = path.join(Global.Path.data, "mcp-auth.json")
|
||||
const credentials = await McpAuth.all()
|
||||
const credentials = await AppRuntime.runPromise(McpAuth.Service.use((auth) => auth.all()))
|
||||
const serverNames = Object.keys(credentials)
|
||||
|
||||
if (serverNames.length === 0) {
|
||||
@@ -373,7 +400,7 @@ export const McpLogoutCommand = cmd({
|
||||
return
|
||||
}
|
||||
|
||||
await MCP.removeAuth(serverName)
|
||||
await AppRuntime.runPromise(MCP.Service.use((mcp) => mcp.removeAuth(serverName)))
|
||||
prompts.log.success(`Removed OAuth credentials for ${serverName}`)
|
||||
prompts.outro("Done")
|
||||
},
|
||||
@@ -623,10 +650,18 @@ export const McpDebugCommand = cmd({
|
||||
prompts.log.info(`URL: ${serverConfig.url}`)
|
||||
|
||||
// Check stored auth status
|
||||
const authStatus = await MCP.getAuthStatus(serverName)
|
||||
const { authStatus, entry } = await AppRuntime.runPromise(
|
||||
Effect.gen(function* () {
|
||||
const mcp = yield* MCP.Service
|
||||
const auth = yield* McpAuth.Service
|
||||
return {
|
||||
authStatus: yield* mcp.getAuthStatus(serverName),
|
||||
entry: yield* auth.get(serverName),
|
||||
}
|
||||
}),
|
||||
)
|
||||
prompts.log.info(`Auth status: ${getAuthStatusIcon(authStatus)} ${getAuthStatusText(authStatus)}`)
|
||||
|
||||
const entry = await McpAuth.get(serverName)
|
||||
if (entry?.tokens) {
|
||||
prompts.log.info(` Access token: ${entry.tokens.accessToken.substring(0, 20)}...`)
|
||||
if (entry.tokens.expiresAt) {
|
||||
|
||||
Reference in New Issue
Block a user