Skip to content

feat(mcp): add list_files and file_exists tools - #1329

Open
yuzhiquan wants to merge 2 commits into
kubernetes-sigs:mainfrom
yuzhiquan:mcp-list-files-exists
Open

feat(mcp): add list_files and file_exists tools#1329
yuzhiquan wants to merge 2 commits into
kubernetes-sigs:mainfrom
yuzhiquan:mcp-list-files-exists

Conversation

@yuzhiquan

@yuzhiquan yuzhiquan commented Aug 4, 2026

Copy link
Copy Markdown

What this PR does / why we need it:

The MCP server exposes upload_file and download_file, but there is no way to
list a directory or test whether a path exists. An agent can write a file and
read it back, yet cannot discover what is in a directory, and can only probe for
a path by attempting a read and interpreting the failure.

Both operations already exist on the Python SDK's filesystem interface —
AsyncFilesystem.list() and .exists() in
clients/python/agentic-sandbox-client/k8s_agent_sandbox/files/async_filesystem.py
— and both are already exposed by the Go SDK (Sandbox.List, Sandbox.Exists).
So this is a cross-SDK parity gap rather than new functionality: the
capability is present and simply unexposed over MCP.

This PR adds two thin tools over those calls, following download_file.py line
for line:

  • list_files — returns each entry's name, size, type
    (file/directory) and mod_time (POSIX timestamp), mapped from the SDK's
    FileEntry. An empty directory returns an empty list, not an error.
  • file_exists — returns a boolean. A missing path is a successful
    exists: false answer rather than a failure, which is a more useful contract
    for an LLM caller than an exception.

Notes for reviewers:

  • Session isolation is preserved and now asserted. Both tools go through the
    existing get_sandbox() helper, so they inherit the per-session ownership
    check unchanged — no new authorization path. Each tool's
    test_session_id_not_found additionally asserts the sandbox is never
    touched when that check rejects, so the tools provably fail closed.
  • Timeouts reuse TOOL_DEFAULT_TIMEOUT / TOOL_MAX_TIMEOUT, so bounds and
    validation match the other file tools.
  • Purely additive: no existing tool, schema, or behaviour is modified.
  • README.md documents both tools in the established per-tool format.

Which issue(s) this PR is related to:

Follow-up to the MCP server added in #1141

Release Note

Added `list_files` and `file_exists` tools to the Agent Sandbox MCP server, bringing its filesystem tool surface in line with the Go SDK.

Summary by CodeRabbit

  • New Features

    • Added a tool to list sandbox directory contents, including names, sizes, types, and modification times.
    • Added a tool to check whether a file or directory exists.
    • Documented both tools and their handling of missing paths.
  • Tests

    • Added coverage for successful operations, empty or missing paths, validation, timeouts, and error handling.

The MCP server exposed read/write/exec but not directory listing or
existence checks, although both already exist on the Python SDK's
filesystem interface (AsyncFilesystem.list/.exists) and both are
exposed by the Go SDK. An agent could therefore write and read a file
but could not discover what was in a directory, or test for a path
without provoking a read error.

Add two tools wrapping the existing SDK calls:

- list_files: returns each entry's name, size, type (file/directory)
  and mod_time. An empty directory is an empty list, not an error.
- file_exists: returns a boolean. A missing path is a successful
  "exists: false" answer rather than a failure.

Both go through get_sandbox(), so they inherit the per-session
ownership check unchanged; the tests assert the sandbox is never
touched when that check rejects, so the tools fail closed. Timeouts
reuse TOOL_DEFAULT_TIMEOUT/TOOL_MAX_TIMEOUT for consistency with the
other file tools.
@netlify

netlify Bot commented Aug 4, 2026

Copy link
Copy Markdown

Deploy Preview for agent-sandbox canceled.

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

@kubernetes-prow kubernetes-prow Bot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Aug 4, 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 requested a review from SHRUTI6991 August 4, 2026 08:39
@kubernetes-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: yuzhiquan
Once this PR has been reviewed and has the lgtm label, please assign shruti6991 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

@kubernetes-prow
kubernetes-prow Bot requested a review from vicentefb August 4, 2026 08:39
@kubernetes-prow kubernetes-prow Bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Aug 4, 2026
@coderabbitai

coderabbitai Bot commented Aug 4, 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: f2c24231-d487-4aac-a2c9-13cb5b113780

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 adds list_files and file_exists tools. Both validate inputs, access a sandbox filesystem, return typed results, handle failures, and include unit tests and documentation.

