Skip to main content
Glama
hypen-code

MCE — MCP Code Execution

by hypen-code

Gryphon

APIs were designed for developers. Gryphon makes them usable by AI agents.

CI Python 3.13+ License: MIT

Gryphon is the guardian between agent-generated code and API authority: compile OpenAPI → discover → inspect → execute → reuse, with a small set of meta-tools, not one MCP tool per endpoint. Credentials stay in the host broker.

Choose a mode

Easiest start

What you get

Local stdio

Checkout + uv run --frozen gryphon stdio

One MCP entry; no Docker, database server, model key, or web login

SaaS locally

Checkout + SQLite + gryphon saas

Browser UI, tenants, uploads, channels and keys on loopback

Hosted server

Docker Compose hosted profile

Same UI with PostgreSQL; provision TLS yourself

Legacy operator HTTP

serve --transport http

Single operator MCP endpoint; no web UI or tenant management

2.0.0 · Python 3.13+ · FastMCP 4.0.2 · pydantic-monty 0.0.18. Real-client tests cover MCP 2026-07-28 and legacy initialization; native MCP Tasks are not implemented. Distribution: gryphon-runtime; package/CLI: gryphon. No PyPI or container-registry publication is claimed.

1. Run local stdio

Install uv and use Python 3.13+ on Linux/macOS (POSIX storage locks); Windows users can use containers. From a new checkout:

git clone https://github.com/hypen-code/gryphon.git
cd gryphon
uv run --frozen gryphon stdio

That is enough for empty-catalog offline compute. The foreground process waits for MCP on stdin; it is not a web server or an interactive Python prompt. Usually your MCP client launches it instead. Stop this trial with Ctrl+C before launching another process against the same state.

For API discovery, add the entry below to your MCP client. The first uv run creates .venv; replace both checkout paths with actual absolute paths. No .env, config copy, or separate compile step is needed:

{
  "mcpServers": {
    "gryphon": {
      "command": "/absolute/path/to/gryphon/.venv/bin/gryphon",
      "args": ["stdio"],
      "env": {
        "GRYPHON_SWAGGERS": "[{\"name\":\"weather\",\"swagger_url\":\"/absolute/path/to/gryphon/examples/weather.yaml\",\"is_read_only\":true}]"
      }
    }
  }
}

Alternatively use your absolute uv executable with args ["--directory", "/absolute/path/to/gryphon", "run", "--frozen", "gryphon", "stdio"] and the same env. Clients may not expand ~, $PWD, or your shell's PATH. GRYPHON_SWAGGERS is JSON encoded as an environment string: use absolute local OpenAPI paths or policy-approved public HTTPS specification URLs. The weather file describes Open-Meteo; compiling that local file needs neither network access nor auth. Actually executing a weather call needs network access. For private/authenticated local-mode APIs, see credentials and writes.

stdio compiles and serves from the launch environment only; it never discovers ambient .env. An explicit --env-file /absolute/path/to/private.env is optional. Omit sources or use GRYPHON_SWAGGERS="[]" for compute only. An explicitly selected GRYPHON_SWAGGER_CONFIG_FILE also works. Optional absolute GRYPHON_STATE_DIR sets the private state root (default: absolute $XDG_STATE_HOME/gryphon, otherwise ~/.local/state/gryphon). Catalog/cache/receipts/artifacts are source-scoped; explicit storage overrides are retained. No installed-package writes occur. Simultaneous clients may share a state root: run-ledger recovery ownership is advisory, and startup recovery only interrupts active receipts older than GRYPHON_RUN_RECOVERY_STALE_SECONDS (default 600), so one process never clobbers another's live runs. Stdio trusts its launcher (local owner). Logs use stderr, MCP uses stdout. A stdio server with no inbound MCP message for GRYPHON_STDIO_IDLE_TIMEOUT_SECONDS (default 1800; 0 disables) exits and releases the ledger, so a host that abandons a connection without closing it cannot wedge later launches. On Linux it also requests a parent-death signal so it exits with the process that launched it. Included catalog POST operations execute by default in stdio (set GRYPHON_ALLOW_CATALOG_POSTS=false to disable); PUT/PATCH/DELETE still require GRYPHON_ALLOW_WRITES and exact permits. Use separate GRYPHON_STATE_DIR roots when clients should not share recipes and receipts.

Development dependencies are optional for running stdio: uv sync --frozen --extra dev --extra saas installs the full test environment; the saas extra requires system libpq (Debian/Ubuntu: libpq5).

Related MCP server: ipybox

2. Run SaaS with the web UI

Least setup: native SQLite on your machine

From the checkout root, install system libpq first (the hosted modules import pure psycopg==3.2.9 even with SQLite). No PostgreSQL server or Docker is needed. Paste this as separate lines, not as one long export command:

uv sync --frozen --extra saas
umask 077
mkdir -p "$PWD/data/saas"
export GRYPHON_SAAS_DATABASE_URL="sqlite:///$PWD/data/saas/control.db"
export GRYPHON_SAAS_STATE_DIR="$PWD/data/saas/state"
export GRYPHON_SAAS_HOST=127.0.0.1
export GRYPHON_SAAS_PORT=8000
export GRYPHON_SAAS_PUBLIC_ORIGIN=http://127.0.0.1:8000
export GRYPHON_SAAS_ALLOW_INSECURE_HTTP=true
export GRYPHON_SAAS_ADMIN_TOKEN="${GRYPHON_SAAS_ADMIN_TOKEN:-$(uv run --frozen python -c 'import secrets; print(secrets.token_urlsafe(48))')}"
printf '%s\n' "$GRYPHON_SAAS_ADMIN_TOKEN"
uv run --frozen --extra saas gryphon saas

Create the SQLite parent directory before startup; SQLite does not create it. Use a dedicated private directory; umask does not repair existing permissions. The token command generates a token only when absent/empty. Save it in a password manager and export that saved value in a new shell; do not generate a new recovery token on every restart. printf displays it only in your private local terminal: do not share it in chat, screenshots, logs, or a committed file.

Open http://127.0.0.1:8000/, expand Bootstrap administrator token, and paste its displayed value, not $GRYPHON_SAAS_ADMIN_TOKEN. Keep the terminal running; Ctrl+C stops the server. On restart, reuse the same database, state directory and token. This explicit loopback-only HTTP exception disables Secure cookies; never use it for public traffic. saas does not load ambient .env; use explicit --env-file /absolute/path/to/private-hosted.env if preferred.

No standalone compile is required: choose File, OpenAPI URL or UCP URL in the browser. gryphon serve --transport http is the legacy MCP-only server, not this UI.

Docker + PostgreSQL hosted server

This is the easiest server deployment if you already have Docker and Compose 2.24+; the image includes hosted dependencies and libpq5. Provision a TLS reverse proxy forwarding to 127.0.0.1:8001, preserving the canonical Host header. Set your actual HTTPS origin below; proxy headers are not trusted. Run from the checkout root, retaining both generated secrets across restarts:

export GRYPHON_SAAS_ADMIN_TOKEN="${GRYPHON_SAAS_ADMIN_TOKEN:-$(python3 -c 'import secrets; print(secrets.token_urlsafe(48))')}"
export GRYPHON_POSTGRES_PASSWORD="${GRYPHON_POSTGRES_PASSWORD:-$(python3 -c 'import secrets; print(secrets.token_hex(32))')}"
export GRYPHON_SAAS_PUBLIC_ORIGIN=https://gryphon.example.com
export GRYPHON_SAAS_ALLOW_INSECURE_HTTP=false
docker compose --env-file /dev/null --profile hosted up --build -d gryphon-hosted

Open your canonical origin and log in with the saved admin token. The explicit service target avoids starting legacy HTTP. Compose derives the database URL and starts isolated PostgreSQL 17.6, with no published DB port and separate named PostgreSQL/local-state volumes. The app is non-root, read-only, resource-limited, restricted by default, and has no Docker socket. Secrets are checked when selected services start, not during inactive-profile interpolation. Use a URL-safe DB password; changing an env value does not rotate an initialized PostgreSQL password. Native PostgreSQL deployments supply GRYPHON_SAAS_DATABASE_URL="postgresql://..." separately. For container loopback development only, use origin http://127.0.0.1:8001 and GRYPHON_SAAS_ALLOW_INSECURE_HTTP=true. /health checks DB readiness and requires the canonical Host; it does not test all channels or upstream APIs.

Tenant and channel workflow

  1. Log in at / with the bootstrap admin token and create a tenant.

  2. Import immutable versions: Swagger/OpenAPI JSON/YAML file, OpenAPI URL, or the bounded UCP URL subset below.

  3. Create a channel bound only to that tenant's versions (or none for offline compute). Choose restricted execution; Docker requires operator provisioning.

  4. Generate/rotate its key and copy it once; only the hash is stored. Connect an MCP streamable-HTTP client to https://your-origin/mcp/{channelUUID} with Authorization: Bearer <channel-key> (use your local origin for development).

  5. Manage channels/users, revoke keys, suspend/resume tenants or confirm deletion, and inspect aggregate usage/audit.

Hosted catalog-bound POST operations execute automatically, without separate POST permissions or approval. POST can have side effects; automatic execution does not mean read-only semantics. The Read-only filter defaults checked: ordinary HTTP POSTs are excluded unless already classified by retained exact read attestations; uncheck to include supported POSTs for execution. Native MCP filtering uses the known read-method subset below. Hosted config enables allow_catalog_posts=True but still forces allow_writes=False and empty write permits: PUT/PATCH/DELETE remain denied, even if visible. Unbound/wrong-channel operations, unsupported methods, invalid arguments and network/budget violations remain blocked. Upstream access is public-API-only, without host-auth inheritance, tenant secrets, environment interpolation or caller-selected host paths. Ordinary OpenAPI external refs stay denied; bounded UCP may resolve approved refs. Channels retain independent stores/authority. Channel create/edit offers Include function names and descriptions (include_function_summaries, default false): optional strict boolean on channel create/PATCH; omission on PATCH preserves the saved value. This channel-owned discovery choice overrides the operator base setting; it is not an MCP caller option. Compact mode remains the default; enabled mode includes all function summaries when they fit, otherwise requires bounded continuation (see MCP interface). Admin /api sessions use bounded, in-memory Secure, HttpOnly, SameSite=Strict cookies with session-bound CSRF checks for mutations; restart invalidates sessions. Channel keys cannot administer /api; admin login does not grant MCP access without a separately issued channel key.

Import, inspect and refresh specifications

Navigation uses consistent outlined icons. API specifications shows one compact entry per parent-linked lineage: ellipsized names/source, short version labels and count badges. Version details exposes full IDs, diagnostics/warnings, provenance and bindings for the latest or any historical version, including middle versions through History. Source/download, update-file/refresh-URL, filter, History and delete use accessible SVG buttons with labels, tooltips and keyboard focus. History is read-only, with no individual-version delete. Unrelated same-name roots stay separate. Channel selection prefers latest for new choices and labels existing pinned older versions; grouping is not a stable version ID or in-place overwrite. Browser-session/CSRF APIs (all under /api/tenants/{tenant_id}):

  • POST /specs: file {"name":"cse","content":"<OpenAPI JSON or YAML>"}; URL {"name":"store","url":"https://merchant.example","kind":"ucp"} (or kind:"openapi"). Optional strict boolean read_only_filter defaults true. Never submit a host file path.

  • URLs are at most 2048 characters, without queries, userinfo or fragments. UCP requires HTTPS; a root URL becomes /.well-known/ucp. Fetches use bounded DNS-pinned HTTP, no auth inheritance, redirects, environment interpolation or proxies. Public OpenAPI relative server URLs resolve against the fetched document and are saved in the self-contained snapshot.

  • POST /specs/{spec_id}/refresh: URL source {} refetches its saved URL; file source {"content":"<replacement document>"} requires replacement bytes. Optional strict boolean read_only_filter defaults to the previous version's choice; import/refresh dialogs expose the checkbox.

  • POST /specs/{spec_id}/filter: {"read_only_filter":false,"update_channels":false} revalidates the saved document, without upload or remote fetch, and preserves source provenance. The row's Read-only filter icon opens a confirmation dialog. Both refresh/filter accept optional strict boolean update_channels, false by default in the API; the UI's Update bound channels starts checked.

  • Changed refresh/filter returns 201, creating an immutable successor with parent_id. A filter change counts even for unchanged GET-only bytes and changes catalog/policy identity (not necessarily document SHA-256). On opt-in, only exact old bindings/revisions advance atomically; invalidated runtimes drain before returning. Other settings and old snapshots stay intact.

  • Unchanged document and all import metadata returns 200 with the old ID and no channel revisions, even if updating was requested. Identity includes diagnostics/warnings/filter, legacy grants, mcp_bindings and resolved provenance: raw native schema/endpoint changes create successors even when normalized OpenAPI is unchanged. Superseded updates return 409; use the latest successor. Legacy uploads default to file provenance, filtering on and empty grants; no spec database schema migration. Retained legacy grants survive identical canonical documents and clear on document change; there is no new approval workflow.

  • Diagnostics report total_operations, available_operations, filtered_operations and unsupported_operations. Availability is discovery inclusion, not authorization for every HTTP method. Unsupported ordinary OpenAPI request/schema semantics reject import. Native MCP explicitly omits unsupported tools with warnings; filter-off includes only supported tools, never full commerce by implication. REST UCP retains its GET subset.

  • One active import, no queued imports, with a 25-second import deadline and cancellation cleanup. Existing spec/storage quotas still apply.

  • Delete any specification with the main-row trash icon: this permanently deletes its whole parent-linked lineage, not one version, and detaches every exact latest/older binding. GET /specs/{spec_id}/deletion previews {name,specification_id,spec_id,version_ids,version_count,channels:[{id,name,revision}],confirmation_token}; specification_id is the root, spec_id the requested version. DELETE /specs/{spec_id} requires the closed JSON object {confirm_name,confirmation_token}. Type the stored name exactly, including case/spaces; no trimming or coercion. The dialog starts blank, enables Delete only on exact match and guards stale tenant/session/dialog context. New versions or affected channel/binding changes invalidate consent: 409 requires a fresh preview and retyping, never automatic retry. Malformed consent/bad names return 400; nonexistent/foreign resources 404. The token fingerprints tenant/root/requested ID, all version IDs and affected channel configurations/revisions; it is not an authorization capability. Browser auth/CSRF and fresh scoped authority checks still apply: tenant users only in their enabled tenant; platform admins/bootstrap can also clean up disabled tenants, as with key revocation.

  • Exact name and current fingerprint are checked before mutation in a serialized SQL transaction. All lineage rows/bindings are removed atomically; each affected channel revision advances once with audit, then owned finish_cleanup drains affected runtimes before 200 {deleted:true,specification_id,deleted_spec_ids,updated_channel_ids}. Unrelated same-name roots, channel IDs/keys/other bindings/settings, usage, receipts and artifacts remain; no host-file/cache purge. Stale recipes reject catalog drift; receipts remain under normal retention. spec_deleted records the verified actor and root through optional AdminAudit.spec_id (legacy default None); old audits remain subject to normal bounded retention, not an unlimited archive. Only explicit confirmed lineage or entire-tenant deletion overrides snapshot retention; refresh/filter still never overwrite history. No migration or development deletion of operator data; tests use temporary stores. Notifications have a close button and 10-second auto-dismiss; replacement messages restart the timer. Inline dialog errors remain after banner dismissal; quiet sign-out clears pending notifications.

UCP is a bounded REST/native MCP adapter, not full commerce or a platform identity provider. Published 2026-01-11, 2026-01-23, 2026-04-08 and 2026-08-25 profile shapes are recognized. Supply a root, JSON profile URL, or explicit /mcp endpoint. Roots and explicit MCP routes probe the same-origin /.well-known/ucp first; a matching advertised shopping REST binding is preferred, otherwise MCP; malformed advertised contracts fail closed. Only explicit MCP routes fall back directly when the profile is unavailable. No arbitrary redirects or HTML links are followed. Public profile-advertised delegations are allowed subject to GRYPHON_ALLOWED_DOMAINS and DNS/TLS policy at every step. REST remains the original subset: matching advertised GET IDs get_checkout, get_cart, get_order (January: checkout only), with schema-defined paths. Required UCP-Agent/Request-Id remain caller-supplied; required auth/signing rejects, including canonical January signing. Unsupported REST response schemas are omitted with explicit warnings, not claimed validation. No broad REST write adaptation or extension composition is added. Native MCP import performs initialize → notifications/initialized → paginated tools/list, never business tools/call. The filter recognizes six read methods: get_checkout, get_cart, get_order, search_catalog, lookup_catalog, get_product (catalog.lookup). Unknown/non-read tools are hidden with filtering on; off includes supported native non-read tools for automatic execution, potentially with side effects. Unsupported native schema constraints (including patterns, combinators and refs) omit the entire tool with warnings rather than silently weaken validation. Preserve all required arguments, including meta.ucp-agent.profile, inside json_body; Gryphon does not generate a real platform identity or inherit auth secrets. For native MCP, the operator may set GRYPHON_UCP_AGENT_PROFILE (default unset/None) to their real, publicly fetchable platform profile, not a fabricated URL or the merchant's profile as default. It must be public HTTPS, at most 2048 characters, without userinfo/query/fragment, quotes/control/unsafe escapes, interpolation or prohibited IP/metadata addresses. Structure/current GRYPHON_ALLOWED_DOMAINS policy are checked at initialization/use, and all DNS answers must be public at use, even if private networks are enabled. Gryphon does not fetch this identity document or prove it works; the operator must verify it. Never overwrite an existing real operator environment during setup/development. Only bound native tools with the entire profile path required in schema receive omitted meta/ucp-agent/profile defaults. Explicit empty/invalid values are never overwritten; a valid explicit body profile wins with a matching UCP-Agent: profile="URI" header on native session requests, including initialization/discovery/owned cleanup as applicable. Ordinary OpenAPI receives no injection. get_functions adds parent ucp_agent_profile metadata (required, operator_configured, input_path, guidance) and an input-based usage example, never the configured URI or an empty-profile example. Changing the configured URI changes policy identity for recipes/receipts. Saved source_url remains the original input; resolved_profile_url, resolved_endpoint and source_transport record resolution separately. Trusted mcp_bindings store the native name, endpoint and raw input/output schema fingerprint outside the untrusted OpenAPI document. View/download is the compiled catalog document, not a portable transport authorization: synthetic /__mcp__/... POST paths are never actual upstream routes. Refresh rediscovers metadata; saved filter changes need no remote fetch. Each native invocation starts a fresh session, rediscovers tools and checks the raw name/input/output fingerprint before one tools/call; stale bindings return conflict and require refresh. No side-effect retries. JSON/SSE replies must match request IDs, with notification/session bounds. structuredContent is preferred; one finite JSON text block is decoded, while non-JSON/multimodal content blocks are retained without resource fetching. Known SDK handshake versions are negotiated (2025-11-25 proposal, compatible 2025-03-26 selection), not a claim of Tasks, modern server discovery or complete authentication support. Discovery permits at most 1000 tools / 100 pages, 32 REST schema documents, and aggregate raw response bytes bounded by hosted/configured spec limits and 5 MiB. MCP discovery has a min(HTTP timeout, 30 seconds) deadline within the outer 25-second import limit; invocation initialize/list/call share the caller deadline and response-byte cap. Verified owned sessions may receive one fixed-endpoint cleanup DELETE under a separate 2-second / 1-KiB nonfatal budget; cancellation awaits owned local cleanup. This protocol cleanup is not authorization for arbitrary DELETE operations. REST refs stay on the approved schema origin (profile origin, https://ucp.dev, or operator-approved), with structural/expansion limits. The reported https://coolbudget.lk/api/ucp/mcp GET redirects to canonical WWW whose route returns HTML 404; following that redirect is not the solution. Its same-origin profile advertises https://qhhihh-tw.myshopify.com/api/ucp/mcp. Metadata-only import now resolves it, including compatibility with initialized ACK 200 {}: 13 discovered tools, six reads, two supported cancel tools hidden by default, five unsupported schemas omitted. This is not full commerce validation; no live business or payment calls were used. UCP discovery failures return useful HTTP 400 ucp_discovery diagnostics, not POST-approval instructions.

Users, passwords and workspace lifecycle

After bootstrap login, open Users → Create user and set a name, username, password and fixed role. Create/enable the tenant before assigning a tenant user. Keep the bootstrap token as recovery access; named users sign in with username/password.

Role

Control-plane access

platform_admin

All tenants; create/list users, edit names, suspend/enable accounts, reset passwords and confirmed user/tenant deletion

tenant_user

Exactly one immutable, enabled tenant; its specs, channels (including confirmed deletion), keys, usage, analytics and scoped audit only

Tenant users cannot create tenants/users, list/manage users, change roles/membership or administer another tenant. There is no self-signup. Change password requires the current password; sign in again afterward. Passwords use salted PBKDF2-HMAC-SHA256, 600,000 iterations (12–128 characters). Resets, profile/status changes and tenant status revisions invalidate affected sessions; re-enabling never revives old cookies. Roles/tenant assignments cannot be edited. Browser/platform authentication never replaces channel-key authentication for MCP. Compact accessible SVG actions provide user name/status/password/delete, channel actions and Tenant controls, with labels, tooltips and keyboard focus. Suspend tenant / Resume tenant uses PATCH /api/tenants/{tenant_id} with strict boolean {enabled:false|true}: reversible access blocking, not deletion. Data and channel keys remain; resuming allows enabled channels' existing valid keys again, but tenant users must sign in again. After a fresh platform check, owned cleanup completes status/session updates and attempts every channel's revision-cutoff invalidation before success, even if one cleanup fails.

  • Delete tenant: type the exact tenant name to permanently remove the entire workspace, including all assigned tenant users/password hashes, channels/key hashes/bindings, all specification versions and scoped usage/analytics. All workspace endpoints/keys stop working; other tenants and platform accounts remain. Suspend is the reversible alternative.

  • Delete channel: type the exact channel name. Only that scoped channel, its key, bindings, usage/analytics and dependent control rows are removed. Other channels, tenant users and specifications remain; its MCP endpoint stops accepting access.

  • Delete user: a platform administrator types the exact username, not display name. Deleting yourself or the last enabled platform administrator returns 409; bootstrap also cannot delete the last enabled administrator. Use another administrator for self-offboarding; create/enable another before deleting the last enabled administrator. User deletion revokes only that browser account and its sessions; independently issued shared channel keys remain valid. Offboarding must also rotate/revoke exposed channel keys. Disable/reset likewise does not revoke channel keys; shared channels/specifications remain.

  • Resource paths are /api/tenants/{tenant_id}, /api/tenants/{tenant_id}/channels/{channel_id} and /api/users/{user_id}. GET <resource>/deletion returns {kind,id,name,label,confirmation_token,impact} (tenant previews also include child IDs); impact counts users, channels, specs (versions), and user name is the username while label is the display name. DELETE <resource> accepts only {confirm_name,confirmation_token}. Exact stored name/username and current public-state fingerprint are checked before mutation in serialized SQL: no trimming, case folding or coercion. Tokens are consent fingerprints, never authorization.

  • Changed configuration/revision/bindings or tenant children invalidates consent: 409 requires a fresh preview/token and retyping from blank, never automatic retry. Malformed consent/wrong name returns 400, missing/foreign scoped resources 404, forbidden platform actions/CSRF 403, invalid sessions 401, according to existing authorization checks. Browser auth/CSRF and fresh server-side scope checks remain mandatory: tenant/user deletion is platform-only; channel deletion permits enabled own-tenant members or platform admins/bootstrap, including platform cleanup of disabled tenants. Dialogs guard stale session/tenant/context and gate Delete on exact match.

  • Deletion commits control rows/audit atomically, freeing corresponding quotas. Owned cleanup then revokes deleted users' sessions with cutoff user.revision + 1 and invalidates channels using deleted snapshots with before_revision=deleted.revision + 1, covering every revision through deletion, including cold snapshots—not unconditional None invalidation. Every cleanup is attempted before 200 {deleted:true,tenant_id|channel_id|user_id}, even if one fails; failure is not success. Hosted runtime acquisition validates the exact enabled database snapshot under its lock before cached/new acquisition and after startup; stale/deleted authority rejects with owned failed-start cleanup. Completed hosted revocation markers are forgotten under that lock, preventing unbounded deleted-ID growth; standalone managers without a validator retain legacy watermarks. Gateway key checks remain mandatory; database mutations commit before acquiring the runtime-manager lock, never wait for it while holding a database transaction. Recreated usernames get new UUIDs; historical actor IDs never resolve by username.

  • Local sandbox/cache/recipes, execution receipts and artifacts are not automatically erased. For tenant/channel deletion they remain private on disk but inaccessible through removed endpoints. This is not secure erasure, backup deletion, undo of upstream effects or HA; protect retained local state/backups separately. Audit separates actor from subject: nested actor has id, username, name, kind (user/bootstrap/system/unknown) and display_source (snapshot/current/unknown). Resource and user-deletion events retain saved public actor snapshots; legacy account events resolve current names by actor ID only, labeled Current account name, or unknown after deletion—never invented historical names or inferred bootstrap. An additive saas_audit_archive preserves affected history while retaining existing live foreign keys/CHECK constraints, without rebuilding tables. Live + archive together share normal 10,000-event retention per category (resource/user), not unlimited retention. Only authorized scoped events expose actors, never a tenant-accessible user directory.

Tenant analytics: measured value, not billing

Select Analytics, a 7/30/90-day UTC window and optionally a channel; Download raw JSON includes methodology and window.tenant_id / window.channel_id (null for all channels). Browser API: GET /api/tenants/{tenant_id}/analytics?days=7&channel_id=<id>; days accepts 1–90 (default 7), channel_id is optional and tenant-checked. Existing browser roles apply: platform admins can select any tenant; tenant users see only their enabled tenant. Channel keys cannot read this API.

Evidence

Meaning and limits

Canonical payload bytes

Paired successful eligible API-backed runs: accepted API JSON before code reduction versus full final JSON, including artifact data, not inline summaries.

Signed payload reduction

100 × (sum upstream − sum final) / sum upstream over those same pairs. Negative means expansion; no API baseline is null / N/A, never 100%.

Actual code reuse

Recorded replay backend starts / all recorded backend starts, separate from failed replay requests. UI shows request/run error categories; cache errors span all requests, not an exact cache-miss versus storage-failure count. Idempotent duplicates do not add runs.

Source/workload

UI shows source bytes/static lines and top-level JSON-array item totals, not executed instructions or semantic records. reused_source_bytes is code executed again, not measured LLM generation avoided.

Broker activity

api_calls counts broker attempts, including some blocked before HTTP dispatch; api_responses counts accepted validated returns only. Pure compute means a backend start with zero broker attempts.

Traffic

Observed request-body bytes and SDK-produced response-body bytes for allowlisted tools/call names—not proven client reception/model consumption. structured_payload_bytes counts canonical structured output once.

Token equivalents

UI separates ESTIMATED wire and structured-payload equivalents: ceil(UTF-8 bytes / 4), not a tokenizer. Round per observed request field / comparable run then sum; reused-source estimate rounds the total.

Timing

UI shows queue/backend averages and response-production latency, excluding its own observation persistence—not client end-to-end latency. Response-production/run histogram p50/p95 are upper bounds, not exact percentiles. Wall times overlap, not CPU/time saved; backend includes network; broker wraps credentials/dispatch/response checks, not earlier argument/policy guards; queue includes preparation.

“Original”/“raw API” means broker-accepted, validated, decoded canonical JSON, not raw HTTP response bodies or an LLM-without-Gryphon counterfactual. Traffic allowlists 13 tool names (eleven core/two optional); SDK-rejected known names can count as errors. Discovery, metadata, get_run polls, artifact reads and transformations count; exclusions include initialization, tools/list/SDK negotiation, HTTP headers and agent context. Wire bytes include duplicate structured/text representations; only the structured-payload counter counts that payload once. Artifact projection is execute-origin pure compute, not replay or a new API-backed reduction baseline. Tool definitions and all client context are not claimed to fit or be counted by Gryphon's response/context budgets. Wire token estimates are not actual model tokens. Actual model context, generation, reasoning and billing are unobservable (null / N/A); no dollar, CPU, time or round-trip savings are guaranteed. Request count is not run count.

Terminal observation includes background submissions when they finish, even without polling; failures/cancellations remain separate. Observation is best effort: crashes/failures can miss records, not a complete billing audit. Legacy usage/request metrics attempt persistence before final body handoff (two-second timeout per writer); failures are nonfatal, incomplete SDK responses use a fallback observation. Daily bounded aggregate JSON covers 90 UTC days; recording_since marks the first measurement, with no lifetime backfill. At most 100,000 deduplication receipts per tenant are retained; other tenants cannot evict in-retention receipts. Deduplication lasts only while retained; total storage is bounded by the hosted 100-tenant quota.

For marketing, replace placeholders only with the selected report's evidence: “Across N successful paired API-backed runs in UTC window [start, end), canonical payload reduced X%, with R% of recorded backend starts using cached code. Tokens are estimated, not billing, and exclude agent context.” Report expansion honestly, not as savings. Operational success/reduction do not establish answer correctness or equivalent task quality. Review exports for activity metadata.

Restart, upgrade, leases and backups

Normal startup adds user/account-audit, analytics and saas_audit_archive tables, preserving existing live foreign keys/CHECK constraints, tenants, uploads, channels, keys and configuration without table rebuilds. Stop and back up first; no separate migration command is required. Never test schema upgrades/development migrations against an operator's real database.

Exactly one active SaaS process per control database. Leases are automatic; no lock installer or separate service. Stop the existing server with Ctrl+C before starting another; never delete .lock files to release live workers/jobs. PostgreSQL uses a session-level advisory lock: direct connection or session pooling, not transaction pooling. Local per-channel run ledgers retain exclusive POSIX locks.

PostgreSQL stores control metadata, immutable specs, key hashes, aggregate usage/analytics and audit—not execution recipes, run receipts or artifacts. Back up/restore the database and private local state volume together while the worker is stopped. Protect disks, backups and secrets; test restoration. TLS, monitoring and incident response are operator responsibilities. This is not HA/horizontal SaaS, billing, SSO, user invitations, encryption-at-rest assurance or security certification. See SECURITY.md.

Install a built package or release through GitHub

Checkout installs work today. To install a locally built wheel into a Python 3.13+ virtual environment (the second command selects hosted dependencies):

uv build
python -m pip install /absolute/path/to/gryphon/dist/gryphon_runtime-2.0.0-py3-none-any.whl
python -m pip install '/absolute/path/to/gryphon/dist/gryphon_runtime-2.0.0-py3-none-any.whl[saas]'

The distribution is gryphon-runtime, not gryphon. Only after version 2.0.0 has actually been published to PyPI, use uvx --from gryphon-runtime==2.0.0 gryphon stdio or python -m pip install gryphon-runtime==2.0.0. An MCP entry can then use your absolute uvx executable, args ["--from", "gryphon-runtime==2.0.0", "gryphon", "stdio"], and the same GRYPHON_SWAGGERS environment (provide your own spec file or public HTTPS spec URL). Hosted package installs use gryphon-runtime[saas]==2.0.0. These are conditional instructions, not a publication claim. See CONTRIBUTING.md for maintainer release setup and quality gates.

The MCP interface

Core tool

Purpose

list_servers

Compact, paginated API summaries

search_functions

Discover capabilities without loading the whole catalog

get_functions

Inspect schemas/invocation metadata for 1–5 functions

execute_code

Run code with structured inputs and optional input_schema

run_cached_code

Reuse exact source with a complete new params object

submit_code

Submit work and receive a persistent run receipt

get_run / cancel_run

Poll caller-owned work / revoke and await cleanup

list_recipes

Search caller-owned cached recipe summaries

read_artifact

Read caller-owned JSON in bounded chunks

transform_artifact

Reduce an owned saved artifact offline; no upstream calls or replayable recipe

reusable_code_guide explains execution on demand. Optional GRYPHON_ENABLE_ADDITIONAL_TOOLS=true adds only list_skills and get_server_skills in local/operator mode. Guides are bounded, untrusted data, not write approval. Discovery includes fingerprints/truncation; follow continuation or narrow searches. MCP readOnlyHint is not authorization. list_servers stays compact by default in legacy serve/run and hosted channels. Local stdio defaults to include_function_summaries=true, so list_servers rows include functions with names/descriptions (all when they fit—not a fixed sample); set GRYPHON_INCLUDE_FUNCTION_SUMMARIES=false to opt out. Hosted channels choose include_function_summaries per channel (default false); local/operator serve/run can opt in with GRYPHON_INCLUDE_FUNCTION_SUMMARIES=true. When enabled, larger collections paginate within response budgets: pass both next_cursor and next_function_cursor back as cursor and function_cursor; a nonzero function cursor continues the same server. limit counts servers, not functions; compact mode requires function_cursor=0. Oversized text is marked truncated. Restart pagination if the registry fingerprint changes; use get_functions for schemas.

Execute, then reuse

Search for weather and inspect with get_functions: {"functions":[{"server_name":"weather","function_name":"get_forecast"}]}. This execute_code request actually calls the public API:

{
  "code": "result = await call_tool(\"weather.get_forecast\", {\"latitude\": inputs[\"latitude\"], \"longitude\": inputs[\"longitude\"], \"current\": \"temperature_2m\"})",
  "description": "current weather by coordinates",
  "inputs": {"latitude": 51.5074, "longitude": -0.1278}
}

The restricted capability takes two arguments: await call_tool("server.function", arguments). Pass the returned cache_id to run_cached_code with a complete new params object: {"cache_id":"<returned-cache-id>","params":{"latitude":48.8566,"longitude":2.3522}}. Inputs are never rewritten into source. A recipe caches code, not API results. Assign JSON-native result; main() is neither required nor auto-called. Restricted Python has no imports, filesystem, direct network or host environment. Optional bounded input_schema rejects references, regexes and combinators ($ref, pattern, allOf, anyOf, oneOf). Recipe identity binds owner, exact source, schema and catalog/policy digest; replay revalidates and rejects drift. Reordering/duplicating exact write permits does not change identity; changing the set does.

UCP search, then reduce the saved result

Before launching/restarting Gryphon, have the operator supply their real fetchable platform profile. The following URL is a placeholder, not a working identity; replace it, and preserve any existing real setting:

export PUBLIC_PROFILE='https://your-platform.example/.well-known/ucp'
export GRYPHON_UCP_AGENT_PROFILE="$PUBLIC_PROFILE"

After importing/binding the merchant UCP source as shop, inspect shop.search_catalog with get_functions; confirm the actual native schema and ucp_agent_profile.operator_configured (no URI is exposed). doctor also reports only ucp_agent_profile_configured, not fetchability.

  1. Send execute_code with catalog inputs only; omitted required identity is filled by the broker:

{"code":"result = await call_tool('shop.search_catalog', {'json_body': inputs})","description":"Search catalog","inputs":{"catalog":{"query":"bedsheets"}}}
  1. If response.truncated and response.artifact_id are present, inspect response.data.json_type / top_level_keys and send transform_artifact using that returned ID. This synthetic shape example assumes an object with products containing title; adapt to the actual inspected shape, not every merchant's response:

{"artifact_id":"<response.artifact_id>","code":"products = inputs['artifact']['products']\nresult = {'count': len(products), 'titles': [p['title'] for p in products[:inputs['params']['take']]]}","description":"Summarize saved search","inputs":{"take":5}}

This reduces already saved JSON without a second upstream call or assembling, for example, 21 read_artifact chunks. Normal run_cached_code is different: it reruns the original recipe and refetches. tests/integration/test_ucp_search_workflow.py exercises this with a synthetic peer, not a live merchant search verified this cycle. The user's report that a fetchable profile worked does not make the merchant profile our configured platform identity; earlier live evidence above remains metadata-only.

Results, receipts and cancellation

Native structured results contain success, result data, available handles and stable error_type categories, including sandbox_unavailable. Failure messages come from a finite Gryphon-owned catalog; public results and nested receipts preserve only freshly validated diagnostics, never raw error text or forged extra fields. Missing/invalid native profiles and the exact known RPC condition return error_type:"upstream", static configuration guidance and diagnostic:{kind:"upstream",phase:"discovery"|"invoke",upstream_code:"invalid_profile_url"}. Other well-formed RPC errors expose phase only, never raw messages, data.content, continue_url, numeric codes or private bodies. AST rejection remains error_type:"security", with diagnostic:{kind:"ast",violation_type:<closed enum>,line:1..1000000}; imports/calls/attributes/global/nonlocal remain guarded. No source, detail, prints, stderr or traces are exposed. Large permitted JSON becomes owner-scoped artifacts. Summary data includes json_type; objects add top_level_keys (at most 32 complete keys / 512 serialized bytes, fewer under small budgets), key_count, keys_truncated; arrays add length, never value previews. Prefer transform_artifact for local reduction. read_artifact remains available: follow next_offset to eof, at most 8192 bytes per chunk, subject to context/full-result limits. transform_artifact exposes saved JSON as inputs['artifact'] and caller parameters as inputs['params']. It uses a fresh restricted Monty VM with no external functions, broker reference or network; call_tool references/aliases and imports are blocked. Owned integrity-checked loading is bounded/off-loop, with normal AST/resource limits, admission, local/shared slots, deadline, cancellation and run ledger. Docker configurations reject without downgrade. Results can become artifacts again; receipts remain, but projections return no replayable cache_id. submit_code returns an id; poll get_run(run_id=...). States: queued, running, succeeded, failed, cancelled, interrupted. Optional owner-scoped idempotency_key deduplicates matching requests only while retained; conflicts fail. Restart marks queued/running work interrupted, with no automatic replay, crash resume or exactly-once effects. Inspect upstream state before retrying. Cancellation revokes authority and awaits cleanup but cannot undo accepted API actions. Receipts/artifacts are not a workflow engine or permanent audit archive.

Configure APIs and policy

Local/operator YAML uses servers: [{name: weather, swagger_url: ./examples/weather.yaml, is_read_only: true}]. swagger_url accepts local files or policy-approved HTTP(S) documents; relative paths use the compilation directory. base_url overrides the spec URL. OpenAPI 3.0/3.1 and Swagger 2.0 produce deterministic v2 manifests; 3.2 is unsupported. Unsupported request semantics fail closed; unsupported response schemas are reported/omitted, not falsely validated. Supported responses are validated. Optional query/header null means omission; required/path null and null query-array elements fail. Declared JSON-body null works. Generated Python is SDK documentation, never host-executed; top_level_functions promotes no MCP tools. LLM enhancement (--llm-enhance / GRYPHON_LLM_ENHANCE=true) is retired and explicitly rejected.

Credentials and writes

Public APIs need no auth block. Local/operator mode supports trusted source auth such as auth: {type: static, value: "Bearer ${UPSTREAM_API_TOKEN}"}; export real credentials only to the broker process, never commit them. Host auth supports static, jwt, basic, OAuth2 client credentials (oauth2), keycloak and session, with refresh. Alternatives: GRYPHON_{SERVER}_AUTH and JSON GRYPHON_{SERVER}_EXTRA_HEADERS. Neither sandbox receives credentials. Hosted channels cannot use these credentials; public upstream APIs only.

is_read_only: true filters ordinary HTTP writes at compilation and dispatch; retained exact read-only POST classifications remain compatible. Legacy serve/run default GRYPHON_ALLOW_CATALOG_POSTS false; local stdio defaults true (like hosted channels), so included catalog POSTs execute—set GRYPHON_ALLOW_CATALOG_POSTS=false to opt out. Ordinary PUT/PATCH/DELETE writes require a write-enabled source, GRYPHON_ALLOW_WRITES=true and exact GRYPHON_ALLOWED_WRITE_OPERATIONS permits. Enabling catalog POSTs never bypasses an unclassified read-only source. SaaS enables catalog POSTs on a private config copy, not by changing operator settings, while PUT/PATCH/DELETE remain denied. No model flag, guide, idempotency key or hint grants authority.

Automatic catalog POSTs and CSE forms

For hosted POST APIs, import with Read-only filter unchecked, bind the version to the channel, inspect its canonical functions, then execute: no separate POST permissions step. Included supported POSTs can mutate upstream state; names and uploaded hints do not prove read semantics. The original CSE document has 1 GET + 25 POST (23 URL-encoded + 2 scalar multipart); document-only parsing exposes all 26 with filtering off. Synthetic CSE-shaped calls/replay use real Monty and mocked HTTP, not live CSE calls or proof every endpoint works. The manual POST read permissions UI and public route are removed: GET and POST /api/tenants/{tenant_id}/specs/{spec_id}/post-reads return 404. Legacy approved_post_reads metadata/helpers remain for old payload compatibility, and removal of that approval workflow does not delete historical post_reads_updated audits or immutable versions. Audits remain under normal bounded retention; versions remain unless explicitly confirmed whole-lineage or entire-tenant deletion is requested. There is no replacement approval UI or route. GRYPHON_ALLOWED_READ_ONLY_POST_OPERATIONS remains an optional empty-default operator compatibility setting: exact canonical server/effective base/literal path/POST tuples, checked by parser/broker and included in policy identity. It is deployment-wide, not credentials or tenant authorization, and is not required for included hosted POSTs. Example tuple shape: {"server_name":"cse","base_url":"https://market.example/api","path":"/companyInfoSummery","method":"POST"}. No templates/globs or automatic operator-env edits. For source cse, use namespace cse, not fixture cse_api. Use list_servers, search_functions and get_functions to inspect canonical names, schemas, method/path and effective destination. After inspection, the two-argument shape is:

result = await call_tool("cse.get_company_info_summery", {"json_body": {"symbol": inputs["symbol"]}})

The illustrative name must match the channel catalog. URL-encoded and multipart forms accept a closed json_body object of scalars/scalar arrays (repeated fields); nested/null/binary/file values reject. The broker chooses encoding; multipart never reads host files or emits caller-selected filenames, with 1024 parts / 2 MiB bounds. Automatic POSTs still enforce source/catalog/channel scope, schemas, DNS/TLS, deadlines and byte/call budgets.

Runtime settings

See .env.example, GryphonConfig, and SaaSConfig. Explicit environment wins over dotenv. Legacy compile/serve/run discover working-directory .env with checkout-root fallback; stdio and saas load only explicit --env-file. Defaults: restricted VM, 30-second deadline, 64,000,000-byte memory, four active runs shared across hosted channels, five-second admission wait, 50 calls; 65,536-byte source/inline, 2,097,152-byte full-result and 16,384-byte discovery budgets, ten items/page. Recipes: 3600 seconds/500 entries; receipts: 86400 seconds/1000 entries; artifacts: 100 per owner. Local run-ledger recovery is stale-scoped at GRYPHON_RUN_RECOVERY_STALE_SECONDS (default 600); a stdio server reaps itself after GRYPHON_STDIO_IDLE_TIMEOUT_SECONDS (default 1800; 0 disables). Configure through GRYPHON_*. Public API/auth/spec destinations require HTTPS; GRYPHON_ALLOWED_DOMAINS is an additional exact-host JSON array, not wildcard/CSV authority. Private/loopback HTTP needs GRYPHON_ALLOW_PRIVATE_NETWORKS=true and approved DNS answers. Metadata, link-local, reserved and mixed public/private destinations stay denied. DNS pinning, original Host/SNI, origin-isolated pools, verified TLS, no environment proxies/redirects and response bounds remain mandatory.

Legacy operator HTTP and maintenance

For existing file-based local/operator deployments, copy .env.example and config/swaggers.yaml.example only into new private configuration files; never overwrite existing configuration. Review before gryphon compile. Successful standalone compile prints non-secret MCP client JSON even when unchanged, pinning absolute storage/env-file paths and disabling recompilation. Dry runs, failures, empty/startup compilations emit no client JSON; client config is never automatically changed or populated with credentials. serve respects GRYPHON_COMPILE_ON_STARTUP; run compiles once then serves. --env-file works before or after the subcommand.

export GRYPHON_HTTP_AUTH_TOKEN="${GRYPHON_HTTP_AUTH_TOKEN:-$(uv run --frozen python -c 'import secrets; print(secrets.token_urlsafe(48))')}"
uv run --frozen gryphon serve --transport http

Connect to http://127.0.0.1:8000/mcp with Authorization: Bearer <your-token> (at least 32 characters). The fixed owner is operator, not a tenant/user identity; sharing this token shares ownership. No web UI. Public exposure requires TLS and operator controls. After preparing file-based config/token, docker compose up --build -d gryphon starts legacy HTTP: loopback port 8000, private named volumes, read-only config, no Docker socket. Its TCP check is not authenticated readiness or hosted /health.

gryphon --version reports the version; doctor emits read-only safe JSON, including boolean ucp_agent_profile_configured, without compilation, store opens, API calls or Docker probing; it never reports the configured profile URI or proves fetchability. compile --dry-run validates without output writes but may fetch remote specs. Stop before clean --yes: it archives recognized compiled output/closed recipe caches to adjacent .gryphon-archive-... paths, retaining runs/artifacts/config. clean --yes --dry-run only validates; clean compile --yes archives then compiles. Links, unknown files, overlapping paths and SQLite sidecars are refused. Restore manually while stopped without overwriting newer data; this is not arbitrary deletion or hosted backup.

Optional offline full Python

Docker is only for offline CPython using sandbox/requirements.txt. Provision daemon, image (docker build -t gryphon-sandbox:2.0.0 sandbox/) and gVisor runsc yourself; select GRYPHON_SANDBOX_MODE=docker. Missing requirements fail closed, never autostart/downgrade. Runs use UID 1000, dropped capabilities, no-new-privileges, read-only root, bounded tmpfs, 64-PID/256-MiB RAM-and-swap/half-core limits; no host mounts, network, credentials or call_tool. AST checks still apply. Hosted Docker requires GRYPHON_SAAS_DOCKER_ENABLED=true and manual daemon access outside Compose. GRYPHON_SANDBOX_ALLOWED_IMPORTS is a JSON list: [] denies imports; unset preserves offline defaults. Only approved preinstalled imports, never arbitrary installs.

Development and support

See CONTRIBUTING.md for locked Ruff, strict mypy, pytest/pre-commit commands, optional PostgreSQL/Docker checks and release instructions. The 90% coverage floor is mandatory; target 100%. Normal tests need no live upstream, Docker or PostgreSQL but import the saas extra. The offline real-MCP demo is uv run --frozen python examples/demo.py (use isolated config/stores). UI state tests: node --test tests/unit/specifications_ui.test.js; node --check src/gryphon/static/admin.js; node --check src/gryphon/static/specifications.js; node --check tests/integration/browser_ui.cjs. With separately provisioned Puppeteer/Chromium, opt in to uv run --frozen --extra saas python tests/integration/browser_ui_fixture.py for disposable browser checks (compact details/action tooltips, grouped read-only history, exact-name whole-lineage deletion/stale-context guards, absence of POST approval controls, actors, filters, bindings, channel summaries, notifications). Deletion suites: uv run --frozen --extra saas pytest tests/unit/test_saas_spec_delete.py tests/integration/test_saas_spec_delete_http.py; see AGENTS.md for prerequisites and targeted automatic-POST/native-MCP/audit suites. These commands are verification instructions, not a claim that pending full gates have passed. Lifecycle checks: uv run --frozen --extra saas pytest tests/integration/test_admin_lifecycle.py tests/integration/test_saas_user_delete_http.py tests/unit/test_saas_resource_delete.py tests/unit/test_saas_resource_delete_audit.py tests/unit/test_saas_user_delete.py tests/unit/test_saas_audit_archive.py tests/unit/test_saas_runtime_validation.py tests/unit/test_saas_lifecycle_cleanup.py; node --test tests/unit/lifecycle_ui.test.js; node --check src/gryphon/static/lifecycle.js; node --check tests/integration/browser_lifecycle.cjs. Opt-in GRYPHON_TEST_POSTGRES=1 uv run --frozen --extra saas pytest tests/integration/test_saas_postgres.py covers user/channel/tenant deletion and archives on disposable PostgreSQL 17.6. The disposable browser fixture above exercises lifecycle actions, exact-name consent and offboarding warnings; commands do not claim browser/full gates passed. For offline measurements including receipts, run uv run --frozen python examples/benchmark.py --iterations 25 --concurrency 1; these are not model/API or end-to-end agent benchmarks. See AGENTS.md, CHANGELOG.md, and ROADMAP.md. Report bugs publicly and vulnerabilities privately. MIT licensed; see LICENSE.

Available Tools

11 tools
cancel_runCancel RunA

Request cooperative cancellation; completed external effects cannot be undone.

ParametersJSON Schema
NameRequiredDescriptionDefault
run_idYesCaller-owned persistent run identifier.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.6/5.0
Behavior4/5

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

Annotations only provide readOnlyHint=false, so the description carries the burden of behavioral disclosure. It adds meaningful context: cancellation is cooperative and completed external effects cannot be undone. This is useful nuance beyond the annotation.

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 a single concise sentence that front-loads the action and then attaches the critical caveat. Every word 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?

For a one-parameter tool with an output schema and a readOnlyHint=false annotation, the description covers the key behavioral caveat. It does not specify what happens if a run has already completed or how the result is reported, but the output schema likely covers the latter.

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

Parameters3/5

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

Schema description coverage is 100%, and run_id is already described as a 'Caller-owned persistent run identifier'. The tool description adds no extra parameter meaning, so the baseline of 3 applies.

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 states a specific action, 'request cooperative cancellation', and the title/name identify the resource as a run. It adds useful nuance about completed external effects being irreversible. It does not explicitly contrast with sibling tools, but the purpose is still unambiguous.

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?

No guidance is given on when to use this tool versus alternatives such as get_run, or when cancellation may be inappropriate. The caveat about completed effects implies some caution, but it does not explain conditions or provide routing.

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

execute_codeExecute CodeB

Run restricted Python with inputs, result, and await call_tool("server.function", args).

ParametersJSON Schema
NameRequiredDescriptionDefault
codeYesPython program assigning result; no host filesystem or network access.
inputsNoStructured dynamic values available as inputs inside the VM.
descriptionYesGeneric reusable operation description, without input values.
input_schemaNoOptional JSON Schema constraining inputs for execution and reuse.
idempotency_keyNoOptional owner-scoped deduplication key, not write approval.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.2/5.0
Behavior3/5

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

Annotations only provide readOnlyHint:false, so the description adds value by stating the Python environment is restricted and that call_tool is available for server function calls. However, it does not disclose potential side effects, failure modes, or execution limits, which are relevant for a code-execution tool.

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 definition is a single compact sentence that leads with the core action and includes the distinctive tool-call capability. It is efficient, though the phrase 'with inputs, result' is telegraphic and slightly awkward.

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

Completeness3/5

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

With an output schema present and complete parameter documentation, most invocation details are already covered. However, the description omits usage guidance relative to sibling tools and leaves side-effect semantics implied rather than explicit, making it adequate but not complete for reliable selection.

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

Parameters3/5

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

The input schema provides 100% coverage of the five parameters, so the baseline is 3. The description's references to 'inputs' and 'result' loosely align with the inputs and code parameters, but it does not add meaningful detail beyond the 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 clearly states the tool runs restricted Python and mentions the distinctive capability to await call_tool for invoking server functions. It names a specific verb and resource, though it does not explicitly contrast itself with siblings like run_cached_code or submit_code.

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 gives no guidance about when to use execute_code versus alternatives such as run_cached_code or submit_code. It does not state any conditions or exclusions, leaving tool selection to inference.

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

get_functionsGet FunctionsA
Read-only

Inspect real parameter, request-body, and response metadata for 1–5 functions.

ParametersJSON Schema
NameRequiredDescriptionDefault
functionsYesItems containing only server_name and function_name.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.6/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=true, so the read-only nature is covered. The description adds useful behavioral context by specifying the supported batch size (1–5 functions) and the type of metadata returned, but it does not go beyond that.

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?

A single, front-loaded sentence packs the operation, resource, and batch limit with no redundant words. Every element 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?

With a complete input schema, a true readOnly annotation, and an output schema present, an agent has what it needs to make a correct call. The only missing context is guidance for choosing this tool, which is covered by the usage_guidelines score.

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

Parameters3/5

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

Input-schema description coverage is 100%, which already documents the array-of-objects shape and the 'only server_name and function_name' constraint. The description adds the 1–5 count limit, but otherwise does not elaborate on parameter semantics, so the baseline 3 applies.

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 uses a specific verb ('Inspect') and names an exact resource: real parameter, request-body, and response metadata for functions. It is clearly distinct from siblings like search_functions or list_servers, which are about discovery rather than metadata lookup.

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 the tool is for inspecting metadata, but it never states when to choose it over search_functions or other siblings, nor does it describe when not to use it. No explicit preconditions, alternatives, or exclusion criteria are provided.

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

get_runGet RunA
Read-only

Read a persistent run receipt owned by the authenticated caller.

ParametersJSON Schema
NameRequiredDescriptionDefault
run_idYesHandle returned by submit_code or execute_code.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

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

Annotations already mark readOnlyHint=true, and the description does not contradict that. It adds useful behavior beyond the annotation: the receipt is 'persistent' and scoped to 'the authenticated caller,' clarifying retention and access boundaries. It could mention error behavior for invalid run_id, but the readOnly annotation and output schema cover much of the safety profile.

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?

A single ten-word sentence, front-loaded with the action and object, with no filler. Every phrase ('persistent,' 'owned by authenticated caller') adds meaning.

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 one-parameter read tool, the description plus full schema and output schema is nearly complete; it tells the caller the receipt is persistent and access is caller-scoped. It is slightly thin on when to call it and what 'receipt' entails, but the schema's run_id description ties it to submit_code/execute_code.

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

Parameters3/5

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

The input schema fully describes run_id as the handle returned by submit_code/execute_code, so schema coverage is 100%. The description's phrase 'run receipt' loosely echoes that parameter but adds no new syntax or format details; baseline 3 applies.

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 names a specific verb ('Read') and resource ('persistent run receipt') plus an ownership constraint ('owned by the authenticated caller'). This clearly separates it from run-producing siblings like execute_code/submit_code and from cancel_run.

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

Usage Guidelines3/5

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

The description implies the use case: retrieve a previously created run receipt for the caller's own run. It does not explicitly state when to prefer this over get_functions/search_functions or give a 'do not use when...' exclusion, so guidance is only implied.

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

list_recipesList RecipesA
Read-only

Find reusable recipes in the authenticated caller's namespace.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoRequested summaries, 1–100; capped by configured discovery_limit.
queryNoOptional description filter; narrow this when results are truncated.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.6/5.0
Behavior3/5

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

The readOnlyHint annotation already covers the safety profile, and the description adds the namespace scoping and 'reusable' qualifier. However, it doesn't disclose pagination, truncation, or result ordering; those are only partially implied by the query parameter description in the schema.

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 one concise sentence with no filler. It front-loads the verb and resource, and every word earns its place while leaving schema details to the structured definition.

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?

With a readOnly annotation, 100% parameter coverage, and an output schema, the description is largely sufficient for a simple list operation. It could have explicitly mentioned truncation or the meaning of 'recipe,' but nothing critical is missing for an agent to invoke it correctly.

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

Parameters3/5

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

Schema description coverage is 100%, so the limit and query parameters are already documented. The description itself adds no parameter-level meaning beyond what the schema provides, which matches the baseline score of 3.

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 ('Find') and resource ('reusable recipes') and scopes it to 'the authenticated caller's namespace,' which clearly states what the tool does. It doesn't explicitly distinguish itself from siblings like search_functions or list_servers, but the resource name makes the purpose reasonably clear.

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

Usage Guidelines3/5

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

The description implies usage for listing the caller's own recipes, but it gives no explicit when-to-use guidance or alternatives. An agent could infer that this is the recipe-listing tool, but the description doesn't say when to prefer it over related sibling tools.

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

list_serversList ServersA
Read-only

Discover servers with function names and descriptions. All summaries are included when they fit. Follow next_cursor and next_function_cursor together as cursor and function_cursor; a nonzero function cursor continues the same server. Truncated descriptions are marked. cursor defaults to 0; limit defaults to 10 (1–100 servers, capped by discovery_limit). Results include the registry fingerprint; restart pagination if it changes. Use search_functions to search and get_functions for full schemas.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoRequested servers, 1–100; capped by discovery_limit, not a function cap.
cursorNoNonnegative server position from next_cursor.
function_cursorNoPosition from next_function_cursor; zero in compact mode.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

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

Beyond the readOnlyHint annotation, the description discloses pagination mechanics, truncation marking, cursor defaults, the discovery_limit cap, and the registry fingerprint restart condition. These behavioral details are essential for correct invocation and are not present in 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?

The description is dense but every sentence earns its place: purpose, pagination semantics, defaults, fingerprint behavior, and sibling routing are all covered without filler. It is front-loaded with the core purpose before diving into pagination 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?

With an output schema present and readOnlyHint true, the description still covers the non-obvious pagination contract and fingerprint invalidation rule. Nothing needed for correct usage is missing.

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 coverage is 100%, so the baseline is 3, but the description adds real meaning: limit is 'capped by discovery_limit, not a function cap,' and a nonzero function_cursor 'continues the same server.' It explains how the three parameters work together, exceeding schema-only 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?

States a specific action ('Discover servers') with a clear resource and payload ('function names and descriptions'). It also differentiates itself from siblings by directing search and schema retrieval to search_functions and get_functions.

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?

Explicitly tells the agent when to use alternatives: 'Use search_functions to search and get_functions for full schemas.' It also provides concrete pagination guidance for cursor handling, leaving little to inference.

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

read_artifactRead ArtifactA
Read-only

Read a bounded chunk of an execution artifact in the caller's namespace.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum requested bytes, from 1 through 8192.
offsetNoNonnegative byte offset from a previous artifact response.
artifact_idYesArtifact handle returned by the execution engine.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true, and the description's 'Read' aligns with that. The description adds value beyond the annotation by disclosing the bounded-chunk behavior and namespace restriction, which matter for pagination and access control.

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?

