refactor: unwrap McpOAuthCallback namespace + self-reexport (#22943)
This commit is contained in:
@@ -56,25 +56,24 @@ interface PendingAuth {
|
|||||||
timeout: ReturnType<typeof setTimeout>
|
timeout: ReturnType<typeof setTimeout>
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace McpOAuthCallback {
|
let server: ReturnType<typeof createServer> | undefined
|
||||||
let server: ReturnType<typeof createServer> | undefined
|
const pendingAuths = new Map<string, PendingAuth>()
|
||||||
const pendingAuths = new Map<string, PendingAuth>()
|
// Reverse index: mcpName → oauthState, so cancelPending(mcpName) can
|
||||||
// Reverse index: mcpName → oauthState, so cancelPending(mcpName) can
|
// find the right entry in pendingAuths (which is keyed by oauthState).
|
||||||
// find the right entry in pendingAuths (which is keyed by oauthState).
|
const mcpNameToState = new Map<string, string>()
|
||||||
const mcpNameToState = new Map<string, string>()
|
|
||||||
|
|
||||||
const CALLBACK_TIMEOUT_MS = 5 * 60 * 1000 // 5 minutes
|
const CALLBACK_TIMEOUT_MS = 5 * 60 * 1000 // 5 minutes
|
||||||
|
|
||||||
function cleanupStateIndex(oauthState: string) {
|
function cleanupStateIndex(oauthState: string) {
|
||||||
for (const [name, state] of mcpNameToState) {
|
for (const [name, state] of mcpNameToState) {
|
||||||
if (state === oauthState) {
|
if (state === oauthState) {
|
||||||
mcpNameToState.delete(name)
|
mcpNameToState.delete(name)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRequest(req: import("http").IncomingMessage, res: import("http").ServerResponse) {
|
function handleRequest(req: import("http").IncomingMessage, res: import("http").ServerResponse) {
|
||||||
const url = new URL(req.url || "/", `http://localhost:${currentPort}`)
|
const url = new URL(req.url || "/", `http://localhost:${currentPort}`)
|
||||||
|
|
||||||
if (url.pathname !== currentPath) {
|
if (url.pathname !== currentPath) {
|
||||||
@@ -137,9 +136,9 @@ export namespace McpOAuthCallback {
|
|||||||
|
|
||||||
res.writeHead(200, { "Content-Type": "text/html" })
|
res.writeHead(200, { "Content-Type": "text/html" })
|
||||||
res.end(HTML_SUCCESS)
|
res.end(HTML_SUCCESS)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function ensureRunning(redirectUri?: string): Promise<void> {
|
export async function ensureRunning(redirectUri?: string): Promise<void> {
|
||||||
// Parse the redirect URI to get port and path (uses defaults if not provided)
|
// Parse the redirect URI to get port and path (uses defaults if not provided)
|
||||||
const { port, path } = parseRedirectUri(redirectUri)
|
const { port, path } = parseRedirectUri(redirectUri)
|
||||||
|
|
||||||
@@ -168,9 +167,9 @@ export namespace McpOAuthCallback {
|
|||||||
})
|
})
|
||||||
server!.on("error", reject)
|
server!.on("error", reject)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function waitForCallback(oauthState: string, mcpName?: string): Promise<string> {
|
export function waitForCallback(oauthState: string, mcpName?: string): Promise<string> {
|
||||||
if (mcpName) mcpNameToState.set(mcpName, oauthState)
|
if (mcpName) mcpNameToState.set(mcpName, oauthState)
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
@@ -183,9 +182,9 @@ export namespace McpOAuthCallback {
|
|||||||
|
|
||||||
pendingAuths.set(oauthState, { resolve, reject, timeout })
|
pendingAuths.set(oauthState, { resolve, reject, timeout })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cancelPending(mcpName: string): void {
|
export function cancelPending(mcpName: string): void {
|
||||||
// Look up the oauthState for this mcpName via the reverse index
|
// Look up the oauthState for this mcpName via the reverse index
|
||||||
const oauthState = mcpNameToState.get(mcpName)
|
const oauthState = mcpNameToState.get(mcpName)
|
||||||
const key = oauthState ?? mcpName
|
const key = oauthState ?? mcpName
|
||||||
@@ -196,9 +195,9 @@ export namespace McpOAuthCallback {
|
|||||||
mcpNameToState.delete(mcpName)
|
mcpNameToState.delete(mcpName)
|
||||||
pending.reject(new Error("Authorization cancelled"))
|
pending.reject(new Error("Authorization cancelled"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isPortInUse(port: number = OAUTH_CALLBACK_PORT): Promise<boolean> {
|
export async function isPortInUse(port: number = OAUTH_CALLBACK_PORT): Promise<boolean> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const socket = createConnection(port, "127.0.0.1")
|
const socket = createConnection(port, "127.0.0.1")
|
||||||
socket.on("connect", () => {
|
socket.on("connect", () => {
|
||||||
@@ -209,9 +208,9 @@ export namespace McpOAuthCallback {
|
|||||||
resolve(false)
|
resolve(false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stop(): Promise<void> {
|
export async function stop(): Promise<void> {
|
||||||
if (server) {
|
if (server) {
|
||||||
await new Promise<void>((resolve) => server!.close(() => resolve()))
|
await new Promise<void>((resolve) => server!.close(() => resolve()))
|
||||||
server = undefined
|
server = undefined
|
||||||
@@ -224,9 +223,10 @@ export namespace McpOAuthCallback {
|
|||||||
}
|
}
|
||||||
pendingAuths.clear()
|
pendingAuths.clear()
|
||||||
mcpNameToState.clear()
|
mcpNameToState.clear()
|
||||||
}
|
|
||||||
|
|
||||||
export function isRunning(): boolean {
|
|
||||||
return server !== undefined
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isRunning(): boolean {
|
||||||
|
return server !== undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export * as McpOAuthCallback from "./oauth-callback"
|
||||||
|
|||||||
Reference in New Issue
Block a user