01Safety
0.0 / 30
What changed in the harness
Selection accuracy 98→100, token cost up 3%, 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
19.8 / 30
13.5 / 20
11.4 / 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
28 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 |
|---|---|---|
sonar_app_extract_keywords |
no_return_description |
Extract the most likely target keywords from an app's title and description, ranked by relevance. Returns a list of keyword terms with a relevance score each. Useful for understanding what an app (yours or a competitor) is optimizing for. Works without an API key (free tier, limited daily use per IP). |
sonar_app_reviews |
no_return_description |
Fetch user reviews for an app. Returns a list of reviews (up to `limit`), each with the review text, star rating, author, and date, optionally filtered by star rating range and sorted by recent or helpful. Useful for sentiment analysis, feature-request mining, and competitive research. |
sonar_keyword_metrics |
no_return_description |
Fetch difficulty + popularity for a specific keyword (or up to 25 in bulk). Returns a metrics object (difficulty 0-100, popularity score) per requested keyword. Use this when you already know which keywords you care about — costs 1 credit per keyword. Works without an API key for up to 5 keywords/day (free tier, per IP); an API key removes that cap. Use sonar_keyword_search instead when you want related keyword ideas alongside metrics. |
sonar_get_app |
no_return_description |
Get full details for one tracked app in the caller's Sonar workspace. Returns the app's store metadata plus up to 90 daily snapshots of rating, review count, version, and installs. Requires a Full plan (trial counts). |
sonar_app_rankings |
no_return_description |
Rank history for an app's tracked keywords. Returns daily ranks over the requested window, one history array per keyword. Use this to check how rankings moved after a metadata change or to find keywords trending up or down. Cursor-paginated over keywords (default 50 per page). Requires a Full plan (trial counts). |
sonar_app_changes |
no_return_description |
Change history for a tracked app. Returns detected releases, metadata edits, screenshot swaps, price changes, and category moves as a list, newest first (up to `limit`, default 50). Useful for correlating rank movements with what the app (or a competitor) changed. Requires a Full plan (trial counts). |
sonar_competitor_keywords |
no_return_description |
List the keywords a tracked competitor currently ranks for (last 7 days of SERP data). Returns keywords with difficulty and popularity per keyword; when own_app_id is set, each keyword also includes your current rank and a gap marker (gap=missing) for keywords you don't rank for. Cursor-paginated (default 50 per page). Requires a Full plan (trial counts). |
sonar_list_products |
no_return_description |
List your products with their linked store versions and competitor counts. Returns a list of products, each with its Sonar product UUID, app versions, and competitor count. Use it to discover product/app UUIDs. Requires a Full plan (trial counts). |
sonar_list_alerts |
no_return_description |
List your alert subscriptions. Returns each alert rule's change type (rank drops, review spikes, etc.), scope (a specific app or org-wide), threshold, and whether it's enabled. Requires a Full plan (trial counts). |
sonar_track_app |
no_return_description |
WRITE tool — links the second-store version of an existing Sonar product (e.g. the product already tracks the iOS app and you want to add the Android version, or vice versa). Each product holds at most one iOS + one Android app; to start tracking a brand-new app, use sonar_create_product instead. Returns a confirmation of the linking with the resulting app id stored on the product. Requires a Full plan (trial counts) and an API key with the write scope. |
sonar_track_competitor |
no_return_description |
WRITE tool — adds a competitor app under a Sonar product so its keywords and rankings get tracked alongside the product's own app. The product must already have its own app linked in the same store as the competitor. Returns a confirmation with the competitor's tracked app id; read the tracked keywords afterwards with sonar_competitor_keywords. Requires a Full plan (trial counts) and an API key with the write scope. |
sonar_update_keyword_note |
no_return_description |
WRITE tool — sets or clears the note on a tracked keyword in the caller's Sonar workspace (e.g. why it's tracked, an optimization hypothesis, a reminder). Idempotent: re-sending the same note is a no-op. Returns a confirmation of the update with the tracked keyword id. Requires a Full plan (trial counts) and an API key with the write scope. |
sonar_star_keyword |
no_return_description |
WRITE tool — stars or unstars a tracked keyword in the caller's Sonar workspace. A star marks the keyword as a favorite/target the user is actively pursuing; starred keywords carry a starred_at timestamp in sonar_app_keywords results. Idempotent: re-starring refreshes the timestamp, unstarring a non-starred keyword is a no-op. Returns a confirmation of the action with the resulting starred state. Requires a Full plan (trial counts) and an API key with the write scope. |
sonar_delete_tracked_keyword |
no_return_description |
WRITE tool — stop tracking one keyword/app pair in the caller's Sonar workspace. Identify the pair by its tracked-keyword id (from sonar_app_keywords). Requires a Full plan (trial counts) and an API key with the write scope. Returns a confirmation that the tracked keyword was deleted, including its id. |
sonar_untrack_keywords |
no_return_description |
WRITE tool — bulk-untrack keywords for an app in the caller's Sonar workspace. Pass `all: true` to remove every tracked keyword, OR `ids: [...]` to remove specific ones (exactly one of the two). Requires a Full plan (trial counts) and an API key with the write scope. Returns the list of tracked-keyword ids that were removed. |
sonar_untrack_app |
no_return_description |
WRITE tool — untrack an app and its associated tracking data in the caller's Sonar workspace. Requires a Full plan (trial counts) and an API key with the write scope. Returns a confirmation that the app and its tracking data were untracked, including the app_id. |
sonar_delete_product |
no_return_description |
WRITE tool — delete a product and untrack its apps in the caller's Sonar workspace. Requires a Full plan (trial counts) and an API key with the write scope. Returns a confirmation that the product was deleted, including the product_id. |
sonar_remove_competitor |
no_return_description |
WRITE tool — remove a competitor from a product in the caller's Sonar workspace. Requires a Full plan (trial counts) and an API key with the write scope. Returns a confirmation that the competitor was removed from the product. |
sonar_set_alert |
no_return_description |
WRITE tool — create or update an alert subscription in the caller's Sonar workspace. Upserts on (type + scope): re-submitting the same type/scope updates the existing rule. Omit `threshold` for the per-type default; omit `scope_app_id` for an org-wide rule. Requires a Full plan (trial counts) and an API key with the write scope. Returns the created or updated alert subscription, including its id (used by sonar_delete_alert). |
sonar_delete_alert |
no_return_description |
WRITE tool — delete an alert subscription in the caller's Sonar workspace. Requires a Full plan (trial counts) and an API key with the write scope. Returns a confirmation that the alert subscription was deleted, including its id. |
sonar_screenshot_layout_guide |
no_return_description |
The layout-format reference for Sonar screenshot sets. Call this ONCE before creating or editing screenshot layouts — it documents the layout JSON schema, coordinate system, image handling (remote URLs), flowing background shapes, fonts, translation overrides, and the recommended workflow. Returns that guide content describing the layout JSON schema and workflow. |
sonar_screenshot_devices |
no_return_description |
List the device sizes supported for app-store screenshot sets, with their canvas dimensions (the pixel coordinate space all layouts use) and which store each belongs to. Pick a device here before sonar_create_screenshot_set. Returns the list of supported devices with their store and canvas dimensions. |
sonar_list_screenshot_sets |
no_return_description |
List a product's app-store screenshot sets (metadata only: store, device size, locales, studio_url). Use sonar_get_screenshot_set for full layouts. Returns the metadata-only list of the product's screenshot sets. |
sonar_delete_screenshot_set |
no_return_description |
Permanently delete a screenshot set and everything in it (screens, translations). Irreversible — confirm with the user before deleting work they may want. Requires a write-scope API key. Returns a confirmation that the set and all its screens and translations were deleted, including the set_id. |
sonar_add_screenshot |
no_return_description |
Append a screen to a screenshot set (at the end; reorder with sonar_update_screenshot_set). Requires a write-scope API key. Returns the created screen, including its screenshot id and layout. |
sonar_update_screenshot |
no_return_description |
Replace one screen's layout in a screenshot set. Whole-document replace — fetch the current layout, modify it, send it back. The change shows up immediately in the Screenshot Studio for human review. Requires a write-scope API key. Returns the updated screen, including its screenshot id and new layout. |
sonar_delete_screenshot |
no_return_description |
Delete one screen from a screenshot set. A set keeps at least one screen — deleting the last one is rejected. Requires a write-scope API key. Returns a confirmation that the screen was deleted, including the screenshot_id. |
sonar_set_screenshot_translations |
no_return_description |
Write a locale's translation overrides for screens in a screenshot set (text copy, localized captures/images). Geometry and styling always come from the source layout; anything not overridden falls back to it. The locale is auto-enabled on the set. Requires a write-scope API key. Returns the updated set, confirming the locale's overrides were stored and the locale enabled. |
Selection evidence
11 pairs where similar names or overlapping descriptions may send an agent toward the wrong tool.
| Tool A | Tool B | Confidence | Why they collide |
|---|---|---|---|
sonar_app_extract_keywords |
sonar_app_keywords |
medium | A task like 'get the keywords for this app' is ambiguous: extract infers keywords from the store listing (store_id input), while app_keywords lists keywords already tracked in the workspace (app_id input), and a pure-name match on 'app keywords' doesn't reveal which the user means. |
sonar_delete_screenshot_set |
sonar_delete_screenshot |
medium | 'Delete that screenshot' is ambiguous because both operate on the screenshot domain: one removes a single screen (screenshot_id), the other removes the entire set (set_id); a user unaware of the set/screen distinction can map to the wrong member. |
sonar_update_screenshot_set |
sonar_update_screenshot |
medium | 'Update the screenshot' could mean editing one screen's layout (update_screenshot) or renaming/reordering the set and locales (update_screenshot_set); same set-vs-screen naming collision plus shared 'update screenshot' phrasing. |
sonar_app_keywords |
sonar_app_rankings |
medium | Both return keyword data for a tracked app (same app_id input); 'show me my keyword rankings' is ambiguous between listing the tracked keywords with metrics and fetching the daily rank history per keyword. |
sonar_app_keywords |
sonar_competitor_keywords |
medium | Both list keywords with difficulty/popularity and accept an app UUID; 'give me the keywords for competitor X' could match either if the agent doesn't distinguish tracked-workspace keywords from SERP-ranked competitor keywords. |
sonar_app_rankings |
sonar_keyword_rankings |
medium | Both are named 'rankings' history tools with overlapping schema (an id plus days window); 'show me the ranking history' doesn't reveal whether it is anchored on an app's tracked keywords or on a single keyword's SERP history. |
sonar_app_search |
sonar_keyword_search |
medium | Nearly identical input schema (query, store, country) and similar names; 'search for meditation' is genuinely ambiguous between finding matching apps and researching the keyword's difficulty/popularity. |
sonar_competitor_keywords |
sonar_scan_competitor |
medium | 'Scan my competitor's keywords' is ambiguous between running the heavyweight discovery scan (write) and reading the already-recorded keyword list (read); both take the same competitor app UUID. |
sonar_keyword_search |
sonar_keyword_suggestions |
medium | Both research a single keyword/seed and both return related autocomplete terms; 'get keyword suggestions for meditation' name-matches suggestions but search also returns related ideas plus metrics, making the choice task-dependent. |
sonar_untrack_keywords |
sonar_untrack_app |
medium | 'Stop tracking this' is ambiguous between the app's individual keywords (bulk untrack, all:true) and the whole app; the shared untrack action requires inferring the user's intended entity scope. |
sonar_set_alert |
sonar_delete_alert |
low | 'Turn off my rank-drop alert' could map to disabling via set_alert (enabled:false, upsert) or to delete_alert; the verbs set/delete mostly disambiguate, but the disable-vs-remove intent is a real edge case. |
Compare the field