A single sentence that is front-loaded with the verb and object, with every word earning its place. It is compact, unambiguous, and easy to parse.

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?

With a full output schema, 100% parameter coverage, and readOnlyHint annotation, the description is nearly complete for correct invocation. It could explicitly mention pagination, but the schema's offset description already covers the mechanics.

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

Parameters3/5

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

Schema description coverage is 100%, so all parameters are already documented in the input schema. The description adds no new parameter-level meaning; it only restates the bounded-chunk concept already implied by limit/offset.

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 (Read), a precise resource (execution artifact), and a scope qualifier (bounded chunk, caller's namespace). This clearly distinguishes it from siblings like transform_artifact and get_run without needing to inspect schemas.

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

Usage Guidelines3/5

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

The description gives clear context on what the tool does and its constraints (bounded chunk, namespace), so an agent can infer when reading an artifact is appropriate. However, it does not explicitly name alternatives or state when not to use it, leaving the choice vs siblings to inference.

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

run_cached_codeRun Cached CodeB

Reuse unchanged cached code with a new structured inputs object.

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsNoComplete new inputs object; never interpolated into Python source.
cache_idYesRecipe identifier returned by execute_code or list_recipes.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.1/5.0
Behavior2/5

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

The description does not disclose that running cached code may execute arbitrary code with side effects, whether execution is synchronous or asynchronous, or how results are surfaced. With readOnlyHint=false already signaling a mutation-capable operation, the description adds little behavioral depth beyond the name and title.

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 a single efficient sentence with no filler or redundant content. It is front-loaded with the core purpose, though the verb 'reuse' is slightly vague compared to 'run' or 'execute.'

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

Completeness2/5

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

For an execution-oriented tool with mutation potential, the description is too thin: it omits when to use it versus execute_code/submit_code, whether it returns a run reference, and what side effects may occur. The output schema likely covers return values, but the invocation context is incomplete.

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

Parameters3/5

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

Schema description coverage is 100%: cache_id and params are already clearly documented, including that params is a complete replacement object and never interpolated into Python source. The description's 'new structured inputs object' reinforces this, but the schema carries the real semantic weight.

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 states a specific action ('Reuse unchanged cached code') and identifies the resource ('cached code') plus the key input concept ('new structured inputs object'). It is distinct enough from execute_code, which implies running new code, though it does not explicitly contrast itself with siblings.

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

Usage Guidelines3/5

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

The phrase 'unchanged cached code' implies the primary use case: when code is already cached and only inputs change. However, there is no explicit guidance about when to prefer this over execute_code, submit_code, or list_recipes, and no alternatives are named.

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

search_functionsSearch FunctionsA
Read-only

Search the compiled registry before requesting full function schemas.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoRequested matches, 1–100; capped by discovery_limit; narrow query if truncated.
queryYesSearch terms for function names and descriptions.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.8/5.0
Behavior3/5

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

The readOnlyHint annotation already covers the safety profile, and the description adds context that this searches a compiled registry rather than returning full schemas. No contradiction with annotations; the added behavioral insight is modest but useful.

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?

A single, front-loaded sentence with no filler. Every phrase contributes meaning, and the key workflow instruction is placed prominently.

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 readOnlyHint, fully documented parameters, and presence of an output schema, the description covers the essential workflow context: search first, then fetch full schemas. It is terse but sufficient for correct invocation.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already fully documents both query and limit. The description does not add parameter-level meaning beyond framing the search as a registry lookup.

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 states a specific action, 'Search the compiled registry,' and clarifies its role as a precursor to requesting full function schemas. It strongly implies a distinction from get_functions, though it does not explicitly name the sibling.

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 provides clear sequencing guidance: use this tool before requesting full function schemas. It does not explicitly list when not to use it or name alternatives, but the intended workflow is evident.

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

submit_codeSubmit CodeB

Submit execution and return a persistent receipt, not an MCP Tasks promise.

ParametersJSON Schema
NameRequiredDescriptionDefault
codeYesRestricted Python program assigning result.
inputsNoStructured execution inputs.
descriptionYesGeneric reusable operation description.
input_schemaNoOptional input validation schema.
idempotency_keyNoOptional owner-scoped deduplication key.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.2/5.0
Behavior3/5

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

With readOnlyHint=false, the tool is already known to be a write/mutation operation. The description adds that it returns a persistent receipt rather than a promise, which is useful, but it does not disclose side effects, execution semantics, or whether results are durable beyond the receipt.

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?

One sentence with no filler, front-loaded with the action and key behavioral differentiator. Every word earns its place.

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

Completeness2/5

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

The description is too sparse for a 5-parameter execution tool with several siblings. It lacks guidance on when to use this tool versus execute_code or run_cached_code, what 'persistent receipt' means for the agent, and how idempotency/input_schema relate. The output schema covers return shape, but the tool's role in the workflow is underspecified.

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

Parameters3/5

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

Schema coverage is 100%, so the schema already describes all parameters. The description does not add parameter-level semantics beyond what is in the schema, meeting the baseline.

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 ('Submit') and resource ('execution'), and adds a distinguishing note about returning a persistent receipt. However, it does not explicitly differentiate from sibling tools like execute_code or run_cached_code, leaving some ambiguity about its exact scope.

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?

No explicit when-to-use or alternative guidance is provided. The phrase 'not an MCP Tasks promise' hints at a use case for persistent results but does not name conditions or sibling tools. An agent would have to infer when to choose this over execute_code or run_cached_code.

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

transform_artifactTransform ArtifactA

Reduce stored JSON offline, without refetching or reading chunks.

ParametersJSON Schema
NameRequiredDescriptionDefault
codeYesRestricted Python assigning result; no imports or call_tool capability.
inputsNoOptional JSON parameters exposed as inputs['params']; parsed stored JSON is inputs['artifact']. Neither grants filesystem or network access.
artifact_idYesOwned handle returned by execution or a run receipt.
descriptionYesBounded generic projection description, without input values.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.5/5.0
Behavior3/5

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

The description adds some behavioral context beyond the readOnlyHint=false annotation: it states the operation is offline and does not refetch or read chunks. However, it does not disclose side effects, persistence behavior, or the fact that execution is sandboxed; those details appear only in the parameter schema, not the description.

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 a single front-loaded sentence with no filler. Every phrase earns its place by conveying the core operation and a key constraint in minimal space.

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

Completeness3/5

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

The output schema and fully documented parameters cover invocation details, and return values need not be described. Still, given sibling tools like execute_code and read_artifact, the description lacks explicit guidance on when to choose transform_artifact over those alternatives, leaving a meaningful contextual gap.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents all parameters in detail, including the restricted Python nature of code and the inputs structure. The description itself adds no parameter-level meaning, matching the baseline for full schema coverage.

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 states a specific action ('Reduce stored JSON') on a specific resource and adds a distinguishing mode ('offline, without refetching or reading chunks'). It is reasonably clear, though it does not explicitly differentiate itself from execute_code or run_cached_code by name.

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

Usage Guidelines3/5

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

The phrase 'without refetching or reading chunks' implies when this tool is useful, but there is no explicit when-to-use or when-not-to-use guidance and no named alternatives. The intended selection versus execute_code or read_artifact is left to inference.

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. 11 tool updatesv2.0.0
    • First observedcancel_run
    • First observedexecute_code
    • First observedget_functions
    • First observedget_run
    • First observedlist_recipes
    • First observedlist_servers
    • First observedread_artifact
    • First observedrun_cached_code
    • First observedsearch_functions
    • First observedsubmit_code
    • First observedtransform_artifact

TDQS

A3.9/5.0

Scored across 11 tools

Disambiguation5/5

Each tool targets a distinct operation: search/get/list for discovery, execute/submit/cached for running code, and dedicated tools for run management and artifact access. Even execute_code and submit_code are clearly differentiated by synchronous execution vs. persistent receipt submission.

Naming Consistency5/5

All 11 tool names follow the snake_case verb_noun pattern, such as search_functions, cancel_run, and transform_artifact. The pattern is consistent across discovery, execution, and artifact operations, with no mixing of conventions.

Tool Count5/5

With 11 tools, the server is well-scoped for its purpose: it covers function discovery, code execution variants, run lifecycle, and artifact handling without excess. The count is within the ideal range and each tool serves a clear role.

Completeness4/5

The surface covers the main workflow well: discovering functions, executing code, managing runs, and reading/transforming artifacts. A minor gap is the lack of a list_runs tool to enumerate persistent runs; callers must know a receipt ID upfront, which is a small workaround.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers