0.0 / 30
What changed in the harness
Selection accuracy 100→98, token cost up 15%, unconfirmed writes 0%→0%.
Category breakdown
Where the score comes from.
Earned points across the four signals Gradable measures. Safety and Legibility are scored out of 30; Economics and Discoverability are scored out of 20.
01Safety
02Legibility
18.0 / 30
03Economics
14.6 / 20
04Discoverability
12.2 / 20
Highest-impact fix
Estimated gain +30 pointsAdd explicit identity and permission preflight tools
Expose machine-readable principal/tenant confirmation and a non-mutating permission check so agents can verify both before destructive actions.
Description evidence
Defects and rewrites.
55 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 |
|---|---|---|
meta_exchange_token |
no_return_description |
Exchange a short-lived access token for a long-lived token valid for ~60 days. Requires META_APP_ID and META_APP_SECRET to be configured. Returns the long-lived token string. |
meta_debug_token |
no_return_description |
Inspect an access token to determine its validity, expiration time, granted scopes, and the associated user. Returns the token inspection details. |
meta_get_app_info |
name_restates_behavior no_return_description |
Retrieve the Meta app's basic profile details for the configured META_APP_ID, including name, category, and namespace, typically used to verify app identity before other API calls. Returns the app information object. |
meta_subscribe_webhook |
no_return_description |
Set up webhook notifications for an object type (e.g., 'instagram', 'page') so events for the requested fields are delivered to your HTTPS callback_url after verification with verify_token. Requires META_APP_ID and META_APP_SECRET. Returns a confirmation of the created subscription. |
meta_get_webhook_subscriptions |
no_return_description |
List the webhook subscriptions currently configured for the Meta App, including each object type, callback URL, and subscribed fields. Returns an array of subscription objects. |
ig_publish_photo |
no_return_description |
Publish a photo to the Instagram feed. Performs a two-step process: creates a media container from image_url (public HTTPS URL, JPEG only, with optional caption, location_id, user_tags, or alt_text), waits for it to be ready, then publishes. Returns the ID of the published media post. |
ig_publish_video |
no_return_description |
Publish a video to the Instagram feed from a public HTTPS video_url. Creates a media container, waits for video processing to finish, then publishes; supports optional caption, thumb_offset, and location_id. Returns the ID of the published media post. |
ig_publish_carousel |
no_return_description |
Publish a carousel (album) post containing 2-10 images/videos. Each item in items must specify a type (IMAGE or VIDEO) and a public HTTPS url; caption and location_id are optional. Returns the ID of the published carousel post. |
ig_publish_reel |
no_return_description |
Publish a Reel (short video) from a public HTTPS video_url. Creates a media container, waits for video processing, then publishes; optionally sets caption, cover_url, share_to_feed, thumb_offset, or alt_text. Returns the ID of the published reel. |
ig_publish_story |
no_return_description |
Publish a Story (image or video) that disappears after 24 hours. media_type selects IMAGE or VIDEO and media_url must be a public HTTPS URL. Returns the ID of the published story. |
ig_get_container_status |
no_return_description |
Check the processing status of a media container by container_id (for example, the container created during a video publish before it is ready). Returns the container status (e.g., FINISHED, IN_PROGRESS, ERROR). |
ig_get_media_list |
name_restates_behavior no_return_description |
List media published on the authenticated Instagram account, with optional limit (max 100, default 25) and pagination via after/before cursors. Returns an array of media summaries (e.g., id, media_type). |
ig_get_media |
params_unexplained no_return_description |
Fetch details for a specific Instagram media post by its media_id (obtained from ig_get_media_list or a publish call), optionally selecting comma-separated fields. Returns the requested media details (default: id, caption, media_type, media_url, permalink, timestamp, like_count, comments_count). |
ig_delete_media |
no_return_description |
Permanently delete an Instagram media post (photo, carousel, reel, or story) by media_id. This action is irreversible and requires the instagram_manage_contents permission. Returns a success confirmation. |
ig_get_media_insights |
params_unexplained no_return_description |
Retrieve analytics for a specific media post by its media_id (from ig_get_media_list or a publish call) for the comma-separated metrics in metric (default views,reach,saved,shares; for REELs add likes,comments,reposts,reels_skip_rate). Note: 'impressions' and 'video_views' are deprecated since v22.0 — use 'views'. Returns the metric values for the post. |
ig_toggle_comments |
params_unexplained no_return_description |
Enable or disable comments on a specific Instagram media post by its media_id (from ig_get_media_list or a publish call); enabled=true turns comments on, false turns them off. Returns the updated comment status. |
ig_get_comments |
params_unexplained no_return_description |
List comments on a specific Instagram media post by its media_id (from ig_get_media_list or a publish call), with optional limit and pagination via after cursor. Returns an array of comment objects. |
ig_get_comment |
params_unexplained no_return_description |
Fetch details for a single comment by its comment_id (obtained from ig_get_comments or a mention notification), including text, user, and timestamps. Returns the comment details. |
ig_post_comment |
no_return_description |
Post a top-level comment with the provided message text on a specific media post by media_id. Returns the ID of the created comment. |
ig_get_replies |
no_return_description |
List replies to a specific comment by comment_id, with optional limit and pagination cursor. Returns an array of reply comment objects. |
ig_reply_to_comment |
no_return_description |
Post a reply with the provided message text to an existing comment by comment_id. Returns the ID of the created reply. |
ig_hide_comment |
params_unexplained no_return_description |
Hide or unhide a comment on your post by comment_id (from ig_get_comments); hide=true makes the comment hidden from public view, false restores it. Returns the updated visibility status. |
ig_delete_comment |
no_return_description |
Permanently delete a comment by comment_id from one of your media posts. This action is irreversible. Returns a success confirmation. |
ig_get_profile |
no_return_description |
Retrieve the authenticated Instagram Business/Creator account profile information (e.g., id, username, name, follower counts). Returns the profile object. |
ig_get_account_insights |
no_return_description |
Retrieve account-level engagement and growth analytics for the comma-separated metrics in metric (views, reach, follower_count, reposts, accounts_engaged, total_interactions), aggregated over period (day/week/days_28/month/lifetime) between since and until. Note: older metrics such as 'impressions' and 'website_clicks' are deprecated since v22.0. Returns the metric values for the period. |
ig_business_discovery |
no_return_description |
Look up another Instagram Business/Creator account's public information by username (without '@'), optionally selecting comma-separated fields. Returns the account's profile fields (default: id, username, name, biography, followers_count, follows_count, media_count). |
ig_get_collaboration_invites |
no_return_description |
List pending collaboration invites for the Instagram account (feature added December 2025), with optional limit and pagination cursor. Returns an array of pending invite objects. |
ig_respond_collaboration_invite |
no_return_description |
Accept or decline a collaboration invite by invite_id using action ('accept' or 'decline'). Returns a confirmation of the invite's updated status. |
ig_search_hashtag |
no_return_description |
Search for a hashtag by name (without '#') to obtain its numeric hashtag ID, which is required before querying hashtag media. Note: limited to 30 unique hashtags per 7-day rolling window. Returns the hashtag ID and name. |
ig_get_hashtag |
no_return_description |
Fetch information about a hashtag by its hashtag_id (obtained from ig_search_hashtag). Returns the hashtag's ID and name. |
ig_get_hashtag_recent |
name_restates_behavior params_unexplained no_return_description |
List the most recent media posts tagged with a hashtag, identified by its hashtag_id (from ig_search_hashtag), with optional limit and pagination cursor. Returns an array of recent media objects. |
ig_get_hashtag_top |
name_restates_behavior params_unexplained no_return_description |
List the top (most popular) media posts tagged with a hashtag, identified by its hashtag_id (from ig_search_hashtag), with optional limit and pagination cursor. Returns an array of top media objects. |
ig_get_tagged_media |
name_restates_behavior no_return_description |
List media on the Instagram account where the account is tagged by others (photo tags, not @mentions), with optional limit and pagination cursor. Returns an array of tagged media objects. |
ig_get_conversations |
no_return_description |
List Instagram DM conversations from the selected folder (inbox or spam, default inbox), with optional limit and pagination cursor. Requires the 'instagram_manage_messages' permission and the Instagram Messaging API. Returns an array of conversation objects. |
ig_get_messages |
params_unexplained no_return_description |
List messages within a specific DM conversation by its conversation_id (obtained from ig_get_conversations), with optional limit and pagination cursor. Returns an array of message objects. |
ig_send_message |
no_return_description |
Send a DM text message to a user by their Instagram-scoped recipient_id. Requires the 'instagram_manage_messages' permission; you can only message users who messaged you first (24-hour window for standard, 7-day for human agent). Returns the ID of the sent message. |
ig_get_message |
params_unexplained no_return_description |
Fetch details for a single DM message by its message_id (obtained from ig_get_messages), including content, sender, and timestamps. Returns the message details. |
threads_publish_text |
params_unexplained no_return_description |
Publish a text-only post on Threads (text, max 500 chars) with optional reply_control, link_attachment, topic_tag, quote_post_id (ID of an existing Threads post to quote in this post), poll_options (2-4 choices), gif_id/gif_provider, alt_text, and is_spoiler. Returns the ID of the created post. |
threads_publish_image |
params_unexplained no_return_description |
Publish an image post on Threads from a public HTTPS image_url (JPEG/PNG, max 8MB), with optional caption, reply_control, topic_tag, quote_post_id (ID of an existing Threads post to quote), alt_text, and is_spoiler. Returns the ID of the created post. |
threads_publish_video |
params_unexplained no_return_description |
Publish a video post on Threads from a public HTTPS video_url (MP4/MOV, max 1GB, up to 5 min). Creates a container, waits for video processing, then publishes; supports caption, reply_control, topic_tag, quote_post_id (ID of an existing Threads post to quote), alt_text, and is_spoiler. Returns the ID of the created post. |
threads_publish_carousel |
params_unexplained no_return_description |
Publish a carousel post on Threads with 2-20 images/videos (each item needs a type of IMAGE or VIDEO and a public HTTPS url), with optional text, reply_control, topic_tag, and quote_post_id (ID of an existing Threads post to quote). Returns the ID of the created carousel post. |
threads_delete_post |
name_restates_behavior no_return_description |
Permanently delete a Threads post by post_id. This action is irreversible and rate-limited to 100 deletions per 24 hours. Returns a success confirmation. |
threads_get_container_status |
no_return_description |
Check the processing status of a Threads media container by container_id (e.g., a container created during video publishing before it is ready). Returns the container status (e.g., FINISHED, IN_PROGRESS, ERROR). |
threads_get_publishing_limit |
no_return_description |
Check how many posts remain available to publish in the current rolling 24-hour window (max 250 posts/day). Returns the current usage and remaining publishing quota. |
threads_get_posts |
no_return_description |
List published Threads posts with optional filters for limit, date range (since/until), and pagination (after/before cursors). Returns an array of post objects. |
threads_get_post |
name_restates_behavior no_return_description |
Fetch details of a specific Threads post by post_id, optionally selecting comma-separated fields to limit the response. Returns the requested post fields. |
threads_search_posts |
name_restates_behavior |
Search public Threads posts by keyword or topic tag (search_type 'keyword' or 'tag', default keyword), with optional filters for media_type, author_username, date range (since/until), limit, and pagination. Returns the matching posts. |
threads_get_replies |
no_return_description |
List replies (the conversation) for a specific Threads post by post_id, optionally in reverse chronological order, with optional limit and pagination cursor. Returns an array of reply objects. |
threads_reply |
no_return_description |
Post a reply (text, max 500 chars) to a Threads post or another reply by reply_to_id, optionally attaching an image_url or video_url. Returns the ID of the created reply. |
threads_hide_reply |
no_return_description |
Hide a reply by reply_id on your Threads post. Hidden replies remain visible when directly accessed. Returns a success confirmation. |
threads_unhide_reply |
no_return_description |
Unhide a previously hidden reply by reply_id on your Threads post. Returns a success confirmation. |
threads_get_profile |
name_restates_behavior no_return_description |
Retrieve the authenticated Threads user's profile information, including verification status. Returns the profile object. |
threads_get_user_threads |
no_return_description |
List threads published by the authenticated user (alias for threads_get_posts scoped to the user), with optional limit, date filters (since/until), and pagination cursor. Returns an array of post objects. |
threads_get_post_insights |
name_restates_behavior no_return_description |
Retrieve analytics for a specific Threads post by post_id for the comma-separated metrics in metric (default views, likes, replies, reposts, quotes, clicks). Returns the metric values for the post. |
threads_get_user_insights |
name_restates_behavior no_return_description |
Retrieve account-level Threads analytics for the comma-separated metrics in metric (views, likes, replies, reposts, quotes, clicks, followers_count, follower_demographics) within the optional since/until window. Returns the aggregate metric values. |
Selection evidence
Confusable tool pairs.
24 pairs where similar names or overlapping descriptions may send an agent toward the wrong tool.
| Tool A | Tool B | Confidence | Why they collide |
|---|---|---|---|
ig_get_comments |
ig_get_comment |
medium | Singular/plural naming for near-identical getters; a task like 'get comment data' or 'get the comments' gives no cue whether the agent has a media_id or comment_id, so the wrong member can be picked. |
ig_get_messages |
ig_get_message |
medium | Both fetch Instagram DM data and differ only by singular/plural plus id type (conversation_id vs message_id); 'get my DMs' or 'get that message' is ambiguous. |
threads_get_posts |
threads_get_post |
medium | Only plural/singular plus 'list of published posts' vs 'details of a specific post' distinguishes them; 'show me posts/this post' can go to either. |
ig_get_comment |
ig_get_mentioned_comments |
medium | Both take a comment_id and return comment details; a task like 'get the comment I was mentioned in' could be routed to the generic specific-comment getter unless '@mention' is explicit. |
ig_get_comments |
ig_get_mentioned_comments |
medium | A task 'get comments where I'm mentioned' vs 'get comments on this post' both match a comment-fetching tool; agent may select the generic one when 'mention' is phrased loosely. |
ig_get_hashtag |
ig_get_hashtag_recent |
medium | 'Get the hashtag #x' is ambiguous between hashtag metadata (ig_get_hashtag) and the recent media for it; both keyed on the same hashtag_id. |
ig_get_hashtag |
ig_get_hashtag_top |
medium | Same id key and same ambiguity: 'info about this hashtag' could mean metadata or the top popular media for it. |
ig_get_hashtag_recent |
ig_get_hashtag_top |
high | Identical inputs and both return media tagged with a hashtag; a natural task 'get the posts for hashtag X' gives no signal for recent vs top. |
ig_get_media |
ig_get_media_insights |
medium | Both target the same post via media_id; 'get the data/stats/performance for this post' is ambiguous between post details and analytics. |
ig_get_media_list |
ig_get_media |
medium | 'Get my media/posts' could mean the full published list or a specific post's details; plural phrasing can route to the wrong member without an id. |
ig_get_media_insights |
ig_get_account_insights |
medium | A task 'show me my Instagram insights/analytics' is ambiguous between per-post metrics (media_id) and account-level metrics (period/metric). |
ig_get_media |
ig_get_tagged_media |
medium | 'Get media' is ambiguous between a specific post and media where the account is tagged; only 'specific post' vs 'tagged' wording separates them. |
ig_get_media_list |
ig_get_tagged_media |
medium | 'Show me the media on this account' could mean published media (list) or media the account is tagged in; descriptions are easy to conflate. |
ig_get_conversations |
ig_get_messages |
medium | 'Check my DMs/messages' is ambiguous between the conversation list and the messages inside one conversation, and both share very similar names/descriptions. |
ig_get_collaboration_invites |
ig_respond_collaboration_invite |
medium | 'Handle the collaboration invite' is ambiguous between reading pending invites and accepting/declining one; both share the invite/collaboration wording. |
ig_get_container_status |
threads_get_container_status |
medium | Descriptions differ only by platform; 'check the media processing status of this container' without an explicit platform can pick the wrong server tool. |
ig_publish_carousel |
threads_publish_carousel |
medium | Near-identical descriptions differing only by platform; 'publish a carousel' is ambiguous unless the user names Instagram or Threads. |
ig_get_profile |
threads_get_profile |
medium | Both return profile info and differ only by platform; 'get the profile information' without platform context can select the wrong one. |
ig_get_replies |
threads_get_replies |
medium | Both are 'get replies' taking an id; a task 'get the replies' is ambiguous between Instagram comment replies and Threads post replies. |
ig_hide_comment |
ig_delete_comment |
medium | 'Remove/get rid of this comment' is ambiguous between hiding it and permanently deleting it; descriptions are close enough to route wrongly. |
ig_post_comment |
ig_reply_to_comment |
medium | 'Comment on this / reply to this post' is ambiguous between posting a top-level comment and replying to an existing comment, especially when a specific thread is referenced. |
threads_get_post |
threads_get_post_insights |
medium | Both target a single post via post_id; 'get the post data/metrics' is ambiguous between post details and its analytics. |
threads_get_post_insights |
threads_get_user_insights |
medium | 'Get my Threads insights/stats' is ambiguous between per-post metrics (post_id) and account-level metrics (metric only). |
threads_get_post |
threads_get_replies |
medium | 'Show me this thread/post' could mean post details or the replies/conversation for it since Threads calls replies the conversation. |
Compare the field