Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion hermes_cli/mcp_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ def cmd_mcp_add(args):
print(color(f" Connecting to '{name}'...", Colors.CYAN))

try:
tools = _probe_single_server(name, server_config)
tools = _probe_single_server(name, server_config, connect_timeout=600)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is cmd_mcp_add, not the hermes mcp login path described by the PR. Current main already grants interactive login at least 315 seconds in _reauth_oauth_server (hermes_cli/mcp_config.py:794-801); extending every add-time probe to 600 seconds changes unrelated discovery failures.

except Exception as exc:
_error(f"Failed to connect: {exc}")
if _confirm("Save config anyway (you can test later)?", default=False):
Expand Down
22 changes: 11 additions & 11 deletions tools/mcp_oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,19 +650,18 @@ async def _wait_for_callback() -> tuple[str, str | None]:
# We just need to poll for the result.
handler_cls, result = _make_callback_handler()

# Start a temporary server on the known port
# Start a temporary server on the known port. The MCP SDK may have
# already started its own callback server on the same port β€” if so,
# we just poll for the result without creating a second one.
server = None
try:
server = HTTPServer(("127.0.0.1", _oauth_port), handler_cls)
server_thread = threading.Thread(target=server.handle_request, daemon=True)
server_thread.start()
except OSError:
# Port already in use β€” the server from build_oauth_auth is running.
# Fall back to polling the server started by build_oauth_auth.
raise OAuthNonInteractiveError(
"OAuth callback timed out β€” could not bind callback port. "
"Complete the authorization in a browser first, then retry."
)

server_thread = threading.Thread(target=server.handle_request, daemon=True)
server_thread.start()
# Port already in use β€” the server from the MCP SDK is running.
# Fall back to polling: the SDK's server will handle the callback.
pass

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result was freshly created at line 651 and is only written by the handler returned from _make_callback_handler(). Since this fallback does not start that handler, a callback received by a different listener cannot update result; polling will run to the 300-second timeout. Please use a shared callback-result hand-off or retain a prompt bind failure.


# Optional paste-fallback thread: only on interactive TTYs. Reads one
# line from stdin and writes the parsed code/state into the shared
Expand Down Expand Up @@ -692,7 +691,8 @@ async def _wait_for_callback() -> tuple[str, str | None]:
await asyncio.sleep(poll_interval)
elapsed += poll_interval
finally:
server.server_close()
if server is not None:
server.server_close()

if result["error"] == _USER_SKIPPED_SENTINEL:
raise OAuthNonInteractiveError("user_skipped")
Expand Down