Skip to content

fix(mobile): normalize root-path remote MCP URLs - #8329

Merged
iscekic merged 3 commits into
mainfrom
drain/remote-mcp-root-url-slash
Oct 21, 2026
Merged

iscekic merged 3 commits into
mainfrom
drain/remote-mcp-root-url-slash

Conversation

@iscekic

@iscekic iscekic commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

What

normalizeRemoteMcpUrl in apps/mobile/src/lib/chat/remote-mcp-url.ts promises that a trailing slash on the path is dropped so two spellings of one endpoint are one server, but it skipped the strip when url.pathname === '/'. A root endpoint kept its slash, so remote-mcp-store.ts treated https://host and https://host/ as two servers, and the same split happened with a query (https://host?a=1 vs https://host/?a=1).

The endpoint now always has its trailing slashes removed; the replace is a no-op when there are none. Query handling, error messages, and the HTTPS/localhost policy are unchanged. The doc comment states the rule directly instead of describing it as mirroring the extension's copy, which keeps its own normalizer.

Persisted bytes are not rewritten. The server store already normalizes a stored URL when it loads, so a row saved as https://host/ now reads back as https://host, and two rows that differ only by the root slash load as one server, as rows differing by a path slash already did. To keep a server that is already signed in from asking for a sign-in again, remote-mcp-oauth-store.ts now compares its saved URL with the server URL through the same normalizer: an entry written with the older spelling still matches its endpoint, and an entry written for another endpoint still reads nothing.

Tests

  • apps/mobile/src/lib/chat/remote-mcp-url.test.ts: root-path cases — bare host vs host plus slash, repeated slashes, localhost with a port, and both spellings with a query.
  • apps/mobile/src/lib/chat/remote-mcp-oauth-store.test.ts: a state entry saved under the older root spelling is read for the normalized URL, survives a later field write, and a different endpoint still reads nothing.
  • apps/mobile/src/lib/chat/remote-mcp-store.test.ts: two persisted rows naming one root endpoint load as one server with its credentials.

Before and after:

  • The two new URL cases fail on the first commit's parent: expected 'https://remote.example/' to be 'https://remote.example' and expected 'https://remote.example/?base=/' to be 'https://remote.example?base=/'.
  • The two endpoint-matching cases fail without the OAuth store change (Tests 2 failed | 4 passed).
  • With both changes the src/lib/chat and src/components/chat pure suites pass 592/592.

End-to-end on Android

