Skip to content

feat(mcp): add /healthz and /readyz probe endpoints - #1339

Open
yuzhiquan wants to merge 2 commits into
kubernetes-sigs:mainfrom
yuzhiquan:mcp-health-probes
Open

feat(mcp): add /healthz and /readyz probe endpoints#1339
yuzhiquan wants to merge 2 commits into
kubernetes-sigs:mainfrom
yuzhiquan:mcp-health-probes

Conversation

@yuzhiquan

@yuzhiquan yuzhiquan commented Aug 5, 2026

Copy link
Copy Markdown
Member

What this PR does / why we need it:

The MCP server ships a Dockerfile that serves streamable HTTP on 0.0.0.0:8000, so it is
deployed as a long-running network service — but it exposes no probe endpoints. A Deployment
has no way to tell a wedged replica from a healthy one, or to keep traffic off one that cannot
reach the Kubernetes API.

Adds the two-endpoint split sandbox-router already uses (sandbox-router/server/server.go:41):

Endpoint Meaning
GET /healthz Liveness. 200 once the ASGI app routes the request. Never contacts the Kubernetes API.
GET /readyz Readiness. Lists SandboxClaims in a configurable probe_namespace. 503 while starting or when the API is unreachable.

Keeping liveness independent of the Kubernetes API is the deliberate part: wiring it to an
external dependency would turn a transient control-plane blip into a rolling restart of every
replica, which is worse than briefly serving no traffic. A test asserts /healthz makes no
Kubernetes call.

/readyz returns a generic reason string rather than the underlying exception, since the
endpoint is unauthenticated.

Two implementation notes for reviewers:

  • /readyz calls the public list_sandbox_claims, not _ensure_initialized. The latter only
    loads credentials and builds API objects without contacting the apiserver, so it would report
    ready while the cluster was unreachable. It is also private, and nothing else in the repo
    depends on SDK internals.
  • The client is stashed on the FastMCP server object. The lifespan's yielded mapping reaches
    tools via ctx.lifespan_context but is not published on the ASGI app, so routes read it as
    request.app.state.fastmcp_server.

httpx is now declared in the test extra — the probe tests drive the ASGI app with it, and it
was previously only available transitively through fastmcp.

Testing

Six new tests: /healthz 200, /healthz makes no Kubernetes call, /readyz ready,
/readyz 503 when the API fails (asserting the exception text is not leaked), /readyz
reports starting before the lifespan runs, and the configured namespace is honoured.
Suite 19 → 25. dev/tools/test-unit green across all suites locally.

Suggested probe config is documented in the README; because /readyz makes a real API call,
it recommends a modest periodSeconds rather than aggressive polling.

Which issue(s) this PR is related to:

Follow-up hardening for the MCP server added in #1141

Release Note

Added `/healthz` and `/readyz` endpoints to the Agent Sandbox MCP server for Kubernetes liveness and readiness probes.

Summary by CodeRabbit

  • New Features
    • Added /healthz liveness and /readyz readiness endpoints.
    • Readiness checks server startup status and Kubernetes API availability.
    • Added configurable namespace selection for readiness checks.
  • Documentation
    • Documented health endpoints, response behavior, and recommended Kubernetes probe configuration.
  • Tests
    • Added coverage for successful, unavailable, and not-yet-started readiness states.
    • Verified liveness checks avoid backend access and responses exclude internal error details.

The server ships a Dockerfile that serves streamable HTTP on
0.0.0.0:8000, so it is deployed as a long-running network service, but
it exposed no probe endpoints. A Deployment therefore had no way to
tell a wedged replica from a healthy one, or to keep traffic away from
one that cannot reach the Kubernetes API.

Add the two-endpoint split sandbox-router already uses
(sandbox-router/server/server.go):

- /healthz -- liveness. Returns 200 as soon as the ASGI app routes the
  request, and never contacts the Kubernetes API. Wiring liveness to an
  external dependency would turn a transient control-plane blip into a
  rolling restart of every replica, which is worse than briefly serving
  no traffic. A test asserts it makes no Kubernetes call.
- /readyz -- readiness. Lists SandboxClaims in a configurable
  probe_namespace, which exercises everything a tool call needs:
  credential loading, client construction and one real apiserver
  request. Returns 503 while starting and when the API is unreachable,
  with a generic reason so the underlying exception is not exposed to an
  unauthenticated caller.

Two implementation notes. The readiness check deliberately calls the
public list_sandbox_claims rather than the private _ensure_initialized:
the latter only loads credentials without reaching the apiserver, and
nothing else in the repo depends on SDK internals. And the lifespan's
yielded mapping reaches tools through ctx.lifespan_context but is not
published on the ASGI app, so the client is stashed on the FastMCP
server object, which routes reach as request.app.state.fastmcp_server.

httpx is now declared in the test extra. The probe tests drive the ASGI
app with it, and it was only available transitively through fastmcp.
@github-project-automation github-project-automation Bot moved this to Backlog in Agent Sandbox Aug 5, 2026
@kubernetes-prow
kubernetes-prow Bot requested review from igooch and vicentefb August 5, 2026 08:24
@kubernetes-prow kubernetes-prow Bot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Aug 5, 2026
@netlify

netlify Bot commented Aug 5, 2026

Copy link
Copy Markdown

Deploy Preview for agent-sandbox canceled.

Name Link
🔨 Latest commit a1575d1
🔍 Latest deploy log https://app.netlify.com/projects/agent-sandbox/deploys/6a730462364e5300085a327a

@kubernetes-prow kubernetes-prow Bot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Aug 5, 2026
@kubernetes-prow

Copy link
Copy Markdown

Hi @yuzhiquan. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@kubernetes-prow kubernetes-prow Bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are limited based on label configuration.

🚫 Review skipped — only excluded labels are configured. (3)
  • needs-ok-to-test
  • do-not-merge/work-in-progress
  • cncf-cla: no

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9d1469f1-0448-487e-9522-61e2d4ece2bf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The MCP server now exposes /healthz and /readyz. Liveness is dependency-free. Readiness checks initialized state and namespace-scoped Kubernetes access. Tests and documentation cover the new behavior.

Changes

MCP server health probes

Layer / File(s) Summary
Probe configuration and route wiring
clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/settings.py, clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/server.py
Adds Settings.probe_namespace, stores initialized probe state, and registers the /healthz and /readyz routes.
Liveness and readiness handlers
clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/probes.py
Adds dependency-free liveness handling and readiness checks for startup state and namespace-scoped Kubernetes API access.
Probe tests and documentation
clients/integrations/mcp-server/tests/unit/test_probes.py, clients/integrations/mcp-server/pyproject.toml, clients/integrations/mcp-server/README.md
Tests successful and failed probe responses, startup state, namespace selection, and Kubernetes access isolation. Documents endpoint behavior and adds the direct httpx test dependency.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ProbeRequest
  participant MCPServer
  participant AsyncSandboxClient
  participant KubernetesAPI
  ProbeRequest->>MCPServer: GET /healthz or GET /readyz
  MCPServer->>MCPServer: Return liveness status
  MCPServer->>AsyncSandboxClient: List SandboxClaims in probe_namespace
  AsyncSandboxClient->>KubernetesAPI: Namespace-scoped SandboxClaim list
  KubernetesAPI-->>AsyncSandboxClient: API response or failure
  AsyncSandboxClient-->>MCPServer: Readiness result
  MCPServer-->>ProbeRequest: HTTP 200 or HTTP 503 response
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely identifies the addition of the /healthz and /readyz probe endpoints.
Description check ✅ Passed The description explains the purpose, implementation, testing, related work, and release note for the probe endpoints.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds Kubernetes-friendly liveness/readiness probe endpoints to the MCP server so it can be safely run as a long-lived HTTP service behind a Deployment/Service, with readiness depending on Kubernetes API reachability while liveness remains dependency-free.

Changes:

  • Introduces GET /healthz (liveness) and GET /readyz (readiness) routes on the MCP server ASGI app.
  • Adds probe_namespace setting to control which namespace /readyz queries.
  • Adds probe-focused unit tests and declares httpx in the test extra to drive ASGI requests.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
clients/integrations/mcp-server/tests/unit/test_probes.py Adds unit tests covering health/ready behavior, startup state, and namespace selection.
clients/integrations/mcp-server/README.md Documents probe endpoints and recommended Kubernetes probe configuration.
clients/integrations/mcp-server/pyproject.toml Adds httpx to the test optional dependency set for ASGI probe testing.
clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/settings.py Adds probe_namespace setting (env-backed) for readiness checks.
clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/server.py Wires probe routes and stashes client/settings for route access.
clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/probes.py Implements /healthz and /readyz endpoints.

Comment thread clients/integrations/mcp-server/README.md Outdated
The README said /readyz returns 503 with {"ready": false}, but the
handler also returns a "reason" field. Document the real shape,
including both reason values, and note that the reason is generic on
purpose because the endpoint is unauthenticated.

Also assert the full body in the failure test rather than just the
"ready" key, so the documented contract cannot drift again silently --
it already had.
@yuzhiquan

Copy link
Copy Markdown
Member Author

Thanks — addressed in a1575d1, applied locally (not via "Commit suggestion").

README//readyz mismatch — correct, fixed. The README now documents the real body ({"ready": false, "reason": "..."}), lists both reason values, and notes the reason is generic on purpose since the endpoint is unauthenticated. The failure test now asserts the whole body rather than just the ready key, so this can't drift silently again.

except Exception catching CancelledError — not applicable, skipping. asyncio.CancelledError has inherited from BaseException, not Exception, since Python 3.8, so except Exception cannot catch it on any version this package supports (requires-python = ">=3.10"):

py3.11.12  issubclass(asyncio.CancelledError, Exception) = False   base=BaseException
py3.12.8   issubclass(asyncio.CancelledError, Exception) = False   base=BaseException
py3.13.9   issubclass(asyncio.CancelledError, Exception) = False   base=BaseException

Cancellation already propagates. Happy to add an explicit re-raise if you'd still prefer it as documentation of intent, but it would be a no-op.

@yuzhiquan

Copy link
Copy Markdown
Member Author

/ok-to-test

@kubernetes-prow kubernetes-prow Bot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Aug 5, 2026
@kubernetes-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: aditya-shantanu, yuzhiquan
Once this PR has been reviewed and has the lgtm label, please assign igooch for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@aditya-shantanu

Copy link
Copy Markdown
Collaborator

/lgtm

@kubernetes-prow kubernetes-prow Bot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. ready-for-review size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

4 participants