Changes

Filesystem MCP tools

Layer / File(s) Summary
Path existence tool
clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/tools/file_exists.py, clients/integrations/mcp-server/tests/unit/test_tools/test_file_exists.py
Adds validated sandbox path checks with boolean output, timeout forwarding, error handling, and tests for present, absent, invalid, and missing-sandbox cases.
Directory listing tool
clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/tools/list_files.py, clients/integrations/mcp-server/tests/unit/test_tools/test_list_files.py
Adds validated directory listing with entry metadata, timeout forwarding, error handling, and tests for populated, empty, invalid, and missing-sandbox cases.
MCP registration and documentation
clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/tools/__init__.py, clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/server.py, clients/integrations/mcp-server/README.md
Exports and registers both tools and documents their parameters and responses.

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

Sequence Diagram(s)

sequenceDiagram
  participant MCP Client
  participant MCP Server
  participant Sandbox
  participant Sandbox Filesystem API
  MCP Client->>MCP Server: invoke list_files or file_exists
  MCP Server->>Sandbox: retrieve sandbox
  Sandbox->>Sandbox Filesystem API: list directory or check path
  Sandbox Filesystem API-->>MCP Server: entries or exists boolean
  MCP Server-->>MCP Client: typed tool response
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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 two MCP tools added by the pull request.
Description check ✅ Passed The description includes all required sections and clearly explains the changes, rationale, related work, and release note.
✨ 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.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
clients/integrations/mcp-server/tests/unit/test_tools/test_file_exists.py (1)

71-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test both timeout rejection boundaries.

Both tools enforce gt=0 and le=TOOL_MAX_TIMEOUT, but the tests do not cover the full contract.

  • clients/integrations/mcp-server/tests/unit/test_tools/test_file_exists.py#L71-L94: Add MCP-level tests for timeout=0 and timeout=TOOL_MAX_TIMEOUT + 1. Assert that mock_sandbox.files.exists is not called.
  • clients/integrations/mcp-server/tests/unit/test_tools/test_list_files.py#L136-L153: Add a test for timeout=TOOL_MAX_TIMEOUT + 1. Assert that mock_sandbox.files.list is not called.

As per coding guidelines, focus findings on substantive issues, including missing tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@clients/integrations/mcp-server/tests/unit/test_tools/test_file_exists.py`
around lines 71 - 94, Add MCP-level timeout boundary tests in
clients/integrations/mcp-server/tests/unit/test_tools/test_file_exists.py:71-94
for timeout=0 and timeout=TOOL_MAX_TIMEOUT + 1, asserting validation rejects
both and mock_sandbox.files.exists is not called. Add the upper-bound rejection
test in
clients/integrations/mcp-server/tests/unit/test_tools/test_list_files.py:136-153,
asserting mock_sandbox.files.list is not called.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/tools/list_files.py`:
- Around line 58-72: Limit the entries processed by list_files before
constructing ListFilesOutputSchema, using a defined maximum count or pagination
strategy so large directories cannot exhaust memory. Document the selected
response-size contract near list_files and preserve the existing FileEntrySchema
mapping for returned entries.

---

Nitpick comments:
In `@clients/integrations/mcp-server/tests/unit/test_tools/test_file_exists.py`:
- Around line 71-94: Add MCP-level timeout boundary tests in
clients/integrations/mcp-server/tests/unit/test_tools/test_file_exists.py:71-94
for timeout=0 and timeout=TOOL_MAX_TIMEOUT + 1, asserting validation rejects
both and mock_sandbox.files.exists is not called. Add the upper-bound rejection
test in
clients/integrations/mcp-server/tests/unit/test_tools/test_list_files.py:136-153,
asserting mock_sandbox.files.list is not called.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ebe184a4-9d00-45f1-acfc-87bb73720e23

📥 Commits

Reviewing files that changed from the base of the PR and between aceb763 and c47a7c3.

📒 Files selected for processing (7)
  • clients/integrations/mcp-server/README.md
  • clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/server.py
  • clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/tools/__init__.py
  • clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/tools/file_exists.py
  • clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/tools/list_files.py
  • clients/integrations/mcp-server/tests/unit/test_tools/test_file_exists.py
  • clients/integrations/mcp-server/tests/unit/test_tools/test_list_files.py