Local dev stack and the dev-client build on the emulator, signed in with a local fake account:

  • Added a remote MCP server from a chat's Chat tools sheet with the URL http://localhost:8815/. The saved row reads http://localhost:8815: the trailing slash was dropped, where before the change the row kept it.
  • Adding that same endpoint again under a second name (http://localhost:8815/) left the add sheet open and added no row, so both spellings reach the one stored server.
  • Control: saving a distinct URL (http://localhost:8815/ctl) closed the sheet and added its row, so the refusal above is endpoint equality rather than a stuck Save.

The OAuth matching change is covered by the unit tests above; no local OAuth MCP server was stood up, so it has no in-app capture.

Screenshots are attached in a follow-up comment.

Proxy answers

The owner's proxy gave these answers in the owner's place.

  1. Question: Scope: The identical root-path bug exists in apps/extension/src/shared/remote-mcp-url.ts:35, and the mobile doc comment claims it mirrors the extension's rules. Fix only mobile (finding scope) or both?
    Answer: Mobile only
    Confidence: 0.55 · Precedent: Product scope: "explorer loop is strictly for the mobile app" (session 55c9c80332a7). Git: separate concern in its own PR (session debabfb38c82). The extension may be an end-of-life surface, which outweighs fixing the class everywhere. Reword the doc comment to state the rule itself.
  2. Question: Stored servers: Servers already saved with a trailing-slash root URL stay as-is after this change. Rewrite stored entries on load, or leave saved data untouched?
    Answer: Leave stored data untouched
    Confidence: 0.70 · Precedent: Speed and least work: "do the least amount of work possible, with no drop in quality" (session 7f3470e0ba6f). Rewriting persisted user data is outside the work item.

normalizeRemoteMcpUrl documented that a trailing slash on the path is
dropped so two spellings of one endpoint are one server, but it skipped
the strip when the pathname was the root. `https://host` and
`https://host/` therefore stayed two servers, and the same split
appeared with a query (`https://host?a=1` vs `https://host/?a=1`), so the
remote server store accepted the same endpoint twice.

Strip trailing slashes from the endpoint unconditionally; the replace is
a no-op when there is none. Query handling is unchanged.
@iscekic iscekic added drain Work item from the findings drain janitor:features-workflows Janitor finding family: features-workflows labels Oct 20, 2026
@iscekic

iscekic commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator Author

bot: End-to-end on the Android emulator, local dev stack, local fake account.

  1. The server saved from the URL http://localhost:8815/ is listed as http://localhost:8815: the root-path trailing slash is dropped.
  2. The add sheet with that same endpoint typed as http://localhost:8815/.
  3. After Save on the duplicate spelling the sheet stays open and no row is added, so both spellings reach the one stored server. A distinct URL (http://localhost:8815/ctl) saved and closed the sheet in the same session, so the refusal is endpoint equality, not a stuck Save.

Chat tools sheet with the saved server row showing the normalized root URL

Add MCP server sheet with the root endpoint typed with a trailing slash

Add MCP server sheet still open after saving the other spelling of the same endpoint

@kilo-code-bot

kilo-code-bot Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The only change since the previous review renames the OAuth-store test case to match its assertions, resolving the outstanding suggestion; the endpoint normalization and OAuth matching code are unchanged and remain correct.

Files Reviewed (1 file)
  • apps/mobile/src/lib/chat/remote-mcp-oauth-store.test.ts
Previous Review Summaries (2 snapshots, latest commit a9f4d0f)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit a9f4d0f)

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

The OAuth endpoint-matching change resolves the prior warning about saved credentials; the only new finding is a test name that contradicts its own assertion.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
apps/mobile/src/lib/chat/remote-mcp-oauth-store.test.ts 112 Test named "keeps the older spelling" asserts url: 'https://demo.example', i.e. the older spelling is rewritten, not kept.
Files Reviewed (3 files)
  • apps/mobile/src/lib/chat/remote-mcp-oauth-store.test.ts - 1 issue
  • apps/mobile/src/lib/chat/remote-mcp-oauth-store.ts - 0 issues
  • apps/mobile/src/lib/chat/remote-mcp-store.test.ts - 0 issues

The previously reported OAuth-mismatch warning is resolved by sameEndpoint in remote-mcp-oauth-store.ts, which compares the saved and current URLs through normalizeRemoteMcpUrl and falls back to exact equality when normalization throws; the new store test also pins the root-slash dedupe as intended behavior.

Previous review (commit d75ff93)

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

Changing the normalizer also re-normalizes already-persisted URLs on load, which invalidates saved OAuth state and silently drops duplicate root-slash rows for existing installs.

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/mobile/src/lib/chat/remote-mcp-url.ts 47 parseRemoteMcpServers normalizes persisted URLs on load, so a stored https://host/ becomes https://host; the saved OAuth entry (matched by exact URL in remote-mcp-oauth-store.ts:82) no longer matches and the user must re-authenticate, and a second stored row differing only by the root slash is dropped by the dedupe at remote-mcp-store.ts:138. The PR body states stored servers are untouched, which does not hold for in-memory load.
Files Reviewed (2 files)
  • apps/mobile/src/lib/chat/remote-mcp-url.ts - 1 issue
  • apps/mobile/src/lib/chat/remote-mcp-url.test.ts - 0 issues

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash-1017 · Input: 31.6K · Output: 1.2K · Cached: 138K

Review guidance: REVIEW.md from base branch main

The server store normalizes every persisted URL when it loads, and it now
drops the trailing slash of a root path. A state entry written before that
change carries the older spelling, so an exact string match stopped
finding it and the person was asked to sign in again for a server whose
endpoint had not changed.

Compare the saved URL and the server URL through the same normalizer.
Entries for other endpoints still read nothing, so a token stays on the
server it was issued for, and a server with no parseable URL compares as
written.
The case checks that a field written for the normalized URL lands in the
entry an older spelling saved, which rewrites the entry's URL; the old
name said the older spelling was kept.
@iscekic iscekic added the merge:human Substantive functional risk requires human review. label Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

drain Work item from the findings drain janitor:features-workflows Janitor finding family: features-workflows merge:human Substantive functional risk requires human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants