Fixes#13845
## What changed
The MCP shared HTTP client (used to execute tool calls against the local
REST API) now includes `ZO_BASE_URI` in its base URL:
`http://localhost:{port}{base_uri}` instead of
`http://localhost:{port}`.
## Why
When `ZO_BASE_URI` is set (e.g. `/abc`), all HTTP routes are mounted
under that prefix. The MCP endpoint itself was reachable (`POST
/abc/api/default/mcp` → 200), but every tool execution failed: the
internal client sent requests to the unprefixed path and got a 404.
```
INFO rmcp_openapi::http_client: Final URL: http://localhost:5080/api/default/streams
INFO config::axum::middlewares::access_log: 127.0.0.1 "GET /api/default/streams HTTP/1.1" 404 ...
```
## Notes for reviewers
- `cfg.common.base_uri` is already normalized at config load to have no
trailing slash (empty when unset), so the empty-prefix behavior is
unchanged.
- The join semantics are safe for path-prefixed base URLs:
rmcp-openapi's `with_base_url` appends a trailing slash and `build_url`
strips the tool path's leading slash before `Url::join`, covered by the
crate's `test_build_url_with_base_url_containing_path`. So `/abc` +
`/api/{org}/streams` → `/abc/api/{org}/streams`.
- The other `localhost:5080` occurrence in `tools.rs` is a test-only
mock client; the OAuth protected-resource metadata handler already
includes `base_uri`.
- `cargo check -p openobserve-mcp` and `cargo test -p openobserve-mcp`
pass.
Design at: #13526
## Summary
Implements MCP specification revision **2026-07-28** (stateless
protocol) alongside the existing 2025-11-25 behavior on the same
endpoint, following the spec's dual-era server model: requests carrying
modern per-request `_meta` are served per 2026-07-28, while `initialize`
selects legacy semantics. Legacy clients are unaffected.
## Changes
- **Version negotiation** (`src/api/http/.../mcp/mod.rs`,
`src/mcp/src/protocol.rs`): the `MCP-Protocol-Version` header is now
validated against a supported-version set (`2026-07-28`, `2025-11-25`)
instead of strict equality — the old check would have rejected every
client that upgrades to the new revision. Unsupported versions (header
or body `_meta`) get `400` + JSON-RPC `-32022`
(`UnsupportedProtocolVersionError`) listing the supported versions so
clients can retry.
- **`server/discover`** (`src/mcp/src/protocol.rs`): new RPC required by
2026-07-28, returning `supportedVersions`, `capabilities`, server
identity in `_meta`, `instructions`, and `ttlMs`/`cacheScope` cache
fields.
- **Modern result decoration** (`src/mcp/src/protocol.rs`): requests
declaring `io.modelcontextprotocol/protocolVersion: 2026-07-28` in
`_meta` get `resultType: "complete"` and `_meta` server identity on
results, plus `ttlMs`/`cacheScope` on `tools/list`. Legacy responses
carry none of these fields — byte-for-byte unchanged.
- **Request metadata headers** (`src/api/http/.../mcp/mod.rs`): modern
requests must mirror body fields into `Mcp-Method` / `Mcp-Name` headers
(including the Base64 sentinel encoding for `Mcp-Name`); missing or
mismatched headers are rejected with `400` + JSON-RPC `-32020`
(`HeaderMismatch`), per the transport's server-validation rules.
- **HTTP status mapping**: unknown methods on modern requests return
`404` + `-32601` so dual-era clients can distinguish a modern server
from a legacy one; version errors return `400`. Legacy requests keep
errors in-band with `200` as before.
- **Legacy paths untouched**: `initialize`, `ping`, the GET SSE
keepalive channel, DELETE no-op, and notification → `202` handling are
unchanged.
Tool ordering was already deterministic (sorted at init), satisfying the
new caching guidance.
## Testing
- `cargo test -p openobserve-mcp` — 94 tests pass (new coverage:
`server/discover` shape, unsupported-version error payload, modern
decoration, legacy non-decoration, sentinel decoding, version set).
- `cargo test -p openobserve-api-http --lib handler::http::request::mcp`
— 13 tests pass (new coverage: modern happy path, missing/mismatched
`Mcp-Method`/`Mcp-Name`, header↔body version mismatch, missing version
header on modern requests, modern unknown method → 404, legacy header
accepted).
- `cargo clippy` clean on both crates.
## What
Adds **MCP OAuth 2.0 sign-in** support (RFC 9728 Protected Resource
Metadata) so MCP clients — `claude mcp add`, Cursor, etc. — can discover
the Dex authorization server and complete an OAuth flow against an
OpenObserve MCP endpoint. While wiring this up, a security review of the
shared auth path found **two authentication/authorization defects**,
both fixed here.
All changes are in OSS; the auth code paths are `#[cfg(feature =
"enterprise")]` (Dex JWT validation + OpenFGA authorization), so the
security impact is on the enterprise build. Tracking issue:
**openobserve/o2-enterprise#2232**.
## Why
- MCP clients had no way to discover the auth server, so OAuth sign-in
couldn't complete.
## Changes
### MCP OAuth (RFC 9728)
- `OAuthProtectedResourceMetadata` type + handler; public
`.well-known/oauth-protected-resource` routes registered before auth
nesting so discovery stays unauthenticated.
- `WWW-Authenticate: resource_metadata=...` advertised on MCP 401
(mcp-path only), pointing clients at the per-org protected-resource
metadata.
- Return 404 for protected-resource metadata when Dex is disabled (don't
advertise an unreachable auth server).
- Header-injection hardening: the org id reflected into the
`WWW-Authenticate` challenge is charset-allowlisted and **rejected**
(not escaped), so a crafted org id can't append a second challenge.
- MCP-path detection tightened (matches the org/`mcp` path segment, so a
stream literally named `mcp` doesn't false-positive) and base_uri
duplication fixed.
## Summary
- move the MCP protocol, tool discovery, schema simplification,
execution, and response filtering implementation into a new OSS
`openobserve-mcp` workspace crate
- enable the real MCP POST, GET, and DELETE handlers in community builds
- initialize the MCP tool catalog from the OpenAPI document for every
edition
- keep OAuth authorization-server discovery enterprise-only because it
depends on Dex
- add OSS handler and OpenAPI initialization coverage
Closes#13266
Design at: #13266
## Impact
Community users can connect MCP clients directly to OpenObserve and use
the OpenAPI-backed tool catalog. Enterprise builds continue to use the
same MCP implementation from the OSS workspace crate.
Companion PR openobserve/o2-enterprise#2206 removes the old private copy
and updates the enterprise workspace overlay.