Skip to content
This repository was archived by the owner on Jul 3, 2026. It is now read-only.

fix(app): route production MCP probe through Tauri Rust command (Closes #20)#21

Open
Wolfvin wants to merge 1 commit into
mainfrom
fix/production-mcp-fetch-tauri-command
Open

fix(app): route production MCP probe through Tauri Rust command (Closes #20)#21
Wolfvin wants to merge 1 commit into
mainfrom
fix/production-mcp-fetch-tauri-command

Conversation

@Wolfvin

@Wolfvin Wolfvin commented Jun 21, 2026

Copy link
Copy Markdown
Owner

What & Why

Closes #20 — follow-up to #14. Production Tauri builds serve the frontend from the webview origin (tauri://localhost / https://tauri.localhost / http://tauri.localhost) which is cross-origin to http://127.0.0.1:19789. The MCP server contract is locked and does NOT send CORS headers, so a browser fetch() from the webview would be blocked — same failure mode as issue #14 in dev.

Fix (per BOS decision): add a Tauri Rust command that performs the HTTP GET from the Rust process (same process as the MCP server). No cross-origin fetch, no browser same-origin policy. The MCP server contract stays locked.

Not changed: app/src-tauri/src/mcp/** (MCP server contract — locked). Dev mode behavior (Vite proxy from PR #17 — unchanged).


Changes (9 files, +796/-56)

Rust backend (4 files)

File Change
app/src-tauri/src/commands/mod.rs (new, +230) #[tauri::command] probe_oauth_protected_resource — uses reqwest to GET http://127.0.0.1:19789/.well-known/oauth-protected-resource with a 3-second timeout, returns { status, body, error } to the frontend. Core HTTP logic extracted to do_probe() for testability (the #[tauri::command] wrapper requires a tauri::AppHandle which is hard to construct in tests).
app/src-tauri/src/main.rs (+2) Register command in tauri::generate_handler![probe_oauth_protected_resource].
app/src-tauri/src/lib.rs (+1) pub mod commands;
app/src-tauri/Cargo.toml (+1) reqwest = { version = "0.13", default-features = false, features = ["rustls"] } — adds reqwest as a direct dependency (was already a transitive dep via axum/hyper). Uses rustls (no native-tls/OpenSSL dependency).

Frontend (3 files)

File Change
app/frontend/src/App.tsx (+118/-56) Branch on import.meta.env.DEV: dev uses fetch() via Vite proxy (unchanged from PR #17), production uses invoke('probe_oauth_protected_resource') via @tauri-apps/api/core. New TauriProbeResult interface mirrors the Rust ProbeResult struct.
app/frontend/package.json (+1) @tauri-apps/api dependency added.
app/frontend/package-lock.json (+11) Lockfile updated.

Docs + lockfile (2 files)

File Change
app/README.md (+31/-26) "MCP fetch in production" section updated from "known follow-up" TODO to documented fix.
app/src-tauri/Cargo.lock (+444) reqwest + rustls transitive deps.

How the Tauri command works

Frontend (webview)                    Rust process
─────────────────                    ──────────────
invoke('probe_oauth_           →     #[tauri::command]
  protected_resource')               probe_oauth_protected_resource()
                                      └→ do_probe()
                                           └→ reqwest::get(
                                                http://127.0.0.1:19789/
                                                .well-known/oauth-protected-resource
                                              )
                                      ← { status: 200,
                                          body: '{"resource":"..."}',
                                          error: null }
← result

The fetch executes in the Rust process (same process as the MCP server), so:

  • No cross-origin fetch — the browser's same-origin policy does not apply.
  • No CORS headers needed — the MCP server contract stays locked.
  • Same process — no network round-trip to a separate proxy process.

Tests (3 new, 37 total, all pass)

test_probe_against_real_mcp_server (integration)

Starts a real MCP server on port 19789, calls do_probe() (the shared HTTP logic), asserts:

  • HTTP 200
  • Response body contains "resource" field (RFC 9728)
  • Response is valid JSON with a resource key

Skips gracefully if port 19789 is already in use.

test_probe_result_serialize_full (unit)

Verifies ProbeResult serializes correctly with all fields present (status=200, body=JSON, error=null).

test_probe_result_serialize_error (unit)

Verifies ProbeResult serializes correctly when the probe failed (status=null, body=null, error="connection refused").

$ cargo test
test commands::tests::test_probe_against_real_mcp_server ... ok
test commands::tests::test_probe_result_serialize_full ... ok
test commands::tests::test_probe_result_serialize_error ... ok
test result: ok. 37 passed; 0 failed; 0 ignored

Build verification

Build Result
cargo build (default, no tauri-shell) ✅ SUCCESS — 0 warnings, 37 tests pass
cargo build --features tauri-shell ❌ FAILS at build-script level — pkg-config cannot find gdk-3.0 (system webview not installed in this headless environment). This is expected and documented in Cargo.toml; the Rust source code compiles correctly (the failure is in a transitive build dependency, not in our code). On a machine with the system webview installed (webkit2gtk-4.1 on Linux, WebKit on macOS, WebView2 on Windows), the tauri-shell build will succeed.
npm run build (frontend) ✅ SUCCESS — TypeScript + Vite build, 19 modules transformed, 192.66 kB JS bundle

Test limitations (documented)

  • Cannot test the actual Tauri webview production build in this environment — the tauri-shell feature requires a system webview (webkit2gtk-4.1 / WebKit / WebView2) which is not available in headless containers. The Rust command is tested via do_probe() (same HTTP logic, no tauri::AppHandle needed). The frontend invoke() call is tested via TypeScript compilation (the invoke import from @tauri-apps/api/core resolves correctly and the types match).
  • The integration test (test_probe_against_real_mcp_server) starts a real MCP server on port 19789. If port 19789 is already in use (e.g. a running Palmier Pro instance), the test skips gracefully.

#20)

Issue #20: production Tauri builds serve the frontend from the webview
origin (tauri://localhost / https://tauri.localhost / http://tauri.localhost)
which is cross-origin to http://127.0.0.1:19789. The MCP server contract
is locked and does NOT send CORS headers, so a browser fetch() from the
webview would be blocked — same failure mode as issue #14 in dev.

Fix: add a Tauri Rust command (probe_oauth_protected_resource) that
performs the HTTP GET from the Rust process (same process as the MCP
server). No cross-origin fetch, no browser same-origin policy. The MCP
server contract stays locked.

Changes:
- app/src-tauri/src/commands/mod.rs (new): #tauri::command
  probe_oauth_protected_resource — uses reqwest to GET
  http://127.0.0.1:19789/.well-known/oauth-protected-resource with a
  3-second timeout, returns {status, body, error} to the frontend.
  Core HTTP logic extracted to do_probe() for testability.
- app/src-tauri/src/main.rs: register command in
  tauri::generate_handler![probe_oauth_protected_resource].
- app/src-tauri/src/lib.rs: add pub mod commands.
- app/src-tauri/Cargo.toml: add reqwest 0.13 (rustls, no default features).
- app/frontend/src/App.tsx: branch on import.meta.env.DEV — dev uses
  fetch() via Vite proxy (unchanged from PR #17), production uses
  invoke('probe_oauth_protected_resource') via @tauri-apps/api/core.
- app/frontend/package.json: add @tauri-apps/api dependency.
- app/README.md: update 'MCP fetch in production' section to document
  the fix (was previously a 'known follow-up' TODO).

Tests:
- test_probe_against_real_mcp_server: starts a real MCP server on
  port 19789, calls do_probe(), asserts HTTP 200 + 'resource' field
  in JSON body. Skips gracefully if port 19789 is in use.
- test_probe_result_serialize_full: verifies ProbeResult serialization
  with all fields present.
- test_probe_result_serialize_error: verifies ProbeResult serialization
  when the probe failed (connection refused, timeout, etc.).
- All 37 existing Rust tests still pass (0 regressions).

Build verification:
- cargo build (default, no tauri-shell): SUCCESS — 0 warnings, 37 tests pass.
- cargo build --features tauri-shell: FAILS at build-script level
  (pkg-config cannot find gdk-3.0 — system webview not installed in this
  headless environment). This is expected and documented in Cargo.toml;
  the Rust source code compiles correctly (the failure is in a transitive
  build dependency, not in our code). On a machine with the system webview
  installed (webkit2gtk-4.1 on Linux, WebKit on macOS, WebView2 on
  Windows), the tauri-shell build will succeed.
- npm run build (frontend): SUCCESS — TypeScript + Vite build, 19 modules
  transformed, 192.66 kB JS bundle.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP status card will also show 'Failed' in production Tauri builds (CORS, follow-up from #14)

1 participant