refactor(cli): convert mcp list, auth, auth list, logout to effectCmd (#25521)
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
import { cmd } from "./cmd"
|
import { cmd } from "./cmd"
|
||||||
|
import { effectCmd } from "../effect-cmd"
|
||||||
|
import { Cause } from "effect"
|
||||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
|
||||||
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
|
||||||
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js"
|
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js"
|
||||||
@@ -65,35 +67,31 @@ function oauthServers(config: Config.Info) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listState() {
|
function listState() {
|
||||||
return AppRuntime.runPromise(
|
return Effect.gen(function* () {
|
||||||
Effect.gen(function* () {
|
const cfg = yield* Config.Service
|
||||||
const cfg = yield* Config.Service
|
const mcp = yield* MCP.Service
|
||||||
const mcp = yield* MCP.Service
|
const config = yield* cfg.get()
|
||||||
const config = yield* cfg.get()
|
const statuses = yield* mcp.status()
|
||||||
const statuses = yield* mcp.status()
|
const stored = yield* Effect.all(
|
||||||
const stored = yield* Effect.all(
|
Object.fromEntries(configuredServers(config).map(([name]) => [name, mcp.hasStoredTokens(name)])),
|
||||||
Object.fromEntries(configuredServers(config).map(([name]) => [name, mcp.hasStoredTokens(name)])),
|
{ concurrency: "unbounded" },
|
||||||
{ concurrency: "unbounded" },
|
)
|
||||||
)
|
return { config, statuses, stored }
|
||||||
return { config, statuses, stored }
|
})
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function authState() {
|
function authState() {
|
||||||
return AppRuntime.runPromise(
|
return Effect.gen(function* () {
|
||||||
Effect.gen(function* () {
|
const cfg = yield* Config.Service
|
||||||
const cfg = yield* Config.Service
|
const mcp = yield* MCP.Service
|
||||||
const mcp = yield* MCP.Service
|
const config = yield* cfg.get()
|
||||||
const config = yield* cfg.get()
|
const auth = yield* Effect.all(
|
||||||
const auth = yield* Effect.all(
|
Object.fromEntries(oauthServers(config).map(([name]) => [name, mcp.getAuthStatus(name)])),
|
||||||
Object.fromEntries(oauthServers(config).map(([name]) => [name, mcp.getAuthStatus(name)])),
|
{ concurrency: "unbounded" },
|
||||||
{ concurrency: "unbounded" },
|
)
|
||||||
)
|
return { config, auth }
|
||||||
return { config, auth }
|
})
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const McpCommand = cmd({
|
export const McpCommand = cmd({
|
||||||
@@ -110,73 +108,68 @@ export const McpCommand = cmd({
|
|||||||
async handler() {},
|
async handler() {},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const McpListCommand = cmd({
|
export const McpListCommand = effectCmd({
|
||||||
command: "list",
|
command: "list",
|
||||||
aliases: ["ls"],
|
aliases: ["ls"],
|
||||||
describe: "list MCP servers and their status",
|
describe: "list MCP servers and their status",
|
||||||
async handler() {
|
handler: Effect.fn("Cli.mcp.list")(function* () {
|
||||||
await WithInstance.provide({
|
UI.empty()
|
||||||
directory: process.cwd(),
|
prompts.intro("MCP Servers")
|
||||||
async fn() {
|
|
||||||
UI.empty()
|
|
||||||
prompts.intro("MCP Servers")
|
|
||||||
|
|
||||||
const { config, statuses, stored } = await listState()
|
const { config, statuses, stored } = yield* listState()
|
||||||
const servers = configuredServers(config)
|
const servers = configuredServers(config)
|
||||||
|
|
||||||
if (servers.length === 0) {
|
if (servers.length === 0) {
|
||||||
prompts.log.warn("No MCP servers configured")
|
prompts.log.warn("No MCP servers configured")
|
||||||
prompts.outro("Add servers with: opencode mcp add")
|
prompts.outro("Add servers with: opencode mcp add")
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [name, serverConfig] of servers) {
|
||||||
|
const status = statuses[name]
|
||||||
|
const hasOAuth = isMcpRemote(serverConfig) && !!serverConfig.oauth
|
||||||
|
const hasStoredTokens = stored[name]
|
||||||
|
|
||||||
|
let statusIcon: string
|
||||||
|
let statusText: string
|
||||||
|
let hint = ""
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
statusIcon = "○"
|
||||||
|
statusText = "not initialized"
|
||||||
|
} else if (status.status === "connected") {
|
||||||
|
statusIcon = "✓"
|
||||||
|
statusText = "connected"
|
||||||
|
if (hasOAuth && hasStoredTokens) {
|
||||||
|
hint = " (OAuth)"
|
||||||
}
|
}
|
||||||
|
} else if (status.status === "disabled") {
|
||||||
|
statusIcon = "○"
|
||||||
|
statusText = "disabled"
|
||||||
|
} else if (status.status === "needs_auth") {
|
||||||
|
statusIcon = "⚠"
|
||||||
|
statusText = "needs authentication"
|
||||||
|
} else if (status.status === "needs_client_registration") {
|
||||||
|
statusIcon = "✗"
|
||||||
|
statusText = "needs client registration"
|
||||||
|
hint = "\n " + status.error
|
||||||
|
} else {
|
||||||
|
statusIcon = "✗"
|
||||||
|
statusText = "failed"
|
||||||
|
hint = "\n " + status.error
|
||||||
|
}
|
||||||
|
|
||||||
for (const [name, serverConfig] of servers) {
|
const typeHint = serverConfig.type === "remote" ? serverConfig.url : serverConfig.command.join(" ")
|
||||||
const status = statuses[name]
|
prompts.log.info(
|
||||||
const hasOAuth = isMcpRemote(serverConfig) && !!serverConfig.oauth
|
`${statusIcon} ${name} ${UI.Style.TEXT_DIM}${statusText}${hint}\n ${UI.Style.TEXT_DIM}${typeHint}`,
|
||||||
const hasStoredTokens = stored[name]
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let statusIcon: string
|
prompts.outro(`${servers.length} server(s)`)
|
||||||
let statusText: string
|
}),
|
||||||
let hint = ""
|
|
||||||
|
|
||||||
if (!status) {
|
|
||||||
statusIcon = "○"
|
|
||||||
statusText = "not initialized"
|
|
||||||
} else if (status.status === "connected") {
|
|
||||||
statusIcon = "✓"
|
|
||||||
statusText = "connected"
|
|
||||||
if (hasOAuth && hasStoredTokens) {
|
|
||||||
hint = " (OAuth)"
|
|
||||||
}
|
|
||||||
} else if (status.status === "disabled") {
|
|
||||||
statusIcon = "○"
|
|
||||||
statusText = "disabled"
|
|
||||||
} else if (status.status === "needs_auth") {
|
|
||||||
statusIcon = "⚠"
|
|
||||||
statusText = "needs authentication"
|
|
||||||
} else if (status.status === "needs_client_registration") {
|
|
||||||
statusIcon = "✗"
|
|
||||||
statusText = "needs client registration"
|
|
||||||
hint = "\n " + status.error
|
|
||||||
} else {
|
|
||||||
statusIcon = "✗"
|
|
||||||
statusText = "failed"
|
|
||||||
hint = "\n " + status.error
|
|
||||||
}
|
|
||||||
|
|
||||||
const typeHint = serverConfig.type === "remote" ? serverConfig.url : serverConfig.command.join(" ")
|
|
||||||
prompts.log.info(
|
|
||||||
`${statusIcon} ${name} ${UI.Style.TEXT_DIM}${statusText}${hint}\n ${UI.Style.TEXT_DIM}${typeHint}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
prompts.outro(`${servers.length} server(s)`)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const McpAuthCommand = cmd({
|
export const McpAuthCommand = effectCmd({
|
||||||
command: "auth [name]",
|
command: "auth [name]",
|
||||||
describe: "authenticate with an OAuth-enabled MCP server",
|
describe: "authenticate with an OAuth-enabled MCP server",
|
||||||
builder: (yargs) =>
|
builder: (yargs) =>
|
||||||
@@ -186,105 +179,106 @@ export const McpAuthCommand = cmd({
|
|||||||
type: "string",
|
type: "string",
|
||||||
})
|
})
|
||||||
.command(McpAuthListCommand),
|
.command(McpAuthListCommand),
|
||||||
async handler(args) {
|
handler: Effect.fn("Cli.mcp.auth")(function* (args) {
|
||||||
await WithInstance.provide({
|
UI.empty()
|
||||||
directory: process.cwd(),
|
prompts.intro("MCP OAuth Authentication")
|
||||||
async fn() {
|
|
||||||
UI.empty()
|
|
||||||
prompts.intro("MCP OAuth Authentication")
|
|
||||||
|
|
||||||
const { config, auth } = await authState()
|
const { config, auth } = yield* authState()
|
||||||
const mcpServers = config.mcp ?? {}
|
const mcpServers = config.mcp ?? {}
|
||||||
const servers = oauthServers(config)
|
const servers = oauthServers(config)
|
||||||
|
|
||||||
if (servers.length === 0) {
|
if (servers.length === 0) {
|
||||||
prompts.log.warn("No OAuth-capable MCP servers configured")
|
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("Remote MCP servers support OAuth by default. Add a remote server in opencode.json:")
|
||||||
prompts.log.info(`
|
prompts.log.info(`
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"my-server": {
|
"my-server": {
|
||||||
"type": "remote",
|
"type": "remote",
|
||||||
"url": "https://example.com/mcp"
|
"url": "https://example.com/mcp"
|
||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
prompts.outro("Done")
|
prompts.outro("Done")
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let serverName = args.name
|
||||||
|
if (!serverName) {
|
||||||
|
// Build options with auth status
|
||||||
|
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,
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
let serverName = args.name
|
const selected = yield* Effect.promise(() =>
|
||||||
if (!serverName) {
|
prompts.select({
|
||||||
// Build options with auth status
|
message: "Select MCP server to authenticate",
|
||||||
const options = servers.map(([name, cfg]) => {
|
options,
|
||||||
const authStatus = auth[name]
|
}),
|
||||||
const icon = getAuthStatusIcon(authStatus)
|
)
|
||||||
const statusText = getAuthStatusText(authStatus)
|
if (prompts.isCancel(selected)) throw new UI.CancelledError()
|
||||||
const url = cfg.url
|
serverName = selected
|
||||||
return {
|
}
|
||||||
label: `${icon} ${name} (${statusText})`,
|
|
||||||
value: name,
|
|
||||||
hint: url,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const selected = await prompts.select({
|
const serverConfig = mcpServers[serverName]
|
||||||
message: "Select MCP server to authenticate",
|
if (!serverConfig) {
|
||||||
options,
|
prompts.log.error(`MCP server not found: ${serverName}`)
|
||||||
})
|
prompts.outro("Done")
|
||||||
if (prompts.isCancel(selected)) throw new UI.CancelledError()
|
return
|
||||||
serverName = selected
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const serverConfig = mcpServers[serverName]
|
if (!isMcpRemote(serverConfig) || serverConfig.oauth === false) {
|
||||||
if (!serverConfig) {
|
prompts.log.error(`MCP server ${serverName} is not an OAuth-capable remote server`)
|
||||||
prompts.log.error(`MCP server not found: ${serverName}`)
|
prompts.outro("Done")
|
||||||
prompts.outro("Done")
|
return
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!isMcpRemote(serverConfig) || serverConfig.oauth === false) {
|
// Check if already authenticated
|
||||||
prompts.log.error(`MCP server ${serverName} is not an OAuth-capable remote server`)
|
const authStatus = auth[serverName] ?? (yield* MCP.Service.use((mcp) => mcp.getAuthStatus(serverName)))
|
||||||
prompts.outro("Done")
|
if (authStatus === "authenticated") {
|
||||||
return
|
const confirm = yield* Effect.promise(() =>
|
||||||
}
|
prompts.confirm({
|
||||||
|
message: `${serverName} already has valid credentials. Re-authenticate?`,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
if (prompts.isCancel(confirm) || !confirm) {
|
||||||
|
prompts.outro("Cancelled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if (authStatus === "expired") {
|
||||||
|
prompts.log.warn(`${serverName} has expired credentials. Re-authenticating...`)
|
||||||
|
}
|
||||||
|
|
||||||
// Check if already authenticated
|
const spinner = prompts.spinner()
|
||||||
const authStatus =
|
spinner.start("Starting OAuth flow...")
|
||||||
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?`,
|
|
||||||
})
|
|
||||||
if (prompts.isCancel(confirm) || !confirm) {
|
|
||||||
prompts.outro("Cancelled")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if (authStatus === "expired") {
|
|
||||||
prompts.log.warn(`${serverName} has expired credentials. Re-authenticating...`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const spinner = prompts.spinner()
|
// Subscribe to browser open failure events to show URL for manual opening
|
||||||
spinner.start("Starting OAuth flow...")
|
const unsubscribe = Bus.subscribe(MCP.BrowserOpenFailed, (evt) => {
|
||||||
|
if (evt.properties.mcpName === serverName) {
|
||||||
|
spinner.stop("Could not open browser automatically")
|
||||||
|
prompts.log.warn("Please open this URL in your browser to authenticate:")
|
||||||
|
prompts.log.info(evt.properties.url)
|
||||||
|
spinner.start("Waiting for authorization...")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Subscribe to browser open failure events to show URL for manual opening
|
yield* MCP.Service.use((mcp) => mcp.authenticate(serverName))
|
||||||
const unsubscribe = Bus.subscribe(MCP.BrowserOpenFailed, (evt) => {
|
.pipe(
|
||||||
if (evt.properties.mcpName === serverName) {
|
Effect.tap((status) =>
|
||||||
spinner.stop("Could not open browser automatically")
|
Effect.sync(() => {
|
||||||
prompts.log.warn("Please open this URL in your browser to authenticate:")
|
if (status.status === "connected") {
|
||||||
prompts.log.info(evt.properties.url)
|
spinner.stop("Authentication successful!")
|
||||||
spinner.start("Waiting for authorization...")
|
} else if (status.status === "needs_client_registration") {
|
||||||
}
|
spinner.stop("Authentication failed", 1)
|
||||||
})
|
prompts.log.error(status.error)
|
||||||
|
prompts.log.info("Add clientId to your MCP server config:")
|
||||||
try {
|
prompts.log.info(`
|
||||||
const status = await AppRuntime.runPromise(MCP.Service.use((mcp) => mcp.authenticate(serverName)))
|
|
||||||
|
|
||||||
if (status.status === "connected") {
|
|
||||||
spinner.stop("Authentication successful!")
|
|
||||||
} else if (status.status === "needs_client_registration") {
|
|
||||||
spinner.stop("Authentication failed", 1)
|
|
||||||
prompts.log.error(status.error)
|
|
||||||
prompts.log.info("Add clientId to your MCP server config:")
|
|
||||||
prompts.log.info(`
|
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"${serverName}": {
|
"${serverName}": {
|
||||||
"type": "remote",
|
"type": "remote",
|
||||||
@@ -295,61 +289,59 @@ export const McpAuthCommand = cmd({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`)
|
}`)
|
||||||
} else if (status.status === "failed") {
|
} else if (status.status === "failed") {
|
||||||
|
spinner.stop("Authentication failed", 1)
|
||||||
|
prompts.log.error(status.error)
|
||||||
|
} else {
|
||||||
|
spinner.stop("Unexpected status: " + status.status, 1)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Effect.catchCause((cause) =>
|
||||||
|
Effect.sync(() => {
|
||||||
spinner.stop("Authentication failed", 1)
|
spinner.stop("Authentication failed", 1)
|
||||||
prompts.log.error(status.error)
|
const error = Cause.squash(cause)
|
||||||
} else {
|
prompts.log.error(error instanceof Error ? error.message : String(error))
|
||||||
spinner.stop("Unexpected status: " + status.status, 1)
|
}),
|
||||||
}
|
),
|
||||||
} catch (error) {
|
Effect.ensuring(Effect.sync(() => unsubscribe())),
|
||||||
spinner.stop("Authentication failed", 1)
|
)
|
||||||
prompts.log.error(error instanceof Error ? error.message : String(error))
|
|
||||||
} finally {
|
|
||||||
unsubscribe()
|
|
||||||
}
|
|
||||||
|
|
||||||
prompts.outro("Done")
|
prompts.outro("Done")
|
||||||
},
|
}),
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const McpAuthListCommand = cmd({
|
export const McpAuthListCommand = effectCmd({
|
||||||
command: "list",
|
command: "list",
|
||||||
aliases: ["ls"],
|
aliases: ["ls"],
|
||||||
describe: "list OAuth-capable MCP servers and their auth status",
|
describe: "list OAuth-capable MCP servers and their auth status",
|
||||||
async handler() {
|
handler: Effect.fn("Cli.mcp.auth.list")(function* () {
|
||||||
await WithInstance.provide({
|
UI.empty()
|
||||||
directory: process.cwd(),
|
prompts.intro("MCP OAuth Status")
|
||||||
async fn() {
|
|
||||||
UI.empty()
|
|
||||||
prompts.intro("MCP OAuth Status")
|
|
||||||
|
|
||||||
const { config, auth } = await authState()
|
const { config, auth } = yield* authState()
|
||||||
const servers = oauthServers(config)
|
const servers = oauthServers(config)
|
||||||
|
|
||||||
if (servers.length === 0) {
|
if (servers.length === 0) {
|
||||||
prompts.log.warn("No OAuth-capable MCP servers configured")
|
prompts.log.warn("No OAuth-capable MCP servers configured")
|
||||||
prompts.outro("Done")
|
prompts.outro("Done")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [name, serverConfig] of servers) {
|
for (const [name, serverConfig] of servers) {
|
||||||
const authStatus = auth[name]
|
const authStatus = auth[name]
|
||||||
const icon = getAuthStatusIcon(authStatus)
|
const icon = getAuthStatusIcon(authStatus)
|
||||||
const statusText = getAuthStatusText(authStatus)
|
const statusText = getAuthStatusText(authStatus)
|
||||||
const url = serverConfig.url
|
const url = serverConfig.url
|
||||||
|
|
||||||
prompts.log.info(`${icon} ${name} ${UI.Style.TEXT_DIM}${statusText}\n ${UI.Style.TEXT_DIM}${url}`)
|
prompts.log.info(`${icon} ${name} ${UI.Style.TEXT_DIM}${statusText}\n ${UI.Style.TEXT_DIM}${url}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
prompts.outro(`${servers.length} OAuth-capable server(s)`)
|
prompts.outro(`${servers.length} OAuth-capable server(s)`)
|
||||||
},
|
}),
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const McpLogoutCommand = cmd({
|
export const McpLogoutCommand = effectCmd({
|
||||||
command: "logout [name]",
|
command: "logout [name]",
|
||||||
describe: "remove OAuth credentials for an MCP server",
|
describe: "remove OAuth credentials for an MCP server",
|
||||||
builder: (yargs) =>
|
builder: (yargs) =>
|
||||||
@@ -357,57 +349,54 @@ export const McpLogoutCommand = cmd({
|
|||||||
describe: "name of the MCP server",
|
describe: "name of the MCP server",
|
||||||
type: "string",
|
type: "string",
|
||||||
}),
|
}),
|
||||||
async handler(args) {
|
handler: Effect.fn("Cli.mcp.logout")(function* (args) {
|
||||||
await WithInstance.provide({
|
UI.empty()
|
||||||
directory: process.cwd(),
|
prompts.intro("MCP OAuth Logout")
|
||||||
async fn() {
|
|
||||||
UI.empty()
|
|
||||||
prompts.intro("MCP OAuth Logout")
|
|
||||||
|
|
||||||
const credentials = await AppRuntime.runPromise(McpAuth.Service.use((auth) => auth.all()))
|
const credentials = yield* McpAuth.Service.use((auth) => auth.all())
|
||||||
const serverNames = Object.keys(credentials)
|
const serverNames = Object.keys(credentials)
|
||||||
|
|
||||||
if (serverNames.length === 0) {
|
if (serverNames.length === 0) {
|
||||||
prompts.log.warn("No MCP OAuth credentials stored")
|
prompts.log.warn("No MCP OAuth credentials stored")
|
||||||
prompts.outro("Done")
|
prompts.outro("Done")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let serverName = args.name
|
let serverName = args.name
|
||||||
if (!serverName) {
|
if (!serverName) {
|
||||||
const selected = await prompts.select({
|
const selected = yield* Effect.promise(() =>
|
||||||
message: "Select MCP server to logout",
|
prompts.select({
|
||||||
options: serverNames.map((name) => {
|
message: "Select MCP server to logout",
|
||||||
const entry = credentials[name]
|
options: serverNames.map((name) => {
|
||||||
const hasTokens = !!entry.tokens
|
const entry = credentials[name]
|
||||||
const hasClient = !!entry.clientInfo
|
const hasTokens = !!entry.tokens
|
||||||
let hint = ""
|
const hasClient = !!entry.clientInfo
|
||||||
if (hasTokens && hasClient) hint = "tokens + client"
|
let hint = ""
|
||||||
else if (hasTokens) hint = "tokens"
|
if (hasTokens && hasClient) hint = "tokens + client"
|
||||||
else if (hasClient) hint = "client registration"
|
else if (hasTokens) hint = "tokens"
|
||||||
return {
|
else if (hasClient) hint = "client registration"
|
||||||
label: name,
|
return {
|
||||||
value: name,
|
label: name,
|
||||||
hint,
|
value: name,
|
||||||
}
|
hint,
|
||||||
}),
|
}
|
||||||
})
|
}),
|
||||||
if (prompts.isCancel(selected)) throw new UI.CancelledError()
|
}),
|
||||||
serverName = selected
|
)
|
||||||
}
|
if (prompts.isCancel(selected)) throw new UI.CancelledError()
|
||||||
|
serverName = selected
|
||||||
|
}
|
||||||
|
|
||||||
if (!credentials[serverName]) {
|
if (!credentials[serverName]) {
|
||||||
prompts.log.error(`No credentials found for: ${serverName}`)
|
prompts.log.error(`No credentials found for: ${serverName}`)
|
||||||
prompts.outro("Done")
|
prompts.outro("Done")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await AppRuntime.runPromise(MCP.Service.use((mcp) => mcp.removeAuth(serverName)))
|
yield* MCP.Service.use((mcp) => mcp.removeAuth(serverName))
|
||||||
prompts.log.success(`Removed OAuth credentials for ${serverName}`)
|
prompts.log.success(`Removed OAuth credentials for ${serverName}`)
|
||||||
prompts.outro("Done")
|
prompts.outro("Done")
|
||||||
},
|
}),
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
async function resolveConfigPath(baseDir: string, global = false) {
|
async function resolveConfigPath(baseDir: string, global = false) {
|
||||||
|
|||||||
Reference in New Issue
Block a user