Addresses the CodeRabbit review.

Cap the number of entries list_files returns (max_entries, default 1000,
hard ceiling 10000) and report total_entries plus truncated so a caller
can tell a complete listing from a partial one.

One correction to the review's rationale: a cap here cannot prevent
memory exhaustion. The SDK's files.list() calls response.json() and
builds the whole list[FileEntry] before this tool is reached, so peak
allocation has already happened by the time we could truncate; that
bound belongs in the SDK or the runtime. The cap is still worth having
for a different reason -- a directory with tens of thousands of entries
would otherwise be serialized straight into an LLM's context window and
exhaust its token budget. total_entries exists so truncation is never
silent, since a model shown a partial listing as if it were complete is
worse than one told to narrow its query.

Also extend the timeout coverage the review asked for: both tools now
test the lower (0) and upper (TOOL_MAX_TIMEOUT + 1) bounds, and
max_entries is tested at 0 and above its ceiling. Every rejection case
asserts the sandbox was never touched, so validation failures cannot
reach the backing call.

Suite: 30 -> 37 tests.
@kubernetes-prow kubernetes-prow Bot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Aug 4, 2026
@yuzhiquan

Copy link
Copy Markdown
Author

Thanks — both findings addressed in a1503a4. Applied locally rather than via "Commit suggestion", per the contributor notice.

Directory listing size — capped, but I want to flag that the stated rationale doesn't hold, in case it matters for where a real fix belongs.

A cap in this tool cannot prevent memory exhaustion. AsyncFilesystem.list() calls response.json() and then builds the complete list[FileEntry] before returning:

entries = response.json()              # whole body already parsed
file_entries = [FileEntry(**e) for e in entries]   # whole list already built

By the time list_files receives that list, peak allocation has already occurred. Truncating afterwards frees nothing. A genuine memory bound needs pagination or a streaming/limit parameter in the SDK's filesystem API (or a cap in the sandbox runtime's list/ endpoint) — worth a separate issue if you agree, since it would apply equally to the Go SDK's Sandbox.List and to any direct SDK consumer, not just MCP.

That said, the cap is worth having for a different reason, so I've implemented it: a directory with tens of thousands of entries would otherwise be serialized straight into an LLM's context window and exhaust its token budget. So:

  • max_entries (default 1000, hard ceiling 10000) bounds the response.
  • total_entries reports the true count and truncated flags partial results — deliberately, so truncation is never silent. A model shown a partial listing as though it were complete is worse off than one told to narrow its query.
  • The docstring, the field descriptions and the README all state that the cap bounds the MCP response, not sandbox-side memory, so the next reader doesn't mistake this for a memory guard.

Timeout boundaries — agreed, coverage was incomplete. Both tools now parametrize over 0 and TOOL_MAX_TIMEOUT + 1, and max_entries is tested at 0 and above its ceiling. Every rejection case asserts the backing sandbox call was never made, so a validation failure can't reach the SDK.

Suite is 30 → 37 tests; dev/tools/test-unit is green across all suites locally (failures=0 errors=0).

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 two new filesystem-oriented tools to the Agent Sandbox MCP server to close a cross-SDK parity gap (Python/Go SDKs already support directory listing and existence checks).

Changes:

  • Introduces list_files tool to list directory entries (with response truncation via max_entries, plus total_entries and truncated).
  • Introduces file_exists tool to check path existence with a boolean result.
  • Adds unit tests and updates MCP server README to document both tools.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/tools/list_files.py New MCP tool that calls sandbox.files.list() and returns structured directory entries with truncation metadata.
clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/tools/file_exists.py New MCP tool that calls sandbox.files.exists() and returns {exists: bool}.
clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/tools/init.py Exports the two new tools from the tools package.
clients/integrations/mcp-server/k8s_agent_sandbox_mcp_server/server.py Registers list_files and file_exists with the FastMCP server.
clients/integrations/mcp-server/tests/unit/test_tools/test_list_files.py Unit coverage for default/non-default args, truncation behavior, validation, error surfacing, and fail-closed session ownership checks.
clients/integrations/mcp-server/tests/unit/test_tools/test_file_exists.py Unit coverage for present/absent paths, validation, error surfacing, and fail-closed session ownership checks.
clients/integrations/mcp-server/README.md Documents the new tools, arguments, and return contracts.

@yuzhiquan

Copy link
Copy Markdown
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
@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/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

4 participants