Rcv prompt updates v3 - #403
Conversation
dustin-sale
left a comment
There was a problem hiding this comment.
Review by @dustin-sale via Codex.
Requested changes
server.py:4340— [P1] Do not expose profile credentials through unauthenticated HTTP.server.py:802— [P1] Preserve the Recovery invocation authorization scope.server.py:799— [P1] Require a valid public OAuth base URL.server.py:527— [P1] Use the shared caller-scoped authentication path.
Additional review notes
test_legacy_recovery_tools.py:357— [P2] Assert the exact user agent for every auth path.CHANGELOG.md:3— [P2] Document the 3.0.0 breaking changes.
Validation
make lint, scoped tests, lock validation, and package build passed. Tests reported 132 passed, 9 xfailed, and 90.01% coverage; git diff --check found four whitespace errors.
See the inline comments in this review for evidence, impact, and suggested remediation.
| oauth_port = int(port or "8000") | ||
| logger.info("Running FastMCP over streamable HTTP with OCI IAM OAuth at http://%s:%s", oauth_host, oauth_port) | ||
| mcp.run(transport="http", host=oauth_host, port=oauth_port) | ||
| elif host and port: |
There was a problem hiding this comment.
Comment from @dustin-sale via Codex.
[P1] Do not expose profile credentials through unauthenticated HTTP
Evidence: _build_auth_provider() returns None for session and apikey, but this branch starts an HTTP listener for those modes. A session-mode probe confirms mcp.auth is None; the prior implementation instead rejected HTTP startup without IDCS configuration.
Impact: any caller able to reach the configured bind address can invoke the server's OCI tools using the operator's API-key or session-token credentials.
Requested change: disallow profile-backed HTTP or attach an authentication provider before listening, and restore a regression test proving HTTP cannot start unauthenticated.
| base_url = _first_env("ORACLE_MCP_BASE_URL", "MCP_BASE_URL", default="http://localhost:8000") | ||
| storage_root = _first_env("ORACLE_MCP_OAUTH_STORAGE_DIR", default=_default_oauth_storage_root()) | ||
| redirect_path = _first_env("ORACLE_MCP_OAUTH_REDIRECT_PATH", default="/auth/callback") | ||
| scopes = (_first_env("ORACLE_MCP_OAUTH_SCOPES", default="openid offline_access") or "").split() |
There was a problem hiding this comment.
Comment from @dustin-sale via Codex.
[P1] Preserve the Recovery invocation authorization scope
Evidence: the hosted OAuth default was openid profile email oci_mcp.recovery.invoke; it is now only openid offline_access.
Impact: the server no longer requires the Recovery-specific authorization scope, so an authenticated identity can reach Recovery tools without the invocation entitlement previously enforced by this server.
Requested change: retain oci_mcp.recovery.invoke or an explicitly documented equivalent, preserve compatible scope configuration, and add a negative test for a token missing that scope.
| return None | ||
|
|
||
| registry = _get_registry() | ||
| base_url = _first_env("ORACLE_MCP_BASE_URL", "MCP_BASE_URL", default="http://localhost:8000") |
There was a problem hiding this comment.
Comment from @dustin-sale via Codex.
[P1] Require a public OAuth base URL for hosted mode
Evidence: missing configuration silently becomes http://localhost:8000, while the new hosted OAuth README instructions require only the auth method and tenancy registry. Authorization metadata and per-tenant callback URLs are derived from this value.
Impact: a remote deployment following the documentation advertises localhost/plain-HTTP authorization and callback URLs, so clients redirect to the wrong machine and login cannot complete correctly.
Requested change: fail fast unless hosted mode has an explicit validated HTTPS public base URL. Keep localhost only behind an explicit local-development mode, and document the per-tenant callback paths.
| _ENV_IDCS_CLIENT_ID = ("ORACLE_MCP_IDCS_CLIENT_ID", "IDCS_CLIENT_ID") | ||
| _ENV_IDCS_CLIENT_SECRET = ("ORACLE_MCP_IDCS_CLIENT_SECRET", "IDCS_CLIENT_SECRET") | ||
|
|
||
| _oauth_signer_cache: dict[str, Any] = {} |
There was a problem hiding this comment.
Comment from @dustin-sale via Codex.
[P1] Use the repository's caller-scoped authentication path
Evidence: repository rules require oracle-mcp-common for credential resolution and IDCS request-token exchange and explicitly prohibit globally cached HTTP signers. This package does not declare that dependency, duplicates the authentication logic, and stores token-exchange signers in this process-wide cache; tests assert that the same signer is reused across requests.
Impact: shared validation and secret-handling fixes are bypassed, and mutable signer state outlives the request context that established the caller identity.
Requested change: use oracle-mcp-common>=0.1.0,<0.2.0 and its request-scoped auth contexts, extending the shared implementation for registry routing if necessary. Construct each HTTP signer and OCI client inside the current request and remove the global signer cache.
| ) | ||
| loaded = server._load_oci_config_for_server() | ||
| assert loaded["region"] == "PROFILE2" | ||
| assert loaded["additional_user_agent"].startswith("oci-recovery-mcp/") |
There was a problem hiding this comment.
Comment from @dustin-sale via Codex.
[P2] Assert the exact user agent on every authentication path
Evidence: this assertion accepts any version beginning with oci-recovery-mcp/, and the new API-key, security-token, and OAuth client-factory tests do not assert the exact additional_user_agent.
Impact: a stale version or a client path that omits telemetry can pass while violating the repository's OCI client-construction contract.
Requested change: assert the exact derived value oci-recovery-mcp/3.0.0 for every supported API-key, security-token, and HTTP/token-exchange client-construction path.
54843e9 to
5f3b79c
Compare
|
Addressed all the review comments. Thank you for reviewing this. |
dustin-sale
left a comment
There was a problem hiding this comment.
Review by @dustin-sale via Codex.
Requested changes
server.py:876— [P1] Route hosted HTTP authentication throughoracle-mcp-common.
Additional review notes
README.md:73— [P2] Remove unsupported profile-backed HTTP instructions.onboard_database_to_recovery_service.txt:186— [P3] Clear the remaining whitespace failures.
Validation
make lint, scoped tests (132 passed, 90.42% coverage), lock validation, package build, and GitHub CI passed. git diff --check found three whitespace errors.
See the inline comments in this review for evidence, impact, and suggested remediation.
| domain_kwargs["oci_domain_id"] = entry.idcs_domain.split(".")[0] | ||
|
|
||
| try: | ||
| signer = TokenExchangeSigner( |
There was a problem hiding this comment.
Comment from @dustin-sale via Codex.
[P1] Route hosted HTTP authentication through oracle-mcp-common
Evidence: profile authentication now uses build_auth_context(), but the hosted path still constructs TokenExchangeSigner directly here and builds MultiTenantOCIAuth/OCIProvider itself. Repository rules require provider configuration through build_idcs_http_auth(required_scopes) and request-scoped OCI credentials through context_for(access_token.token).
Impact: the hosted path continues to bypass shared authentication validation, secret-handling fixes, and lifecycle guarantees.
Requested change: construct a shared IDCS HTTP-auth object for each configured tenancy and use its context_for() result for caller-specific OCI clients. If registry routing is not supported yet, extend the shared implementation rather than duplicating the authentication path in this server.
| ``` | ||
|
|
||
| For HTTP transport, start the server with: | ||
| To run a local HTTP listener for `session` or `apikey` mode, set both values below. This listener uses the server's local OCI credentials, so expose it only on a trusted network. |
There was a problem hiding this comment.
Comment from @dustin-sale via Codex.
[P2] Remove unsupported profile-backed HTTP instructions
Evidence: this section says session and apikey can use a local HTTP listener, but main() now rejects any non-OAuth listener and the 3.0.0 changelog correctly describes those modes as stdio-only. The environment-variable table repeats the unsupported local-listener exception.
Impact: operators following the README receive a startup error and may believe an unsafe credential-exposure model is supported.
Requested change: document session/apikey as stdio-only, reserve ORACLE_MCP_HOST and ORACLE_MCP_PORT for OAuth mode, and update the matching _effective_auth_method() docstring.
| • DNS configuration; | ||
| • IAM policies; | ||
| • authentication configuration; | ||
| • database wallet/encryption requirements, only for on-premise database; |
There was a problem hiding this comment.
Comment from @dustin-sale via Codex.
[P3] Clear the remaining whitespace failures
Evidence: git diff --check reports trailing whitespace on this line and line 286, plus a new blank line at EOF in test_recovery_coverage.py.
Impact: the patch does not pass the repository's clean-diff validation.
Requested change: remove all three whitespace errors and rerun git diff --check.
Description
This change delivers Recovery MCP Server v3.0.0, expanded Recovery Service and Database Service read coverage, and guided operational workflows for Cloud Protect onboarding.
Fixes # (issue)
Type of change
Please delete options that are not relevant.
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
Added targeted unit tests covering:
Guidance-tool availability and input validation.
Recovery and Recovery Database tool behavior, including legacy coverage retained during test-suite restructuring.Multi-Tenant OAuth authentication and tenancy header routing.
Onboard testing :
Prompt
codex> "Using recovery service mcp server onboard database DB0729 with ip 100.102.44.215 to recovery service."
Final Result:
DB0729 is onboarded to Recovery Service.
The initial health is WARNING — Waiting for archive logs, which is expected immediately after onboarding; the first scheduled task is due at 13:20 UTC. Real-time redo remains disabled. I did not enable it because that is a separate configuration change and requires a
Checklist: