Skip to main content
Glama

MCP Modal Server

mcp-modal MCP server

PyPI

An MCP server for managing Modal — apps, containers, volumes, and secrets — and for deploying & running Modal apps directly from Claude Code and other MCP clients.

Every tool shells out to your local modal CLI, so it operates against whatever Modal profile and credentials are configured on your machine. There are no extra tokens to manage.

Installation

The server is published on PyPI as mcp-modal. No manual install is needed — the recommended way to run it is with uvx, which fetches and launches it on demand. Just point your MCP client at the command below (see Configuration).

Every version is also tagged and published on the Releases page, with release notes and the same .whl / .tar.gz that PyPI serves attached — useful for pinning, air-gapped installs, or reading what changed between two versions.

Related MCP server: MCP Server Modal

Logging in to Modal

This server uses your local Modal credentials. If you haven't authenticated yet, run:

modal setup

This opens a browser to log in and stores a token in ~/.modal.toml. Already logged in elsewhere? Check with modal profile current.

Configuration

Add the server to Claude Code with the claude mcp CLI:

claude mcp add mcp-modal -- uvx mcp-modal@latest

Or add it to a .mcp.json file in your project root, which is the better option for a team — everyone who opens the repo gets the same configuration:

{
  "mcpServers": {
    "mcp-modal": {
      "command": "uvx",
      "args": ["mcp-modal@latest"]
    }
  }
}

Why @latest, and when to pin instead

uvx caches the environment it builds on the first run and does not check PyPI again:

"uvx will use the latest available version of the requested tool on the first invocation. After that, uvx will use the cached version of the tool unless a different version is requested, the cache is pruned, or the cache is refreshed." — uv docs

So a plain uvx mcp-modal means latest at install time, frozen forever after — restarting the client or rebooting changes nothing, because the cache lives on disk. Different people end up on different versions depending on when they first ran it, with no warning.

  • mcp-modal@latest re-resolves on every launch, so a restart picks up new releases. Costs one network round-trip at startup. Use it while the tool surface is still moving.

  • mcp-modal@0.4.0 (an explicit version) is reproducible and upgrades become a deliberate one-line change. Use it once you want stability, or for a wider audience.

To move a machine that is already stuck on an old cached build, switching it to either form above is enough — requesting a version invalidates the cache. Otherwise uv cache clean mcp-modal forces a refresh.

Requirements

  • Python 3.11 or higher

  • uv (provides uvx)

  • Modal CLI 1.5 or newer, configured with valid credentials (modal setup) — 1.5 is where modal billing summary/rates landed and where the billing report switched to snake_case columns; the cost tool reads both spellings but needs 1.5 for those two views

  • For Modal deploy and run support:

    • The project being deployed/run must use uv for dependency management

    • modal must be installed in that project's virtual environment

Security

This server shells out to your local modal CLI using whatever credentials are in ~/.modal.toml. A few tools are powerful by design — if the MCP client driving the server is ever prompt-injected (for example by malicious text inside logs it fetched), these are the escalation paths and should stay behind your client's tool-approval prompts rather than being auto-approved:

  • deploy_modal_app / run_modal_app — execute arbitrary local Python on the host (modal deploy imports the app file; uv run resolves and installs the target project's dependencies).

  • modal_volume_files with action="put" — can read any local file (e.g. ~/.ssh/id_rsa, ~/.modal.toml) and upload it to a cloud volume (a data-exfiltration primitive).

  • modal_volume_files with action="get" and force=True — can overwrite any local path (e.g. ~/.zshrc or a shell profile, a persistence primitive).

  • manage_modal_container with action="exec" — runs arbitrary commands inside a container, by design.

Every tool declares MCP tool annotations, so a client can distinguish the four read-only tools (list_modal_resources, get_modal_logs, search_modal_logs, analyze_modal_costs — all readOnlyHint: true) from the eight that change remote state or start compute. Six of those eight are destructiveHint: true; the exceptions are run_modal_app and inspect_modal_secret, which start compute without removing or overwriting anything. Auto-approve the reads; keep the rest behind a prompt.

Optional local-path allowlist

To contain the two filesystem-touching volume tools, set the MCP_MODAL_ALLOWED_LOCAL_PATHS environment variable to an os.pathsep-separated list of directories (: on macOS/Linux). When it is set, modal_volume_files is refused for any local path — local_path on action="put", the destination on action="get" — unless the resolved path, after expanding ~ and collapsing ../symlinks, falls inside one of those roots. The download target "-" (return contents instead of writing a file) is exempt because nothing is written to disk.

When the variable is unset (the default) there is no restriction, so existing setups are unaffected. Configure it in your MCP client, e.g.:

{
  "mcpServers": {
    "mcp-modal": {
      "command": "uvx",
      "args": ["mcp-modal"],
      "env": { "MCP_MODAL_ALLOWED_LOCAL_PATHS": "/Users/me/modal-workspace:/tmp/modal" }
    }
  }
}

All tools also pass user-supplied names/paths after a -- end-of-options separator, so a value beginning with - is always treated as data, never as a modal CLI flag. Secret values handed to manage_modal_secret are redacted from the echoed command, logs, and any error output.

Supported Tools

12 tools. Related operations are grouped behind an action/resource argument rather than split one-per-CLI-subcommand: every tool schema is loaded into the model's context for the whole session, so a smaller surface leaves more room for your actual work (and gives the model fewer near-identical tools to choose between).

Tools that talk to environment-scoped resources take an optional env argument to target a specific Modal environment; if omitted, they use the profile's default (or MODAL_ENVIRONMENT). The exception is manage_modal_container and container logs — a container ID is globally unique and the CLI accepts no environment there.

Read-only

  1. List Modal Resources (list_modal_resources) — one lookup for the whole account.

    • Parameters: resource (required), name, path (default /), env

    • resource values:

      value

      returns

      name means

      apps

      deployed/running/recently-stopped apps

      app_history

      one app's deployment versions (for rollback)

      app name/ID

      containers

      running containers (ta-...)

      app ID to filter by

      volumes

      named volumes

      volume_files

      files inside a volume (with path)

      volume name

      secrets

      secret names (values are never exposed)

      environments

      valid env values for this workspace

      profile

      active profile + all profiles

    • volume_files sets empty: true with a message when a listing genuinely returns nothing, so an empty directory is distinguishable from a wrong path.

    • Listings over 200 entries are capped, with omitted_items giving the number dropped.

  2. Get Modal Logs (get_modal_logs) — fetch or stream logs for an app or a container.

    • Parameters: identifier (required), target (auto/app/container, default auto — anything starting ta- is a container), timeout_seconds (default 30), env, since, until, tail, source (stdout/stderr/system), timestamps, follow

    • since without tail fetches every entry in the range; pass until as well (max range 35 days, tail max 20,000) to keep a busy app's output bounded.

    • With follow=True, logs stream until the app/container stops or timeout_seconds is reached, returning a snapshot with truncated: true.

    • Only covers the stdout/stderr/system streams; some failures (e.g. a crash reported as "... exited with ...") are Modal dashboard events, not log lines, and won't appear here.

  3. Search Modal Logs (search_modal_logs) — grep logs and get each hit with the surrounding lines, built for "where did it go wrong?" debugging. Logs are fetched once and searched locally, so you get context, regex, case control, and exact match counts.

    • Parameters: identifier (required), pattern (required), target (default auto), regex, case_sensitive, context_lines (default 3), max_matches (default 50), since, until, tail (defaults to the last 1000 entries), source, exclude (drop noise lines before searching, e.g. "queue put failed"), prefilter, timestamps (default true), timeout_seconds, env

    • Bound the window on a busy app. since on its own fetches everything from then until now — hundreds of KB per hour on a chatty app, which the 30s fetch cuts off (logs_truncated: true) and the output budget trims. since and until around the minute you care about is the fix, and is usually kilobytes.

    • prefilter=True pushes pattern down to Modal as a server-side substring filter (modal app logs --search), so non-matching lines are never fetched — the lever for logs too large to drain. Requires regex=False, and context lines then show only other matches, so use it to locate the window and re-query it with prefilter=False.

    • Returns match_count and matches: timestamped, line-numbered context blocks where matched lines are prefixed with >, e.g. > 8: 2026-06-04T... ValueError: bad input. The whole fetched log is always searched, so match_count stays exact even when fewer blocks are returned. returned is how many matches came back (adjacent matches merge into one block, counted by returned_blocks). Reports excluded_lines when exclude is used.

    • A window the CLI rejects (reversed range, over 35 days, tail over 20,000) comes back as success: false with Modal's own message, not a bare exit code.

    • Same stdout/stderr/system-only caveat as get_modal_logs.

Deploy & run

  1. Deploy Modal App (deploy_modal_app)

    • Deploys a Modal app (modal deploy). Deployed web endpoints persist, so any links in the output are live and shareable (returned in urls).

    • Parameters: absolute_path_to_app (required), env, name, tag, strategy (rolling/recreate), stream_logs

    • The app's directory must use uv with modal installed in its virtualenv.

  2. Run Modal App (run_modal_app)

    • Runs a function or local entrypoint once and collects its output (modal run).

    • Parameters: absolute_path_to_app (required), function_name, env, detach, timeout_seconds (default 120)

    • Returns a snapshot with truncated: true if the run is still going at the timeout. Pass detach=True to keep long jobs alive on Modal past the timeout.

Why no modal serve tool? modal serve only keeps its endpoints alive while the blocking process runs — an MCP tool that returns would tear them down immediately, handing back a dead URL. Use deploy_modal_app for a persistent, shareable endpoint.

State changes

  1. Manage Modal App (manage_modal_app) — action is stop (shut the app down and terminate its containers) or rollback (redeploy a previous version).

    • Parameters: action (required), app_identifier (required), version (rollback only — defaults to the immediately preceding version), env

  2. Manage Modal Container (manage_modal_container) — action is exec (run a command inside a running container, modal container exec --no-pty) or stop (terminate it).

    • Parameters: action (required), container_id (required), command (exec only — a list of args, e.g. ["python", "-c", "print('hi')"]), timeout_seconds (default 60)

  3. Manage Modal Volume (manage_modal_volume) — action is create, delete (the volume and all its data, irreversible), or rename.

    • Parameters: action (required), volume_name (required), new_name (rename only), env

  4. Modal Volume Files (modal_volume_files) — write operations on a volume's files: action is put (upload), get (download), cp (copy inside the volume), or rm.

    • Parameters: action (required), volume_name (required), local_path, remote_path, paths (for cp: sources then destination), recursive, force, env

    • action="get" with local_path="-" returns the file contents instead of writing a file.

    • To list a volume's contents use list_modal_resources(resource="volume_files").

  5. Manage Modal Secret (manage_modal_secret) — action is create or delete.

    • Parameters: action (required), secret_name (required), key_values (dict), from_dotenv (path), from_json (path), force, env. Creating requires at least one of key_values, from_dotenv, or from_json.

    • Secret values are redacted from every field returned, including error output.

    • To list secret names use list_modal_resources(resource="secrets").

Costs

  1. Analyze Modal Costs (analyze_modal_costs) — read-only. Fetches modal billing once and aggregates locally, so you get ranked totals and period-over-period changes instead of hundreds of raw rows.

    • Parameters: view (default by_app), period, start, end, resolution (d/h), timezone, app, environment, top_n (default 10), tag_names

    • view values:

      value

      answers

      by_app

      "what is my costliest app?" — apps ranked by spend, with % share

      timeline

      "why was Monday expensive?" — cost per interval, plus an explanation that diffs the peak interval against the one before and ranks which apps grew

      by_environment

      which environment the money goes to

      by_resource

      CPU vs GPU class vs memory vs storage

      summary

      billed vs metered cost for a month cycle, with credits/plan adjustments

      rates

      current unit prices

    • total_cost always covers every row in range, even when groups is cut to top_n — quote it rather than summing the visible rows.

    • Billing is workspace-wide (the CLI takes no -e), so this reports across all environments; environment filters the rows afterwards.

    • Modal reports whole intervals only, so a partially elapsed day reads low.

Secrets — inspection

  1. Inspect Modal Secret (inspect_modal_secret) — lists the key names inside a secret, never the values.

    • Parameters: secret_name (required), env, image, timeout_seconds (default 300)

    • Modal exposes no API for this by design: not the CLI, not the SDK, not the gRPC layer. The only way to see which keys a secret defines is to mount it in a container and list the environment. So this tool runs modal shell --secret <name> with compgen -e (a bash builtin that prints exported variable names only — no value is ever printed, even inside the container), then subtracts the variables the image and Modal runtime set anyway — 23 known names plus anything under six prefixes (MODAL_, PYTHON, PIP_, NVIDIA_, CUDA_, LD_LIBRARY_PATH), which also covers the MODAL_TOKEN_* credentials that live in every container.

    • This one call starts remote compute, so it costs a few cents and takes tens of seconds (longer when the image has to build). Every other read in this server is free; use list_modal_resources(resource="secrets") to see which secrets exist and reach for this only when you need to know what is inside one.

    • Returns keys, plus the unfiltered all_env_names so a key that looks like a runtime variable is still visible rather than silently dropped.

    • Omit image to use Modal's default (built to match the server's Python — the most reliable choice). Pass one, e.g. python:3.12-slim, if your workspace's image builder rejects that Python version.

Prompts

The server also ships four MCP prompts — multi-step workflows your client can invoke directly (in Claude Code they appear as /mcp__mcp-modal__<name>). Prompts are fetched on demand, so unlike tools they cost nothing in per-session context:

  • debug_modal_app (app_name, optional symptom) — an ordered triage routine: check the app is up, search logs for tracebacks with context, narrow the window instead of widening it when a log fetch comes back truncated, fall back to the log tail, check whether sibling apps were hit in the same window, inspect containers, then compare against deployment history and consider a rollback.

  • deploy_and_verify (absolute_path_to_app, optional env) — confirm the target workspace, deploy, report the live URLs, then verify the app is healthy instead of assuming it.

  • review_modal_account (optional env) — a read-only inventory that flags idle apps, unexplained running containers, and orphaned volumes/secrets, naming the exact call that would clean each one up without running it.

  • investigate_modal_costs (optional period, app) — traces a spend increase from the daily timeline down to the peak hour, the resource class, and the deploy or still-running container behind it.

Output caps

Log, run, and exec output is capped before it is returned, so one chatty app can't flood your context window. The default budget is 40,000 characters per text field (roughly 10k tokens); when a field is trimmed the result sets output_capped: true and the text carries a marker naming how much was dropped. A capped field keeps its head and its tail, so a startup banner and the traceback at the end both survive.

Searching is never capped before the fact: search_modal_logs greps the whole fetched log and only limits how many context blocks come back, so match_count is always exact.

Raising timeout_seconds or the budget is rarely the right answer to a truncated log search — fetching less is. Bound the window with since and until, filter with source/exclude, or set prefilter=True to drop non-matching lines inside Modal.

Set MCP_MODAL_MAX_OUTPUT_CHARS to raise or lower the budget, or to 0 to disable capping entirely:

{
  "mcpServers": {
    "mcp-modal": {
      "command": "uvx",
      "args": ["mcp-modal"],
      "env": { "MCP_MODAL_MAX_OUTPUT_CHARS": "80000" }
    }
  }
}

Response Format

All tools return responses in a standardized format, with slight variations depending on the operation type:

# Lookups (list_modal_resources):
{
    "success": True,
    "apps": [...],          # or "containers", "volumes", "contents", "secrets", ...
    "omitted_items": 0      # present when the listing was capped at 200 entries
}

# Action operations (deploy, stop, rollback, create, delete, rename, cp, put, get, rm):
{
    "success": True,
    "message": "Operation successful message",
    "command": "executed command string",
    "stdout": "command output",  # if any
    "stderr": "error output"     # if any
}

# Log / run / exec operations (snapshot-based):
{
    "success": True,
    "logs": "...",          # or "output" for run/exec
    "truncated": False,     # True when cut off at timeout_seconds
    "output_capped": False, # True when text was trimmed to fit MCP_MODAL_MAX_OUTPUT_CHARS
    "command": "executed command string"
}

# Log search (search_modal_logs):
{
    "success": True,
    "match_count": 12,      # exact: the whole fetched log is searched
    "returned": 5,          # matches actually shown
    "returned_blocks": 2,   # adjacent matches merge into one context block
    "matches": ["> 8: ...", ...],
    "logs_truncated": False,  # True when the log fetch hit timeout_seconds
    "output_capped": False,
    "command": "executed command string"
}

# Error case (all operations):
{
    "success": False,
    "error": "Error message describing what went wrong",
    "command": "executed command string",
    "stdout": "command output",  # if available
    "stderr": "error output"     # if available
}

License

This project is licensed under the MIT License - see the LICENSE file for details.

Available Tools

12 tools
analyze_modal_costsA
Read-onlyIdempotent
Break down what the workspace is spending (`modal billing`). Costs are fetched once
and aggregated locally, so you get ranked totals and period-over-period changes
rather than hundreds of raw rows.

Answering common questions:
  "what is my costliest app?"     -> view="by_app", period="this month"
  "why was Monday expensive?"     -> view="timeline", period="last week" (the
                                     `explanation` field diffs the peak day against
                                     the day before and ranks which apps grew)
  "what did that day cost hourly?" -> view="timeline", start="2026-08-31",
                                      end="2026-09-01", resolution="h"
  "where does the money go?"      -> view="by_resource" (CPU / GPU / memory / ...)
  "what is the bill this cycle?"  -> view="summary"

Billing is workspace-wide, so this reports across every environment; use
`environment` to narrow it after the fact.

Args:
    view: "by_app" (default), "timeline" (per interval, with an explanation of the
        peak), "by_environment", "by_resource", "summary" (billed vs metered for a
        month cycle), or "rates" (current unit prices).
    period: Convenience range — "today", "yesterday", "this week", "last week",
        "this month", "last month". For "summary" also accepts "YYYY-MM".
    start / end: Explicit range instead of `period` — ISO dates ("2026-08-31") or
        relative ("3 days ago"). Start is inclusive, end exclusive; end defaults to now.
    resolution: "d" (daily, default) or "h" (hourly). Hourly is what you want when
        drilling into a single day.
    timezone: Timezone for interpreting dates — "local", an offset ("+05:30"), or an
        IANA name. Requires resolution="h".
    app: Only include apps whose name or ID contains this string (case-insensitive).
    environment: Only include rows from this Modal environment.
    top_n: How many groups/movers to return. Default 10.
    tag_names: Comma-separated cost-attribution tag names to include.

Returns: {total_cost, groups | intervals, explanation (for timeline), row_count}.
    Costs are strings of US dollars with 4 decimals. `total_cost` always covers every
    row in range, even when `groups` is cut to top_n.
ParametersJSON Schema
NameRequiredDescriptionDefault
appNo
endNo
viewNoby_app
startNo
top_nNo
periodNo
timezoneNo
tag_namesNo
resolutionNod
environmentNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, and the description adds meaningful behavior beyond that: 'Costs are fetched once and aggregated locally', 'total_cost always covers every row in range, even when groups is cut to top_n', and costs are strings with 4 decimals. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Long but tightly organized: purpose, example-driven usage, workspace-wide caveat, Args list, and Returns. For a tool with 10 parameters, the length is justified and every sentence adds operational value; no filler or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers all 10 optional parameters, return shape, cost formatting, date-range edge cases, and view-specific explanation behavior. The description is complete enough for an agent to invoke this tool correctly without external documentation, even with an output schema present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description documents every parameter: view values, period named ranges, start/end inclusivity, resolution alternatives, timezone requirements, app filtering, environment scoping, top_n default, and tag_names format. This fully compensates for the schema's lack of property descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Break down what the workspace is spending ... modal billing') and enumerates distinct views. It is unambiguous and clearly differentiated from sibling tools like deploy, run, logs, and resource management, none of which cover cost analytics.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit common-question mappings ('what is my costliest app?' -> view="by_app", period="this month"') and explains scope ('Billing is workspace-wide... use environment to narrow it after the fact'). This gives an agent concrete decision rules for selecting the right view and parameters.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

deploy_modal_appA
Destructive
Deploy a Modal app (`modal deploy`). Deployed endpoints persist after this returns,
so any URLs in the result are live, shareable links.

Args:
    absolute_path_to_app: Absolute path to the app file. Its directory must use `uv`
        and have `modal` installed in its virtualenv.
    env: Modal environment to deploy into.
    name: Deployment name (`--name`).
    tag: Version tag (`--tag`).
    strategy: Rollout strategy — "rolling" or "recreate".
    stream_logs: Stream the app's logs after deploying.

Returns: {message, urls (live endpoints), stdout, stderr}.
ParametersJSON Schema
NameRequiredDescriptionDefault
envNo
tagNo
nameNo
strategyNo
stream_logsNo
absolute_path_to_appYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=false, so the agent knows deployment is a mutating operation. The description adds useful context by stating that deployed endpoints persist and that returned URLs are live shareable links. It does not disclose what can be overwritten or replaced during deployment, which would have strengthened transparency given the destructive hint.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and well-structured: a one-sentence purpose with the most important behavioral fact, followed by a succinct argument list and a return summary. Every sentence earns its place and there is no filler or repetition of schema defaults.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with six parameters, an output schema, and annotations, the description covers the key call-time prerequisites, parameter semantics, and return fields. It could mention failure/conflict behavior or rollback semantics, but the output schema and annotations already cover safety and return structure, so the description is reasonably complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the explanatory burden for parameters. It documents all six arguments, including the absolute path precondition (uv + modal in virtualenv), the mapping of name/tag to `--name`/`--tag`, the valid strategy values, and stream_logs behavior. It could add more detail on env naming or tag format, but it already adds substantial meaning beyond the raw schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb and resource: 'Deploy a Modal app', and reinforces it with the underlying command `modal deploy`. It also distinguishes deployment from ephemeral execution by noting that endpoints persist after the call returns. However, it does not explicitly contrast itself with siblings like run_modal_app or manage_modal_app.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies persistent deployment through the phrase 'endpoints persist after this returns', but it gives no explicit guidance on when to choose deploy over run_modal_app or manage_modal_app. There are no stated exclusions, prerequisites beyond the app's directory, or alternative tools to consider.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_modal_logsA
Read-onlyIdempotent
Fetch logs for an app or container (`modal app logs` / `modal container logs`).
To find where something went wrong, prefer search_modal_logs — it returns matches
with surrounding context instead of a raw tail.

Covers the stdout/stderr/system streams ONLY. Crash events shown on the Modal
dashboard (e.g. "... exited with ...") are not log lines and never appear here.

Args:
    identifier: App name/ID ("my-app", "ap-...") or container ID ("ta-...").
    target: "auto" (default — "ta-..." is a container), "app", or "container".
    timeout_seconds: Max seconds to collect. Default 30.
    env: Modal environment. Apps only — container logs take no environment.
    since / until: Time range, ISO 8601 or relative ("2h", "30m", "1d"). Max 35 days.
        `since` without `tail` fetches EVERY entry in the range — pass `until` too
        (or a `tail`) to bound the volume on a busy app.
    tail: Only the last N entries (max 20000).
    source: "stdout", "stderr", or "system".
    timestamps: Prefix each line with its wall-clock timestamp.
    follow: Live-stream until the app/container stops or the timeout hits.

Returns: {logs, truncated (still streaming at the timeout), output_capped (text
trimmed to fit context — narrow with tail/since/source)}.
ParametersJSON Schema
NameRequiredDescriptionDefault
envNo
tailNo
sinceNo
untilNo
followNo
sourceNo
targetNoauto
identifierYes
timestampsNo
timeout_secondsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark readOnly, idempotent, and non-destructive, and the description adds genuinely new behavior knowledge beyond those: only stdout/stderr/system streams are covered, dashboard crash events never appear, `since` without `tail` fetches every entry in range, and returns a `truncated`/`output_capped` flag. This is the kind of context agents need to avoid misreading results.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long, but for a 10-parameter tool with subtle behaviors, every sentence adds necessary information. It is front-loaded with purpose and direction, uses a clean Args list, and closes with the Returns shape. No filler or repeated schema details.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has 10 parameters and an output schema, this description provides crucial contextual constraints: volume pitfalls, stream coverage, crash-event exclusion, defaults, maximum bounds (35 days, tail 20000), and return flag meanings (truncated, output_capped). It is complete enough to invoke correctly without clicking outside docs.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the entire burden for parameter semantics. It fully compensates by explaining identifier formats ('my-app', 'ap-...', 'ta-...'), the `target` auto resolution logic, `timeout_seconds` default, environment scope, ISO/relative and max 35 days for `since`/`until`, `tail` max, allowed sources, `follow` behavior, and `timestamps`. Every parameter in the schema is addressed meaningfully.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource ('Fetch logs for an app or container') and maps it to the underlying CLI commands. It explicitly differentiates itself from the sibling search_modal_logs by contrasting raw logs with matches plus surrounding context, so an agent can tell them apart immediately.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states when not to use this tool: 'To find where something went wrong, prefer search_modal_logs... instead of a raw tail.' It also excludes crash events, which is a clear boundary for agent decision-making. This is explicit when/when-not guidance plus an alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

inspect_modal_secretA
List the KEY NAMES inside a Modal secret — never the values.

Modal exposes no API for this: neither the CLI, the SDK, nor the gRPC layer can read
a secret's contents, by design. The only way to see which keys a secret defines is to
mount it in a container and look at the environment variable names. So this tool
starts a short-lived container (`modal shell --secret ...`), prints the variable NAMES
only, and subtracts the ones the image and the Modal runtime would have set anyway.

That means, unlike every other read in this server, a call here **starts remote
compute and costs a few cents** (and takes tens of seconds — longer on the first run
for a given image, which has to be built). It is not a free lookup: use
list_modal_resources(resource="secrets") to see which secrets exist, and reach for
this only when you need to know what is inside one.

Values never leave the container: the probe is `compgen -e`, a bash builtin that
prints exported variable NAMES only, so no value is ever printed or read.

Args:
    secret_name: Name of the secret, from list_modal_resources(resource="secrets").
    env: Modal environment the secret lives in.
    image: Optional container image. Omit it to use Modal's default image, which is
        built to match this server's Python — that is the most reliable choice. Pass one
        (e.g. "python:3.12-slim") if the workspace's image builder rejects that Python.
    timeout_seconds: Max seconds to wait, including image build. Default 300.

Returns: {keys: [...names...], all_env_names: [...], filtered_out: n}. `all_env_names`
    is the unfiltered list, so a key that looks like a runtime variable (e.g. one
    literally named "PATH") is still visible rather than silently dropped.
ParametersJSON Schema
NameRequiredDescriptionDefault
envNo
imageNo
secret_nameYes
timeout_secondsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes well beyond the annotations by disclosing that a call starts remote compute, costs money, takes tens of seconds, and may trigger an image build on first run. It also explains the probe mechanism and assures that values never leave the container. These are critical behavioral traits not captured by readOnlyHint=false or destructiveHint=false. There is no contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Although the description is long, it is front-loaded with the most critical information (keys only, not values) and the cost warning. The explanation of why no API exists, the specifically scoped probe detail, and the per-parameter Args all earn their place. The structure is clean and skimmable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers return value semantics even beyond the output schema, explaining filtered vs unfiltered results. It also addresses cost, latency, prerequisites, alternatives, and parameter provenance. For a tool with remote side effects and unusual constraints, nothing important is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With schema description coverage at 0%, the description fully compensates via an Args section. Each parameter is explained: secret_name's source, env's purpose, image's optionality and recommended fallback, and timeout_seconds' default and meaning. This addresses the gaps left by the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific action and scope: 'List the KEY NAMES inside a Modal secret — never the values.' It clearly names the resource and differentiates this tool from sibling tools like list_modal_resources and manage_modal_secret by emphasizing it inspects secret contents, not resource existence or management.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use the tool versus alternatives: 'use list_modal_resources(resource="secrets") to see which secrets exist, and reach for this only when you need to know what is inside one.' It also warns that this is 'not a free lookup' and should be used sparingly, giving clear decision guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_modal_resourcesA
Read-onlyIdempotent
Read-only lookup of everything in the Modal account. Start here to find the app name,
container ID or volume name that the other tools take.

Args:
    resource: One of:
        "apps" — deployed/running/recently-stopped apps.
        "app_history" — one app's deployment versions (`name` = app name/ID); use it
            to pick a version for manage_modal_app(action="rollback").
        "containers" — running containers ("ta-..."); `name` = app ID to filter.
        "volumes" — named volumes.
        "volume_files" — files in a volume (`name` = volume, plus `path`).
        "secrets" — secret names only; values are never returned.
        "environments" — valid values for every `env` argument.
        "profile" — active profile + all profiles (which account am I?).
    name: App name/ID, app ID filter, or volume name — see `resource`.
    path: Path inside the volume for "volume_files". Default "/".
    env: Modal environment. Ignored for "environments"/"profile".

Returns: {<resource key>: [...]} — e.g. "apps", "containers", "contents". Listings
over 200 entries are capped, with `omitted_items` giving the count dropped.
ParametersJSON Schema
NameRequiredDescriptionDefault
envNo
nameNo
pathNo/
resourceYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and non-destructive behavior, and the description adds substantial behavioral context beyond that: 200-entry cap with omitted_items, the return shape, that secret values are never returned, and that env is ignored for 'environments'/'profile'. This fully discloses what happens when the tool is invoked.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is detailed yet tightly structured with a front-loaded purpose line and a bulleted resource list. Every sentence adds operational value; no filler or redundant restatement of the name/schema exists.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a broad read-only lookup tool, the description covers purpose, all parameter behaviors, return format, pagination truncation, and caveats like secret value redaction. An agent has enough context to select the right resource and correctly interpret results without opening the output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description compensates completely by documenting every parameter: the full set of resource enum values, the polymorphic meaning of name, the path default, and when env applies. It adds meaning far beyond the raw schema properties.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource ('Read-only lookup of everything in the Modal account') and enumerates eight clearly differentiated resource types. It distinguishes this discovery tool from the sibling action tools by positioning it as the starting point to find identifiers those tools consume.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says 'Start here to find the app name, container ID or volume name that the other tools take,' and each resource option includes its intended use, such as using 'app_history' to pick a version for manage_modal_app(action='rollback'). It also gives exclusions, e.g. secrets return names only and env is ignored for certain resources.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

manage_modal_appA
Destructive
Change a deployed app's state. Both actions affect live traffic.

Args:
    action: "stop" — shut the app down, ending web endpoints (`modal app stop`).
        "rollback" — restore a previous deployment (`modal app rollback`).
    app_identifier: App name ("my-app") or ID ("ap-...").
    version: Rollback target; omit for the immediately preceding version. List valid
        versions with list_modal_resources(resource="app_history", name=...).
    env: Modal environment to target.

Returns: {message, stdout, stderr} or {error}.
ParametersJSON Schema
NameRequiredDescriptionDefault
envNo
actionYes
versionNo
app_identifierYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark destructiveHint=true, readOnlyHint=false, idempotentHint=false. The description adds concrete behavioral detail: stop 'shut the app down, ending web endpoints' and rollback 'restore a previous deployment,' plus the warning that both actions affect live traffic. This goes beyond the structured hints.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact: an opening one-line summary, a tidy Args list, and a Returns line. There is no redundant prose, and the live-traffic warning is front-loaded for safety.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the tool's purpose, both actions, all four parameters, return shape ({message, stdout, stderr} or {error}), and a cross-reference to list valid rollback targets. It doesn't discuss failure cases or reversibility, but output schema and destructive annotations cover part of that, making this adequate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must carry all parameter meaning. It explains action with both permitted values, app_identifier with name/ID formats, version with omission semantics and a pointer to list_modal_resources, and env as the Modal environment to target. The env entry is brief, but every parameter receives usable semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Change a deployed app's state,' naming a specific verb and resource, then enumerates two concrete actions (stop and rollback) with their effects. This clearly distinguishes it from siblings like deploy_modal_app or run_modal_app.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states that both actions affect live traffic, framing when this tool is relevant, and explicitly directs users to list_modal_resources(resource='app_history', name=...) to find valid versions. It does not list exclusions vs. deploy/run, but the action-specific guidance is strong context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

manage_modal_containerA
Destructive
Act on one running container. Find IDs with
list_modal_resources(resource="containers").

Args:
    action: "exec" — run a command inside the container (`modal container exec`).
        This is arbitrary remote code execution: treat it like SSH, not a lookup.
        "stop" — terminate it (`modal container stop`); in-flight inputs are
        cancelled and rescheduled elsewhere.
    container_id: Container ID ("ta-..."). Unique across environments, so no `env`
        argument is needed (the CLI accepts none for these subcommands).
    command: For "exec": argv list, e.g. ["python", "-c", "print('hi')"] or
        ["ls", "-la", "/"].
    timeout_seconds: For "exec": max seconds to wait. Default 60.

Returns: exec → {output, returncode, truncated, output_capped}; stop → {message}.
ParametersJSON Schema
NameRequiredDescriptionDefault
actionYes
commandNo
container_idYes
timeout_secondsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the destructiveHint annotation, it discloses that stop cancels and reschedules in-flight inputs, that exec is equivalent to SSH, and that container IDs are environment-unique so no env arg is needed. This meaningfully surfaces risk and runtime behavior the agent could not infer from annotations alone.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with what the tool does, then organized as Args/Returns. Every sentence adds value, including examples, defaults, and the SSH warning, with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with two modes, four parameters, and meaningful destructive/execution risk, the description covers inputs, behavior, risks, ID discovery, and return shapes. The output schema is also available, but the description already explains exec and stop return structures.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description carries full parameter documentation: action values, container_id format ('ta-...'), command shape for exec, and timeout_seconds semantics with default. It fully compensates for the schema's bare property definitions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Act on one running container' and immediately enumerates the two concrete actions, 'exec' and 'stop', with exact CLI commands. This clearly distinguishes the tool from sibling app/volume/log tools by resource type and operation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It tells the agent to discover container IDs via list_modal_resources(resource="containers"), and warns that exec is 'arbitrary remote code execution... not a lookup.' This is clear, practical guidance, though it doesn't explicitly contrast every sibling tool such as manage_modal_app or get_modal_logs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

manage_modal_secretA
Destructive
Create or delete a secret. To list secret names use
list_modal_resources(resource="secrets") — values are never readable.

Values are redacted from every field returned (command, stdout, stderr, error), so
they cannot leak back into the transcript on failure.

Args:
    action: "create" or "delete".
    secret_name: Secret name.
    key_values: For "create": {"API_KEY": "abc", ...}.
    from_dotenv / from_json: For "create": load key/values from a local file instead.
    force: For "create": overwrite an existing secret.
    env: Modal environment to target.

Returns: {message, stdout, stderr} or {error}, with values redacted.
ParametersJSON Schema
NameRequiredDescriptionDefault
envNo
forceNo
actionYes
from_jsonNo
key_valuesNo
from_dotenvNo
secret_nameYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark the tool as destructive and not read-only, and the description aligns with those flags. It adds valuable context by stating that values are never readable and are redacted from every returned field, including error fields, preventing transcript leaks.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is tight and well-structured: a one-line purpose, a critical security note, a compact Args list, and a return-shape line. Every sentence adds useful information and no space is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, an output schema exists, and the description covers all parameters, the create/delete distinction, file-based alternatives, overwrite behavior, environment targeting, and security-redaction behavior. Nothing essential is missing for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the full burden for all seven parameters. It explains action, secret_name, key_values, from_dotenv/from_json, force, and env, including which parameters apply to 'create' and the shape of key_values.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Create or delete a secret,' naming the specific verb and resource. It also distinguishes itself from the listing path by directing users to list_modal_resources(resource='secrets'), making its scope unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly tells users to use list_modal_resources for listing secret names, which is the main alternative. It does not exhaustively enumerate when to choose inspect_modal_secret or other siblings, but the create/delete scope is clear enough for appropriate routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

manage_modal_volumeA
Destructive
Volume lifecycle. For the files inside a volume use modal_volume_files (writes) or
list_modal_resources(resource="volume_files") (reads).

Args:
    action: "create", "delete" (removes the volume and ALL its data — irreversible),
        or "rename".
    volume_name: Volume name (the current name, for "rename").
    new_name: Required for "rename".
    env: Modal environment. Volumes are environment-scoped, so this must match the
        environment the volume lives in.

Returns: {message, stdout, stderr} or {error}.
ParametersJSON Schema
NameRequiredDescriptionDefault
envNo
actionYes
new_nameNo
volume_nameYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and readOnlyHint=false. The description adds value by specifying 'removes the volume and ALL its data — irreversible' for the delete action, and by disclosing the return format '{message, stdout, stderr} or {error}'. These details enrich the behavioral picture beyond what structured hints provide, though it could mention reversibility of create/rename or permission requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficient: a one-line summary, a sibling-routing sentence, a concise Args block, and a Returns line. It is front-loaded with purpose and the most important caveat (irreversibility) appears in the action list. Slightly verbose with the 'Returns' line but not bloated; every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the 4 parameters, existence of an output schema, and destructive annotations, the description covers the essentials: action semantics, parameter requirements, environment constraint, and return type. It could be more explicit about what 'create' does regarding naming (e.g., that volume_name serves as the new name) and whether rename affects existing file references, but overall it is sufficiently complete for an agent to use the tool safely.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has zero description coverage, so the description carries the full burden. It explains every parameter: action with allowed values and the irreversibility note for delete, volume_name as the current name for rename, new_name as required for rename, and env with the environment-scoping rule. This fully compensates for the lack of schema documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states 'Volume lifecycle' and explicitly lists the three actions (create, delete, rename), making the tool's purpose unambiguous. It also differentiates from sibling tools by directing file-level operations to modal_volume_files and list_modal_resources, so an agent can distinguish it from those alternatives without opening their schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides explicit guidance on when to use this tool versus the siblings: 'For the files inside a volume use modal_volume_files (writes) or list_modal_resources(resource="volume_files") (reads).' It also adds a requirement that 'env' must match the volume's environment, which is a concrete precondition for correct invocation. This goes beyond mere context to actionable routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

run_modal_appA
Run a Modal function or local entrypoint once and collect its output (`modal run`).
Use this to test on Modal compute; use deploy_modal_app to publish.

Args:
    absolute_path_to_app: Absolute path to the app file. Its directory must use `uv`
        and have `modal` installed in its virtualenv.
    function_name: Function/entrypoint name, e.g. "main". Omit if the module has
        exactly one.
    env: Modal environment to target.
    detach: Keep the run alive on Modal past this call (`--detach`) — for long jobs.
    timeout_seconds: Max seconds to collect output. Default 120.

Returns: {output, urls, truncated (still running at the timeout), output_capped}.
ParametersJSON Schema
NameRequiredDescriptionDefault
envNo
detachNo
function_nameNo
timeout_secondsNo
absolute_path_to_appYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations are sparse (no readOnly, no idempotency), so the description carries the disclosure burden. It explains one-shot execution, output collection, detach persistence, and timeout truncation behavior—valuable details beyond the structured fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded with purpose and alternative routing, followed by a scannable Args list and a sharp Returns line. Every sentence adds functional value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite no schema descriptions, the definition covers prerequisites, parameter behavior, return shape, timeout handling, and the publish alternative. It is complete enough for an agent to invoke the tool correctly without external information.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description fully compensates by explaining each parameter: absolute path requirements (uv + modal installed), function_name disambiguation, env target, detach semantics, and timeout default. This adds meaning the schema alone does not provide.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Run') and resource ('Modal function or local entrypoint') and explicitly contrasts with deploy_modal_app ('use deploy_modal_app to publish'). This clearly differentiates the tool from its main sibling.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It says 'Use this to test on Modal compute; use deploy_modal_app to publish,' giving direct when-to-use and when-not-to-use guidance. It also notes detach for long jobs, helping agents select the right mode for the task.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_modal_logsA
Read-onlyIdempotent
Search an app's or container's logs and return each hit WITH surrounding context —
the fastest way to find a traceback, an error or a request ID. Logs are fetched once
and grepped locally, so you get the lines around each match, not just the match.

Covers the stdout/stderr/system streams ONLY. Crash events shown on the Modal
dashboard (e.g. "... exited with ...") are not log lines, so a search for them
returns 0 matches even though the failure is real — check the dashboard instead.

Args:
    identifier: App name/ID ("my-app", "ap-...") or container ID ("ta-...").
    pattern: Text to find, or a Python regex when regex=True.
    target: "auto" (default — "ta-..." is a container), "app", or "container".
    regex / case_sensitive: Match mode. Both default False.
    context_lines: Lines of context each side of a match. Default 3.
    max_matches: Cap on match blocks returned. Default 50.
    since / until: Time range, ISO 8601 or relative ("2h", "30m", "1d"). PREFER a
        bounded range (both ends) when you know roughly when something happened —
        `since` alone fetches every entry from then until now, which on a busy app
        is megabytes and gets cut off at the timeout. Range must be <= 35 days.
    tail: Search only the last N entries (max 20000) instead of a whole range.
        With no since/until/tail, defaults to the last 1000 entries.
    source: Search only "stdout", "stderr", or "system".
    exclude: Drop lines matching this BEFORE searching, to strip repeated noise.
    prefilter: Push `pattern` down to Modal as a server-side substring filter, so
        non-matching lines are never fetched. The big lever for huge logs, but it
        requires regex=False and leaves `context_lines` showing only other matching
        lines — use it to find *where* something is, then re-query that window.
    timestamps: Prefix lines with their timestamp. Default True.
    timeout_seconds: Max seconds spent fetching logs. Default 30.
    env: Modal environment (apps only).

Returns: {match_count (exact, whole log searched), returned (matches actually shown),
returned_blocks, matches (context blocks, matched lines prefixed ">"), excluded_lines,
output_capped}.
ParametersJSON Schema
NameRequiredDescriptionDefault
envNo
tailNo
regexNo
sinceNo
untilNo
sourceNo
targetNoauto
excludeNo
patternYes
prefilterNo
identifierYes
timestampsNo
max_matchesNo
context_linesNo
case_sensitiveNo
timeout_secondsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare read-only, open-world, and idempotent behavior, but the description adds substantial behavioral context beyond structured data: logs are fetched once and grepped locally, crash events are absent from log streams, a single bounded 'since' range can fetch megabytes and be cut off by timeout, and prefilter changes what context_lines shows. These are meaningful, non-obvious behaviors an agent needs to know.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The structure is purposeful: a front-loaded purpose statement, a critical stream-coverage caveat early, and then parameter documentation that earns its place by adding real semantic and operational value. Despite being long, there is little redundancy, and the return-value summary is compact.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 16-parameter tool with 0% schema coverage, the description covers every parameter's semantics, gives default behaviors, failure modes, performance trade-offs, and explains the return shape. It is complete enough for an agent to select and invoke the tool correctly with no additional information.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the full burden, and it succeeds: every parameter is explained with non-schema semantics. identifier gets app/container ID prefixes, target gets an 'auto' rule, since/until gets a format warning and a 35-day cap, tail gets a max and default, prefilter gets a clear server-side behavior, and exclude' is defined as pre-search filtering.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Search an app's or container's logs') and clarifies the unique selling point: returns each hit with surrounding context to find tracebacks, errors, or request IDs. It also distinguishes itself from sibling tools by scoping coverage to stdout/stderr/system streams and explicitly excluding Modal dashboard crash events.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use guidance ('fastest way to find a traceback, an error or a request ID') and a clear when-not-to-use case: crash events are not log lines and should be checked on the dashboard. It also gives operational guidance for large logs, such as preferring bounded ranges, using prefilter for huge logs, and re-querying a window after locating the hit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 1 tool updatev0.5.0
    • Changedsearch_modal_logs2 fields changed
      • addedInput schema / properties / prefilter
        Added value: +{
        +  "default": false,
        +  "title": "Prefilter",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / until
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Until"
        +}
  2. 33 tool updatesv0.2.2
    • Addedanalyze_modal_costs
    • Removedcopy_modal_volume_files
    • Removedcreate_modal_secret
    • Removedcreate_modal_volume
    • Removeddelete_modal_secret
    • Removeddelete_modal_volume
    • Removedexec_modal_container
    • Removedget_modal_app_history
    • Removedget_modal_app_logs
    • Removedget_modal_container_logs
    • Addedget_modal_logs
    • Removedget_modal_profile
    • Removedget_modal_volume_file
    • Addedinspect_modal_secret
    • Removedlist_modal_apps
    • Removedlist_modal_containers
    • Removedlist_modal_environments
    • Addedlist_modal_resources
    • Removedlist_modal_secrets
    • Removedlist_modal_volume_contents
    • Removedlist_modal_volumes
    • Addedmanage_modal_app
    • Addedmanage_modal_container
    • Addedmanage_modal_secret
    • Addedmanage_modal_volume
    • Addedmodal_volume_files
    • Removedput_modal_volume_file
    • Removedremove_modal_volume_file
    • Removedrename_modal_volume
    • Removedrollback_modal_app
    • Changedsearch_modal_logs4 fields changed
      • addedInput schema / properties / exclude
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Exclude"
        +}
      • addedInput schema / properties / source
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null,
        +  "title": "Source"
        +}
      • changedInput schema / properties / target / default
        Previous value: -"app"New value: +"auto"
      • addedInput schema / properties / timestamps
        Added value: +{
        +  "default": true,
        +  "title": "Timestamps",
        +  "type": "boolean"
        +}
    • Removedstop_modal_app
    • Removedstop_modal_container
  3. 26 tool updatesv0.1.0
    • First observedcopy_modal_volume_files
    • First observedcreate_modal_secret
    • First observedcreate_modal_volume
    • First observeddelete_modal_secret
    • First observeddelete_modal_volume
    • First observeddeploy_modal_app
    • First observedexec_modal_container
    • First observedget_modal_app_history
    • First observedget_modal_app_logs
    • First observedget_modal_container_logs
    • First observedget_modal_profile
    • First observedget_modal_volume_file
    • First observedlist_modal_apps
    • First observedlist_modal_containers
    • First observedlist_modal_environments
    • First observedlist_modal_secrets
    • First observedlist_modal_volume_contents
    • First observedlist_modal_volumes
    • First observedput_modal_volume_file
    • First observedremove_modal_volume_file
    • First observedrename_modal_volume
    • First observedrollback_modal_app
    • First observedrun_modal_app
    • First observedsearch_modal_logs
    • First observedstop_modal_app
    • First observedstop_modal_container

TDQS

A4.4/5.0

Scored across 12 tools

Disambiguation5/5

Every tool targets a distinct resource-action pair: logs are split into raw retrieval and context search with explicit cross-references, app and container management are separated, and secret/volume operations are cleanly divided into listing, inspecting, and mutating. The only close pair (get vs search logs) is clearly disambiguated by pointing out which to prefer.

Naming Consistency4/5

Most tools follow a clear [verb]_modal_[noun] pattern (manage_modal_app, deploy_modal_app, list_modal_resources), with consistent snake_case including the 'modal' brand. The outlier is 'modal_volume_files', which drops the verb prefix and reads as a resource path rather than an action, breaking the otherwise predictable naming convention.

Tool Count5/5

Twelve tools cover the full Modal surface — apps, containers, logs, volumes, secrets, billing, and resource discovery — without redundancy or bloat. Each tool addresses a significant workflow, so the count feels naturally scoped for the domain.

Completeness5/5

The toolset provides full lifecycle coverage: deploy/run apps, manage their state (stop/rollback), execute/stop containers, read and write volume files, create/delete secrets plus read key names, cost analytics, and broad resource discovery. Useful missing operations (like editing a secret) are handled via supported actions such as create-with-force, so there are no obvious dead ends.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    A Model Context Protocol server that enables LLMs and AI assistants to create, manage, and interact with isolated cloud-based Python environments with GPU support on Modal.com.
    11
    1
    MIT