01Safety
0.0 / 30
What changed in the harness
Selection accuracy 100→100, token cost up 12%, unconfirmed writes 0%→0%.
Category breakdown
Earned points across the four signals Gradable measures. Safety and Legibility are scored out of 30; Economics and Discoverability are scored out of 20.
0.0 / 30
26.8 / 30
19.3 / 20
8.3 / 20
Highest-impact fix
Estimated gain +30 pointsExpose machine-readable principal/tenant confirmation and a non-mutating permission check so agents can verify both before destructive actions.
Description evidence
9 defects found across the exposed tool descriptions. Suggested rewrites make purpose, inputs, boundaries, and returns easier for an agent to understand.
| Tool | Defect types | Suggested rewrite |
|---|---|---|
list_artifacts |
params_unexplained |
List artifacts owned by the calling tenant, newest first. Filter by `session_id` or `agent_id` to see artifacts produced by a specific session/agent, by `filename` (exact match), by `content_type`, by `created_after` / `created_before` (ISO 8601) to bound artifact creation time, or by one or more `metadata.<key>=<value>` pairs (multi-key requires Pro). `limit` caps the page size (1–200, default 50); `cursor` is the opaque pagination token from the previous page's `next_cursor`. Returns a page of artifact records and a `next_cursor` to fetch the next page. Use this to discover what an agent or pipeline produced when you only know a session or agent ID. |
get_artifact |
params_unexplained |
Fetch metadata for a single artifact by `artifact_id` (an ID of the form `art_` followed by 16 alphanumeric characters): filename, content type, size, content hash, session/agent IDs, custom metadata, expiry, and creation timestamp. Does NOT return the file bytes — call `get_artifact_download_url` for that. Returns `artifact_not_found` for unknown IDs, `artifact_already_deleted` (HTTP 410) for soft-deleted ones, and `artifact_expired` (HTTP 410) for those past their TTL. |
get_artifact_download_url |
params_unexplained |
Generate a short-lived presigned URL (1 hour) the agent can use to download the artifact's bytes directly from Cloudflare R2, for the artifact identified by `artifact_id` (an ID of the form `art_` followed by 16 alphanumeric characters). Returns the presigned URL. Use this when the agent itself needs to consume the file. For sharing with humans, use `create_download_link` instead — that produces a stable `dl.artifacta.io/lnk_…` URL with configurable expiry. |
list_sessions |
params_unexplained no_return_description |
List session IDs synthesized from the calling tenant's artifacts, ordered by most recent activity. `created_after` / `created_before` (ISO 8601) restrict which sessions are returned by time range; `limit` caps page size (1–200, default 50); `cursor` is the opaque pagination token from the previous page's `next_cursor`. Returns a page of session entries — each with artifact count, seal status, and first/last activity timestamps — plus a `next_cursor` to fetch the next page. Sessions are not first-class; they exist only as long as artifacts reference them. |
store_artifact |
params_unexplained |
Upload a file as a new artifact in a single call. `filename` (1–255 characters) names the artifact. Provide EITHER up to ~10 MB of base64-encoded bytes via `content`, OR a local filesystem `path` that the MCP server reads and streams as multipart/form-data (up to 500 MB). For files larger than 500 MB, use `request_upload_url` (Pro only) instead — `store_artifact` returns `file_too_large` for them. Optionally tag the artifact with `session_id` (the producing session), `agent_id` (the producing agent), and `metadata` (string key/value pairs) for later retrieval. Returns the full artifact record including its new `artifact_id` and `content_hash`. Path uploads are confined: `path` must be inside the launcher-configured allow-list (default: the MCP server's CWD); paths outside it, paths traversing symlinks out of it, and paths to known-sensitive locations (such as `~/.ssh`, `~/.aws`, `/etc/`) are refused with `invalid_request`. For crash-safe retries, supply your own `idempotency_key` (any string up to 256 characters): a replay within 24h returns the original artifact and never double-bills. If you omit it, the server auto-generates one and returns it under `_meta.idempotency_key`, but that key only protects in-process retries within a single call — it is lost if the server restarts, so pre-commit your own key when durability matters. |
request_upload_url |
params_unexplained |
Reserve a presigned R2 PUT URL for a file too large to send through `store_artifact` (over 500 MB up to 5 GB). `filename` (1–255 characters) names the artifact, `content_type` is its MIME type, and `size_bytes` is its exact size in bytes (1 to 5,368,709,120). Optionally tag the pending artifact with `session_id`, `agent_id`, or `metadata` (string key/value pairs), and set `ttl` to a duration suffix such as `7d` or `30d`. Returns an `upload_url`, headers to include in the PUT, and an `artifact_id` in `pending` state. The agent (or its environment) PUTs the bytes directly to R2, then calls `complete_upload`. Pro plan only. Most agents should use `store_artifact` and let the MCP server pick the path automatically. Not retry-safe: this endpoint does not support idempotency keys, so on an HTTP 5xx or network error the reservation may or may not have been created. Do NOT blindly retry — the error guidance tells you to first call `list_artifacts` with the same `session_id`/`agent_id` to detect any pending artifact, so you don't create a duplicate. |
complete_upload |
params_unexplained |
Finalize an artifact previously reserved via `request_upload_url`, identified by `artifact_id` (an ID of the form `art_` followed by 16 alphanumeric characters), after the bytes have been PUT to the presigned URL. The server verifies the blob, computes the content hash, transitions the artifact from `pending` to `active`, increments tenant usage, and returns the updated artifact record. Calling this on an already-active artifact is idempotent and returns the existing record. Calling before the PUT completes returns `upload_not_found` — wait and retry. |
publish_artifact |
params_unexplained |
Publish an existing artifact as a polished, shareable public page at https://artifacta.io/a/{slug}. `artifact_id` identifies the artifact to publish; `title` (max 255 characters) optionally overrides the page title; `visibility` sets whether the page is `unlisted` (link-only, the default) or `public` (gallery-eligible); `access` sets whether viewing requires no password (`none`) or a password (`password`). Composes with store_artifact (store first, then publish). Returns a public_url anyone can open without an Artifacta account. Idempotent: re-publishing the same artifact_id updates the existing page and keeps the same URL. |
unpublish_artifact |
params_unexplained no_return_description |
Remove the public page for the artifact identified by `artifact_id`, making its public URL inaccessible. The artifact itself is not deleted — only its shareable page is taken down, and the URL stops resolving immediately. Returns a success result once the page has been removed. Idempotent: calling unpublish on an already-unpublished artifact is a no-op. |
Selection evidence
1 pair where similar names or overlapping descriptions may send an agent toward the wrong tool.
| Tool A | Tool B | Confidence | Why they collide |
|---|---|---|---|
get_artifact |
get_artifact_download_url |
high | Same artifact_id input and near-identical 'get_artifact' prefix make tasks like 'get/fetch/retrieve artifact X' or 'give me the artifact' ambiguous: get_artifact returns only metadata (explicitly not the bytes) while get_artifact_download_url yields the presigned URL, so an agent may pick the metadata call and fail to obtain the file, or conversely pick the URL call when metadata was wanted; the descriptions' cross-references signal real selection risk. |
Compare the field