{"serverId":"mcp-registry/com.hookdetector/hookdetector","tools":[{"name":"admin_list_accounts","fixed":{"name":"admin_list_accounts","description":"Owner only: every account, newest first, as GET /v1/admin/accounts: {total,\n    accounts: [{id, email, name, label, credits, created_at, last_seen_at, runs, hooks,\n    charged_total, is_admin, admitted_by, keys_active}]}. Free.","inputSchema":{"type":"object","title":"admin_list_accountsArguments","properties":{"q":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q","default":null,"description":"up to 200 characters: part of an email or label (any case), or the start of an id"},"limit":{"anyOf":[{"type":"integer","maximum":200,"minimum":1},{"type":"null"}],"title":"Limit","default":null,"description":"page size, 1 to 200 (default 50)"},"offset":{"anyOf":[{"type":"integer","maximum":1000000,"minimum":0},{"type":"null"}],"title":"Offset","default":null,"description":"how many to skip"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":261,"tokensAfter":261},{"name":"admin_list_actions","fixed":{"name":"admin_list_actions","description":"Owner only: the audit of password sign-ins and credit changes, newest first, as GET\n    /v1/admin/actions: {actions: [{id, action, actor_email, target_account_id, detail,\n    created_at}]}. Free.","inputSchema":{"type":"object","title":"admin_list_actionsArguments","properties":{"limit":{"anyOf":[{"type":"integer","maximum":200,"minimum":1},{"type":"null"}],"title":"Limit","default":null,"description":"page size, 1 to 200 (default 50)"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":161,"tokensAfter":161},{"name":"admin_list_runs","fixed":{"name":"admin_list_runs","description":"Owner only: every account's research runs, newest first, as GET /v1/admin/runs:\n    {runs: [{run_id, account_id, email, topic, language, country, status, requested,\n    hooks, reserved, charged, failure_kind, error, created_at, finished_at}]}. Free.","inputSchema":{"type":"object","title":"admin_list_runsArguments","properties":{"limit":{"anyOf":[{"type":"integer","maximum":200,"minimum":1},{"type":"null"}],"title":"Limit","default":null,"description":"page size, 1 to 200 (default 50)"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","default":null,"description":"only runs with this status: queued, running, done, failed"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"account_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Id","default":null,"description":"only this account's"}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":255,"tokensAfter":255},{"name":"approve_access_request","fixed":{"name":"approve_access_request","description":"Owner only: approve an access request. Makes a one-use code, marks the request\n    approved and emails the code. The code is in this result once, with emailed true or\n    false; when false, send it yourself or approve again (the unused code is revoked and\n    a new one sent). 409 request_already_decided once its code was used. Free.","inputSchema":{"type":"object","title":"approve_access_requestArguments","properties":{"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id","default":null}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":184,"tokensAfter":184},{"name":"clear_find_decision","fixed":{"name":"clear_find_decision","description":"Undo keep_find: the video goes back to having no verdict, on every find of it, and\n    leaves list_kept_finds.\n    Returns {\"find_id\", \"verdict\": null}. Clearing a find with no decision is the same\n    success, so a retry is safe. Same as DELETE /v1/finds/{find_id}/decision. Free.","inputSchema":{"type":"object","title":"clear_find_decisionArguments","required":["find_id"],"properties":{"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"find_id":{"type":"string","title":"Find Id"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":161,"tokensAfter":161},{"name":"create_access_code","fixed":{"name":"create_access_code","description":"Owner only: make an access code to hand out. max_uses 1 to 100000 (default 1),\n    expires_in_days 1 to 365 (optional), note your own. email binds it to one person: then\n    only Google sign-in with that verified email redeems it, never the API or MCP; leave\n    it out for a code that works on every door. The code is in this result only. Free.","inputSchema":{"type":"object","title":"create_access_codeArguments","properties":{"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note","default":null},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","default":null},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"max_uses":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Uses","default":null},"expires_in_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Expires In Days","default":null}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":260,"tokensAfter":260},{"name":"create_account","fixed":{"name":"create_account","description":"Create an account and get an API key with free credits. Needs a beta access code\n    (access_code, HD-XXXX-XXXX; case and dashes do not matter): without one the error is\n    403 access_code_required, and a code that does not work is 403 invalid_access_code.\n    Ask for a code with request_access, or at hookdetector.com/access. No key needed for\n    this call. label is an optional note up to 120 characters.\n\n    Keep the api_key it returns: it is the only copy. Send it as the header\n    'Authoriz","inputSchema":{"type":"object","title":"create_accountArguments","properties":{"label":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Label","default":null},"access_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Access Code","default":null}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":230,"tokensAfter":230},{"name":"create_key","fixed":{"name":"create_key","description":"Create another API key on your account, for example one per agent or machine, so\n    you can revoke one without touching the others. name is up to 60 characters.\n\n    Give the key only what it needs. scopes: any of \"read\" (read runs, hooks, keeps,\n    usage), \"write\" (keep, reject, undo, chat), \"research\" (start runs, which spend\n    credits), \"admin\" (manage keys, delete the account); all four when left out. An agent\n    that finds hooks needs [\"read\", \"write\", \"research\"]. credit_limit: the mo","inputSchema":{"type":"object","title":"create_keyArguments","properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","default":null},"scopes":{"type":"array","items":{"enum":["read","write","research","admin"],"type":"string"},"title":"Scopes","default":null,"minItems":1},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"credit_limit":{"type":"integer","title":"Credit Limit","default":null,"minimum":1},"expires_in_days":{"type":"integer","title":"Expires In Days","default":null,"minimum":1}}},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: false, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":312,"tokensAfter":312},{"name":"delete_account","fixed":{"name":"delete_account","description":"Erase your account and everything in it, for good: every API key, run, hook, keep\n    or reject decision, conversation and message. Unspent credits go with it. It cannot\n    be undone.\n\n    confirm must be exactly \"delete my account\", or nothing happens (422\n    confirmation_required). Refused with 409 run_in_flight while a run is queued or\n    running: wait for it with get_run first. Returns {\"deleted\": true, \"account_id\",\n    \"erased\": {counts per kind}, \"message\"}. Every key of the account st","inputSchema":{"type":"object","title":"delete_accountArguments","properties":{"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"confirm":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Confirm","default":null}}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: true (inferred from the name).","verify":true}],"tokensBefore":228,"tokensAfter":228},{"name":"export_run","fixed":{"name":"export_run","description":"A finished run's hooks as a file a creator can open: format \"csv\" (default; one row\n    per hook, opens in any spreadsheet, formula-like cells defused with a leading quote),\n    \"json\" (the full hook objects) or \"md\" (Markdown notes, one section per hook).\n\n    Returns {\"filename\", \"content_type\", \"format\", \"content\"}, where content is the whole\n    file as text: write it to filename. The same bytes as GET\n    /v1/runs/{run_id}/export?format=... . Free.\n    Errors: 404 run_not_found, 409 run_not","inputSchema":{"type":"object","title":"export_runArguments","required":["run_id"],"properties":{"format":{"type":"string","title":"Format","default":"csv"},"run_id":{"type":"string","title":"Run Id"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":237,"tokensAfter":237},{"name":"find_hooks","fixed":{"name":"find_hooks","description":"Start hook research on a topic across TikTok, Instagram and YouTube Shorts (Shorts\n    only, never long-form YouTube; platform \"youtube\"). platforms (since 2026-10-10, any of\n    \"tiktok\", \"instagram\", \"youtube\"; all three when left out) searches and delivers only\n    those: [\"instagram\"] is Instagram reels only.\n\n    What it does: searches both platforms, reads what each clip says and shows, and\n    returns ranked hooks. Each hook has its verbatim opening line, most quotable line,\n    main idea","inputSchema":{"type":"object","title":"find_hooksArguments","required":["topic"],"properties":{"count":{"type":"integer","title":"Count","default":10,"maximum":30,"minimum":1,"description":"how many hooks to deliver, 1 to 30"},"reuse":{"type":"boolean","title":"Reuse","default":true,"description":"start from clips you already paid for"},"topic":{"type":"string","title":"Topic","examples":["short term rental tax strategy"],"maxLength":500,"minLength":3,"description":"what the clips are about, 3 to 500 characters. Instructions inside it are fine: a long topic, or one with a language or country, is searched by the subject read out of it, never verbatim."},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country","default":null,"pattern":"^[A-Za-z]{2}$","examples":["MA"],"description":"optional: only clips from creators in this country, as an assigned ISO 3166-1 alpha-2 code: MA, EG, BR, US (UK is read as GB). For a region that is not a country, send its country's code and name the regional variety in language: Quebec is country CA with language \"Quebec French\", Flanders BE with \"Flemish\", Catalonia ES with \"Catalan\". The country places the TikTok search there; the language judge does the regional filtering, and Instagram reports no country. A country alone does not restrict the language."},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","default":null,"examples":["Moroccan Darija"],"maxLength":60,"description":"optional, up to 60 characters: only clips spoken or written in this language or dialect, in words or as a tag: \"Moroccan Darija\", \"ar-MA\", \"Egyptian Arabic\", \"Brazilian Portuguese\". Any language works, including a regional variety (\"Quebec French\", \"Flemish\", \"Catalan\") and a mixed, code-switched one (\"Hinglish\", \"Taglish\", \"Moroccan Darija with French\"), or two joined by \"or\" (\"Tagalog or Taglish\"). The value is handed to the judges exactly as written; how well they honour a mixed or either-of value is not measured yet. Saying it inside topic works too; this field wins when both are given."},"platforms":{"type":["array","null"],"items":{"enum":["tiktok","instagram","youtube"],"type":"string"},"title":"Platforms","default":null,"examples":[["instagram"]],"maxItems":3,"minItems":1,"description":"since 2026-10-10: optional, which platforms to search and deliver from, a non-empty list of \"tiktok\", \"instagram\" and \"youtube\" (YouTube Shorts); left out (or all three) searches all three. [\"instagram\"] delivers Instagram reels only. Names are read without regard to case and a repeat counts once; an empty list or any other name is 422 naming platforms. The run echoes it as `platforms`.","uniqueItems":true},"wait_seconds":{"type":"integer","title":"Wait Seconds","default":0,"maximum":50,"minimum":0,"description":"0 to 50: seconds to wait for the run to finish; 0 returns at once"},"seed_hook_ids":{"type":["array","null"],"items":{"type":"string","format":"uuid"},"title":"Seed Hook Ids","default":null,"maxItems":10,"description":"since 2026-10-01: optional, up to 10 hook ids from your runs (a kept hook from GET /v1/keeps, or any hook you can read): \"find more like these\". The engine reads their lines, topics, creators and languages to steer the search phrases and the creators it expands; a seed is never delivered again. An id that is not one of your hooks is 422.","uniqueItems":true},"conversation_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Id","default":null,"description":"attach the run to one of your conversations"},"idempotency_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency Key","default":null,"description":"1 to 255 visible ASCII characters; the same key within 24 hours returns the first run instead of paying for a second"},"posted_within_days":{"enum":[7,30,90,365,null],"type":["integer","null"],"title":"Posted Within Days","default":null,"examples":[30],"description":"since 2026-10-01: optional, how recent the clips must be, as days: 7, 30, 90, 365. The search asks the platforms for that window and the floor for a clip under 7 days old uses views per day (10,000 a day passes); null or left out means any time. A clip's `posted_at` and `age_days` say what the run found."}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":1369,"tokensAfter":1369},{"name":"find_long_hooks","fixed":{"name":"find_long_hooks","description":"Find long-form hooks: how the YouTube videos that beat their own channel open, on a\n    topic. Long-form YouTube only, never Shorts, TikTok or Instagram (for those: find_hooks).\n\n    What it does: searches YouTube for long-form videos on the topic, keeps only the\n    outliers (a video whose views are at least min_outlier times the median views of its\n    own channel's recent uploads; 2 by default) posted in 2023 or later (nothing older is\n    ever delivered), reads the first intro_seconds (60 by","inputSchema":{"type":"object","title":"find_long_hooksArguments","required":["topic"],"properties":{"count":{"type":"integer","title":"Count","default":8,"maximum":30,"minimum":1,"description":"how many long-form hooks to deliver, 1 to 30"},"reuse":{"type":"boolean","title":"Reuse","default":true,"description":"start from the videos your last long or packaging run on this topic already bought (within a week), so the second tab on a topic costs little"},"topic":{"type":"string","title":"Topic","examples":["home espresso","https://www.youtube.com/@name"],"maxLength":500,"minLength":3,"description":"the subject to research, 3 to 500 characters, in any language. Or a YouTube address instead of a subject: a channel's (https://www.youtube.com/@name, or just @name) returns that channel's own outliers, its recent long-form uploads that beat its usual; a long-form video's (https://www.youtube.com/watch?v=...) returns that one video, whatever its multiple. A Short's address is refused (422)"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country","default":null,"pattern":"^[A-Za-z]{2}$","examples":["US"],"description":"optional, an assigned ISO 3166-1 alpha-2 code (US, GB, MA): searches YouTube as seen from this country. It steers the search; it does not filter the channels"},"openers":{"type":["integer","null"],"title":"Openers","default":null,"maximum":6,"minimum":0,"examples":[3],"description":"optional, 0 to 6 (default 3): how many openings to write for the subject from the structure of the real ones; 0 for none"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","default":null,"examples":["Spanish"],"maxLength":60,"description":"optional, up to 60 characters: the language the videos should be spoken in, in words or as a tag (\"Spanish\", \"pt-BR\"). Left out, it is read from the topic's own language"},"min_outlier":{"type":["number","null"],"title":"Min Outlier","default":null,"maximum":100,"minimum":1.5,"examples":[3],"description":"optional, 1.5 to 100 (default 2): how many times its own channel's usual views a video must have to count as an outlier. Only outliers are ever delivered"},"channel_size":{"enum":["small","medium","large",null],"type":["string","null"],"title":"Channel Size","default":null,"examples":["small"],"description":"optional: only videos from channels of this size, by what the channel's uploads usually get (its median views, the figure the outlier is measured against): small (under 20,000), medium (up to 200,000) or large. Null or left out: any size"},"wait_seconds":{"type":"integer","title":"Wait Seconds","default":0,"maximum":50,"minimum":0,"description":"0 to 50: seconds to wait for the run to finish; 0 returns at once"},"intro_seconds":{"type":["integer","null"],"title":"Intro Seconds","default":null,"maximum":120,"minimum":20,"examples":[60],"description":"optional, 20 to 120 (default 60): how many seconds of each video's opening to read and return"},"idempotency_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency Key","default":null,"description":"1 to 255 visible ASCII characters; the same key within 24 hours returns the first run instead of paying for a second"},"posted_within_days":{"enum":[30,90,365,730,null],"type":["integer","null"],"title":"Posted Within Days","default":null,"examples":[365],"description":"optional, how recent the videos must be, as days: 30, 90, 365, 730. Null or left out means everything since 2023-01-01; nothing older is ever delivered"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":1154,"tokensAfter":1154},{"name":"find_packaging","fixed":{"name":"find_packaging","description":"Find packaging: the titles and thumbnails that earned the click on a topic, from\n    long-form YouTube videos that beat their own channel.\n\n    What it does: runs the same outlier search as find_long_hooks (long-form YouTube,\n    2023 or later, views at least min_outlier times the channel's usual), then looks at\n    each video's thumbnail beside its title and says what the pair is doing.\n\n    What comes back: the run's `finds`, ranked. Each is a real package, as YouTube shows\n    it: title (verb","inputSchema":{"type":"object","title":"find_packagingArguments","required":["topic"],"properties":{"count":{"type":"integer","title":"Count","default":12,"maximum":30,"minimum":1,"description":"how many packages to deliver, 1 to 30"},"ideas":{"type":["integer","null"],"title":"Ideas","default":null,"maximum":10,"minimum":0,"examples":[6],"description":"optional, 0 to 10 (default 6): how many written packages (a title and a thumbnail concept) to add for the subject; 0 for none"},"reuse":{"type":"boolean","title":"Reuse","default":true,"description":"start from the videos your last long or packaging run on this topic already bought (within a week), so the second tab on a topic costs little"},"topic":{"type":"string","title":"Topic","examples":["home espresso","https://www.youtube.com/@name"],"maxLength":500,"minLength":3,"description":"the subject to research, 3 to 500 characters, in any language. Or a YouTube address instead of a subject: a channel's (https://www.youtube.com/@name, or just @name) returns that channel's own outliers, its recent long-form uploads that beat its usual; a long-form video's (https://www.youtube.com/watch?v=...) returns that one video, whatever its multiple. A Short's address is refused (422)"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country","default":null,"pattern":"^[A-Za-z]{2}$","examples":["US"],"description":"optional, an assigned ISO 3166-1 alpha-2 code (US, GB, MA): searches YouTube as seen from this country. It steers the search; it does not filter the channels"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","default":null,"examples":["Spanish"],"maxLength":60,"description":"optional, up to 60 characters: the language the videos should be spoken in, in words or as a tag (\"Spanish\", \"pt-BR\"). Left out, it is read from the topic's own language"},"min_outlier":{"type":["number","null"],"title":"Min Outlier","default":null,"maximum":100,"minimum":1.5,"examples":[3],"description":"optional, 1.5 to 100 (default 2): how many times its own channel's usual views a video must have to count as an outlier. Only outliers are ever delivered"},"channel_size":{"enum":["small","medium","large",null],"type":["string","null"],"title":"Channel Size","default":null,"examples":["small"],"description":"optional: only videos from channels of this size, by what the channel's uploads usually get (its median views, the figure the outlier is measured against): small (under 20,000), medium (up to 200,000) or large. Null or left out: any size"},"wait_seconds":{"type":"integer","title":"Wait Seconds","default":0,"maximum":50,"minimum":0,"description":"0 to 50: seconds to wait for the run to finish; 0 returns at once"},"idempotency_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency Key","default":null,"description":"1 to 255 visible ASCII characters; the same key within 24 hours returns the first run instead of paying for a second"},"posted_within_days":{"enum":[30,90,365,730,null],"type":["integer","null"],"title":"Posted Within Days","default":null,"examples":[365],"description":"optional, how recent the videos must be, as days: 30, 90, 365, 730. Null or left out means everything since 2023-01-01; nothing older is ever delivered"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":1087,"tokensAfter":1087},{"name":"get_conversation","fixed":{"name":"get_conversation","description":"One chat conversation: its newest messages in order (role, content, run_id,\n    created_at), a page of `limit`, with earlier_cursor for the older ones (null when the\n    page reaches the first message), and the runs those messages hold, with their hooks,\n    plus nullable language and country search picker settings. Same shape as GET\n    /v1/conversations/{conversation_id}. Free. Errors: 404 conversation_not_found, 422 for\n    an id that is not a UUID or a cursor this API did not issue.","inputSchema":{"type":"object","title":"get_conversationArguments","required":["conversation_id"],"properties":{"limit":{"type":"integer","title":"Limit","default":100,"maximum":500,"minimum":1,"description":"messages per page, newest page first"},"before":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Before","default":null,"description":"earlier_cursor from the previous page; leave out for the newest messages"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"conversation_id":{"type":"string","title":"Conversation Id"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":311,"tokensAfter":311},{"name":"get_find","fixed":{"name":"get_find","description":"One result of a long or packaging run with every field: the video, its numbers and\n    the opening or the package analysis. find_id comes from a run's finds (get_run) or from\n    list_kept_finds. Same shape as GET /v1/finds/{find_id}. Free. Errors: 404\n    find_not_found, 422 for an id that is not a UUID.","inputSchema":{"type":"object","title":"get_findArguments","required":["find_id"],"properties":{"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"find_id":{"type":"string","title":"Find Id"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":161,"tokensAfter":161},{"name":"get_hook","fixed":{"name":"get_hook","description":"One hook with every field: its opening line, transcript, watch_url and the\n    vertical player_url (null only for an unusable id or a photo post found before\n    2026-09-26; runs deliver single videos only). hook_id comes from get_run. Free.","inputSchema":{"type":"object","title":"get_hookArguments","required":["hook_id"],"properties":{"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"hook_id":{"type":"string","title":"Hook Id"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":143,"tokensAfter":143},{"name":"get_remix","fixed":{"name":"get_remix","description":"One remix you paid for (from remix_hook, remix_find or a chat turn), as its answer\n    held it. Free. 404 remix_not_found for one that is not yours. Same as GET\n    /v1/remixes/{remix_id}.","inputSchema":{"type":"object","title":"get_remixArguments","required":["remix_id"],"properties":{"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"remix_id":{"type":"string","title":"Remix Id"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":129,"tokensAfter":129},{"name":"get_run","fixed":{"name":"get_run","description":"A research run: its status, progress, charge and every hook it produced.\n\n    wait_seconds 0 to 50: wait up to that long for the run to finish, returning the\n    moment it is done or failed. Use wait_seconds=50 and call again while status is\n    \"queued\" or \"running\"; a fresh run needs about 3 or 4 such calls. progress has\n    stage, message and updated_at, plus counts once the research reports them, preview\n    (hooks written so far) while a running run holds one, and feed (the live research\n  ","inputSchema":{"type":"object","title":"get_runArguments","required":["run_id"],"properties":{"since":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Since","default":null},"run_id":{"type":"string","title":"Run Id"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"wait_seconds":{"type":"integer","title":"Wait Seconds","default":0,"maximum":50,"minimum":0,"description":"0 to 50: seconds to wait for the run to finish; 0 returns at once"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":288,"tokensAfter":288},{"name":"keep_find","fixed":{"name":"keep_find","description":"Keep or reject a find (a long-form hook or a package): verdict is \"keep\" (default)\n    or \"reject\", and the last verdict wins. The verdict is the video's on that tab: every\n    find of the same video you have there carries it, so a later run that finds the video\n    again shows it already kept. Kept videos come back from list_kept_finds, across all\n    runs. Returns {\"find_id\", \"verdict\"}. Same as POST\n    /v1/finds/{find_id}/decision. Free. Errors: 404 find_not_found.","inputSchema":{"type":"object","title":"keep_findArguments","required":["find_id"],"properties":{"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"find_id":{"type":"string","title":"Find Id"},"verdict":{"type":"string","title":"Verdict","default":"keep"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":228,"tokensAfter":228},{"name":"list_access_codes","fixed":{"name":"list_access_codes","description":"Owner only: every access code, newest first, with code_id, code_prefix, email,\n    note, source, max_uses, uses, expires_at, revoked_at, created_at and usable. Never the\n    code itself. Free.","inputSchema":{"type":"object","title":"list_access_codesArguments","properties":{"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":115,"tokensAfter":115},{"name":"list_access_requests","fixed":{"name":"list_access_requests","description":"Owner only: every access request, newest first, optionally only one status\n    (pending, approved, rejected). Each has request_id, email, note, status, code_id,\n    code_prefix, created_at and decided_at. Never a code. Needs the admin scope and an\n    admin account (403 admin_required otherwise). Free.","inputSchema":{"type":"object","title":"list_access_requestsArguments","properties":{"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","default":null},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":172,"tokensAfter":172},{"name":"list_conversations","fixed":{"name":"list_conversations","description":"Your chat conversations, most recently active first, limit 1 to 100 (default 30)\n    per page: conversation_id, title, created_at, updated_at, language, country and\n    messages (the count). Nullable language/country are the saved search picker settings.\n    Returns {\"conversations\": [...], \"next_cursor\"}; pass next_cursor back as cursor for\n    more. Same shape as GET /v1/conversations. Next step: get_conversation. Free.","inputSchema":{"type":"object","title":"list_conversationsArguments","properties":{"limit":{"type":"integer","title":"Limit","default":30,"maximum":100,"minimum":1,"description":"page size, 1 to 100"},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor","default":null},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":239,"tokensAfter":239},{"name":"list_finds","fixed":{"name":"list_finds","description":"The index: every video your long and packaging runs found, across all runs, one\n    entry a video on each tab (its newest read, with the keep or reject made on the\n    video). Each is a full find object: the title and thumbnail_url as YouTube shows them,\n    views, channel_median_views, outlier (views over the channel's usual) and\n    outlier_score (0 to 100 on one fixed scale, so entries from different runs rank\n    together), plus the analysis its run wrote. q (at most 120 characters) looks in","inputSchema":{"type":"object","title":"list_findsArguments","properties":{"q":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q","default":null,"description":"words a title, channel, hook or thumbnail must hold"},"kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind","default":null,"description":"only \"long\" or only \"packaging\"; leave out for both"},"sort":{"type":"string","title":"Sort","default":"score","description":"\"score\" (default: the highest outlier score first), \"outlier\" (views over the channel's usual), \"views\" or \"newest\""},"tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tier","default":null,"description":"only \"breakout\" (10 times the channel's usual or more), \"strong\" (5 to 10) or \"outlier\" (under 5)"},"limit":{"type":"integer","title":"Limit","default":100,"maximum":100,"minimum":1,"description":"page size, 1 to 100"},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor","default":null},"device":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Device","default":null,"description":"only entries filed under this device: a hook_type, a title_device or one of the thumbnail_devices"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"min_score":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Min Score","default":null,"description":"only entries with an outlier_score of at least this, 0 to 100"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":553,"tokensAfter":553},{"name":"list_keeps","fixed":{"name":"list_keeps","description":"The hooks you kept with keep_hook, newest decision first, across all runs or within\n    one run_id, limit 1 to 100 (default 100) per page. Returns {\"kept\": [...], \"count\": n\n    on this page, \"next_cursor\"} with full hook objects; pass next_cursor back as cursor\n    for more. Same shape as GET /v1/keeps. Free. Errors: 422 for a bad run_id or cursor.","inputSchema":{"type":"object","title":"list_keepsArguments","properties":{"limit":{"type":"integer","title":"Limit","default":100,"maximum":100,"minimum":1,"description":"page size, 1 to 100"},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor","default":null},"run_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Run Id","default":null},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":239,"tokensAfter":239},{"name":"list_kept_finds","fixed":{"name":"list_kept_finds","description":"The videos you kept with keep_find (long-form hooks and packages), newest decision\n    first: one entry a video on each tab across all runs, or the kept finds of one run_id,\n    limit 1 to 100 (default 100) per page.\n    Returns {\"kept\": [...], \"count\": n on this page, \"next_cursor\"} with full find objects.\n    Same shape as GET /v1/finds/keeps. Free. Errors: 422 for a bad kind, run_id or cursor.","inputSchema":{"type":"object","title":"list_kept_findsArguments","properties":{"kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind","default":null,"description":"only \"long\" or only \"packaging\"; leave out for both"},"limit":{"type":"integer","title":"Limit","default":100,"maximum":100,"minimum":1,"description":"page size, 1 to 100"},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor","default":null},"run_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Run Id","default":null},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":298,"tokensAfter":298},{"name":"list_keys","fixed":{"name":"list_keys","description":"Your API keys and what each may do. keys: key_id, name, prefix, created_at,\n    last_used_at and current (true for the key making this call). access, per key_id:\n    scopes, credit_limit, credits_used, expires_at and expired. scopes: the calling key's.\n    The original signup key has key_id \"original\". The keys themselves are never shown\n    again. Needs admin. Same shape as GET /v1/keys. Free.","inputSchema":{"type":"object","title":"list_keysArguments","properties":{"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":169,"tokensAfter":169},{"name":"list_remixes","fixed":{"name":"list_remixes","description":"The remixes you paid for from one hook (hook_id) or one find (find_id), newest\n    first, each as its answer held it, with remix_id and created_at. Returns {\"remixes\":\n    [...]}. Free. 404 hook_not_found or find_not_found for one that is not yours. Same as\n    GET /v1/hooks/{hook_id}/remixes and GET /v1/finds/{find_id}/remixes.","inputSchema":{"type":"object","title":"list_remixesArguments","properties":{"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"find_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Find Id","default":null,"description":"one of your finds; or give hook_id"},"hook_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hook Id","default":null,"description":"one of your hooks; or give find_id"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":229,"tokensAfter":229},{"name":"list_runs","fixed":{"name":"list_runs","description":"Your research runs, newest first, without their results, limit 1 to 100 (default 20)\n    per page. Each run says its kind: \"short\" (find_hooks), \"long\" (find_long_hooks) or\n    \"packaging\" (find_packaging); pass kind to list only one. Returns {\"runs\": [...],\n    \"next_cursor\"}: pass next_cursor back as cursor for the next page; it is null on the\n    last. Same shape as GET /v1/runs. Next step: open one with get_run, or download it with\n    export_run. Free. Errors: 422 invalid_cursor for a curso","inputSchema":{"type":"object","title":"list_runsArguments","properties":{"kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind","default":null,"description":"only runs of this kind: short, long or packaging; leave out for every kind"},"limit":{"type":"integer","title":"Limit","default":20,"maximum":100,"minimum":1,"description":"page size, 1 to 100"},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor","default":null},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":305,"tokensAfter":305},{"name":"remix_find","fixed":{"name":"remix_find","description":"Make it mine, for a long-form opening or a package: write up to n (1 to 5, default\n    3) new ones for your niche that keep one real find's structure. From a long find,\n    `written` holds openings: hook (what the creator says first), then (what the next half\n    minute has to deliver) and kept_structure, built on the find's template and its order\n    of beats. From a packaging find it holds packages: title, thumbnail (what to show and\n    how to frame it), thumbnail_text (the words to print, or","inputSchema":{"type":"object","title":"remix_findArguments","properties":{"n":{"type":"integer","title":"N","default":null,"maximum":5,"minimum":1,"description":"how many to write, 1 to 5 (default 3)"},"mode":{"enum":["openings","intro",null],"type":["string","null"],"title":"Mode","default":null,"examples":["intro"],"description":"optional: \"openings\" (the default) writes openings or packages for a niche; \"intro\" is Write my intro: your own timed intro for the video you describe in video_brief, in this long-form opening's structure. Only a long find has an intro (a packaging find is 422); niche and n are not used in intro mode"},"niche":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Niche","default":null,"description":"your niche, product or audience, as you would say it (1 to 120 characters)"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"find_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Find Id","default":null,"description":"one of your finds (from get_run, get_find, list_finds or list_kept_finds)"},"video_brief":{"type":["string","null"],"title":"Video Brief","default":null,"examples":["A beginner video on why home espresso tastes sour, built around my 18 grams in, 36 grams out recipe."],"maxLength":600,"minLength":10,"description":"required for mode \"intro\", 10 to 600 characters: what your video is about, in a few sentences, in your own words and language. The intro uses only the facts and numbers you write here; anything else becomes a {placeholder}"},"idempotency_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency Key","default":null,"description":"1 to 255 visible ASCII characters; the same key and request within 24 hours answer the saved remix and charge nothing; the same key with another request is 422 idempotency_key_reused"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":668,"tokensAfter":668},{"name":"revoke_access_code","fixed":{"name":"revoke_access_code","description":"Owner only: revoke an access code (code_id from list_access_codes). It admits\n    nobody from now on; accounts it already made keep working. 404 access_code_not_found\n    for an unknown id. Free.","inputSchema":{"type":"object","title":"revoke_access_codeArguments","properties":{"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null},"code_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Id","default":null}}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":141,"tokensAfter":141},{"name":"revoke_key","fixed":{"name":"revoke_key","description":"Revoke one of your API keys at once: key_id from list_keys, or \"original\" for the\n    signup key. Any key may revoke itself (the result says so, and that key stops working\n    immediately); revoking another needs admin. Never the last active key on the account\n    (409): create another with create_key first.\n\n    If a key leaked, pass key_id \"others\": every key of the account except the one you\n    are calling with is revoked at once, and the result says how many (revoked_count) and\n    which ke","inputSchema":{"type":"object","title":"revoke_keyArguments","required":["key_id"],"properties":{"key_id":{"type":"string","title":"Key Id"},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key","default":null}}},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: false, destructiveHint: true, idempotentHint: false (inferred from the name).","verify":true}],"tokensBefore":217,"tokensAfter":217},{"name":"showcase","fixed":{"name":"showcase","description":"Real hooks this service has delivered, for a wait screen: talking heads only, each\n    hook_id, still_url (a JPEG, no key needed), platform, creator, views and line (the\n    banner on the video, else its opening line). The most viewed first, one per creator,\n    only what is already public on TikTok or Instagram: never a topic, an account or a run.\n    No key needed. Cached 10 minutes. Free. REST: GET /v1/showcase?n=12.\n    ","inputSchema":{"type":"object","title":"showcaseArguments","properties":{"n":{"type":"integer","title":"N","default":12,"description":"how many; clamped to 1 to 24"}}},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"annotations","detail":"Added readOnlyHint: true (inferred from the name).","verify":true}],"tokensBefore":177,"tokensAfter":177}],"toolsChanged":32,"tokensSaved":0,"todo":[{"tool":"admin_adjust_credits","params":["api_key"]},{"tool":"admin_list_accounts","params":["api_key"]},{"tool":"admin_list_actions","params":["api_key"]},{"tool":"admin_list_runs","params":["api_key"]},{"tool":"admin_overview","params":["api_key"]},{"tool":"approve_access_request","params":["api_key","request_id"]},{"tool":"balance","params":["api_key"]},{"tool":"chat","params":["api_key","message","conversation_id"]},{"tool":"clear_decision","params":["api_key","hook_id"]},{"tool":"clear_find_decision","params":["api_key","find_id"]},{"tool":"create_access_code","params":["note","email","api_key","max_uses","expires_in_days"]},{"tool":"create_account","params":["label","access_code"]},{"tool":"create_key","params":["name","scopes","api_key","credit_limit","expires_in_days"]},{"tool":"delete_account","params":["api_key","confirm"]},{"tool":"embed_hook","params":["api_key","hook_id"]},{"tool":"export_run","params":["format","run_id","api_key"]},{"tool":"find_hooks","params":["api_key"]},{"tool":"find_long_hooks","params":["api_key"]},{"tool":"find_packaging","params":["api_key"]},{"tool":"get_conversation","params":["api_key","conversation_id"]},{"tool":"get_find","params":["api_key","find_id"]},{"tool":"get_hook","params":["api_key","hook_id"]},{"tool":"get_remix","params":["api_key","remix_id"]},{"tool":"get_run","params":["since","run_id","api_key"]},{"tool":"keep_find","params":["api_key","find_id","verdict"]},{"tool":"keep_hook","params":["note","reason","api_key","hook_id","verdict"]},{"tool":"list_access_codes","params":["api_key"]},{"tool":"list_access_requests","params":["status","api_key"]},{"tool":"list_conversations","params":["cursor","api_key"]},{"tool":"list_finds","params":["cursor","api_key"]},{"tool":"list_keeps","params":["cursor","run_id","api_key"]},{"tool":"list_kept_finds","params":["cursor","run_id","api_key"]},{"tool":"list_keys","params":["api_key"]},{"tool":"list_remixes","params":["api_key"]},{"tool":"list_runs","params":["cursor","api_key"]},{"tool":"reject_access_request","params":["api_key","request_id"]},{"tool":"remix_find","params":["api_key"]},{"tool":"remix_hook","params":["api_key"]},{"tool":"request_access","params":["note","email"]},{"tool":"revoke_access_code","params":["api_key","code_id"]},{"tool":"revoke_key","params":["key_id","api_key"]},{"tool":"usage","params":["api_key"]}],"note":"Deterministic fixes. Changes marked verify need a human look before shipping (renames, inferred annotations, inferred types, removed text). Parameter descriptions in `todo` need to be written by someone who knows the API."}