|
1 | 1 | import { createConnection } from "net" |
2 | 2 | import { createServer } from "http" |
3 | 3 | import { Log } from "../util/log" |
4 | | -import { OAUTH_CALLBACK_PORT, OAUTH_CALLBACK_PATH } from "./oauth-provider" |
| 4 | +import { OAUTH_CALLBACK_PORT, OAUTH_CALLBACK_PATH, parseRedirectUri } from "./oauth-provider" |
5 | 5 |
|
6 | 6 | const log = Log.create({ service: "mcp.oauth-callback" }) |
7 | 7 |
|
| 8 | +// Current callback server configuration (may differ from defaults if custom redirectUri is used) |
| 9 | +let currentPort = OAUTH_CALLBACK_PORT |
| 10 | +let currentPath = OAUTH_CALLBACK_PATH |
| 11 | + |
8 | 12 | const HTML_SUCCESS = `<!DOCTYPE html> |
9 | 13 | <html> |
10 | 14 | <head> |
@@ -71,9 +75,9 @@ export namespace McpOAuthCallback { |
71 | 75 | } |
72 | 76 |
|
73 | 77 | function handleRequest(req: import("http").IncomingMessage, res: import("http").ServerResponse) { |
74 | | - const url = new URL(req.url || "/", `http://localhost:${OAUTH_CALLBACK_PORT}`) |
| 78 | + const url = new URL(req.url || "/", `http://localhost:${currentPort}`) |
75 | 79 |
|
76 | | - if (url.pathname !== OAUTH_CALLBACK_PATH) { |
| 80 | + if (url.pathname !== currentPath) { |
77 | 81 | res.writeHead(404) |
78 | 82 | res.end("Not found") |
79 | 83 | return |
@@ -135,19 +139,31 @@ export namespace McpOAuthCallback { |
135 | 139 | res.end(HTML_SUCCESS) |
136 | 140 | } |
137 | 141 |
|
138 | | - export async function ensureRunning(): Promise<void> { |
| 142 | + export async function ensureRunning(redirectUri?: string): Promise<void> { |
| 143 | + // Parse the redirect URI to get port and path (uses defaults if not provided) |
| 144 | + const { port, path } = parseRedirectUri(redirectUri) |
| 145 | + |
| 146 | + // If server is running on a different port/path, stop it first |
| 147 | + if (server && (currentPort !== port || currentPath !== path)) { |
| 148 | + log.info("stopping oauth callback server to reconfigure", { oldPort: currentPort, newPort: port }) |
| 149 | + await stop() |
| 150 | + } |
| 151 | + |
139 | 152 | if (server) return |
140 | 153 |
|
141 | | - const running = await isPortInUse() |
| 154 | + const running = await isPortInUse(port) |
142 | 155 | if (running) { |
143 | | - log.info("oauth callback server already running on another instance", { port: OAUTH_CALLBACK_PORT }) |
| 156 | + log.info("oauth callback server already running on another instance", { port }) |
144 | 157 | return |
145 | 158 | } |
146 | 159 |
|
| 160 | + currentPort = port |
| 161 | + currentPath = path |
| 162 | + |
147 | 163 | server = createServer(handleRequest) |
148 | 164 | await new Promise<void>((resolve, reject) => { |
149 | | - server!.listen(OAUTH_CALLBACK_PORT, () => { |
150 | | - log.info("oauth callback server started", { port: OAUTH_CALLBACK_PORT }) |
| 165 | + server!.listen(currentPort, () => { |
| 166 | + log.info("oauth callback server started", { port: currentPort, path: currentPath }) |
151 | 167 | resolve() |
152 | 168 | }) |
153 | 169 | server!.on("error", reject) |
@@ -182,9 +198,9 @@ export namespace McpOAuthCallback { |
182 | 198 | } |
183 | 199 | } |
184 | 200 |
|
185 | | - export async function isPortInUse(): Promise<boolean> { |
| 201 | + export async function isPortInUse(port: number = OAUTH_CALLBACK_PORT): Promise<boolean> { |
186 | 202 | return new Promise((resolve) => { |
187 | | - const socket = createConnection(OAUTH_CALLBACK_PORT, "127.0.0.1") |
| 203 | + const socket = createConnection(port, "127.0.0.1") |
188 | 204 | socket.on("connect", () => { |
189 | 205 | socket.destroy() |
190 | 206 | resolve(true) |
|
0 commit comments