# Hook Detector (remote · api.hookdetector.com)

Real TikTok and Instagram hooks that already work, in any language, with why each travelled.

- Trust score: 63/100 (medium)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-26

## Components

- remote · `api.hookdetector.com`: 63/100 (this document), [markdown](https://verifymcp.io/servers/com-hookdetector-hookdetector/api.md), [page](https://verifymcp.io/servers/com-hookdetector-hookdetector/api)

## Channel facts

- Endpoint: `https://api.hookdetector.com/mcp`
- Transports: `streamable-http`
- Auth: `none`
- Version: `2.4.0`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-26.

- **Endpoint Security**: 66/100
  - The endpoint's TLS certificate is valid, in date, and uses a strong key.
  - Authorisation not fully verified: no authorisation is required to call this server, and 19 tool(s) never declared a destructiveHint. The MCP spec treats an absent hint as destructive by default, so we cannot call this surface safe.
  - HTTPS is enforced; there's no plaintext access path.
  - The HSTS (Strict-Transport-Security) header is present.
  - DNSSEC is configured correctly; the domain's records validate against the full chain to the root.
- **Transport & Reachability**: 100/100
  - Verified streamable-http transport via a live MCP handshake.
- **Schema Quality & AI Usability**: 63/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 4068 tokens (~214/item across 19 items; 19 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 3/100
  - Stability observed for 1 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 78/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 23% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Tool Safety**: 75/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - 0 of 2 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "delete_account" implies "delete" and declares no destructiveHint at all, which the MCP spec reads as destructive by default.
  - An AI judge read all 20 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

## Install

### How do I install the Hook Detector MCP server?

Hook Detector is a hosted endpoint at https://api.hookdetector.com/mcp, so there is nothing to install locally. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add --transport http com-hookdetector-hookdetector 'https://api.hookdetector.com/mcp'
```

### Cursor

```json
{
  "mcpServers": {
    "com-hookdetector-hookdetector": {
      "url": "https://api.hookdetector.com/mcp"
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "com-hookdetector-hookdetector": {
      "type": "http",
      "url": "https://api.hookdetector.com/mcp"
    }
  }
}
```

### Codex

```toml
[mcp_servers.com-hookdetector-hookdetector]
url = "https://api.hookdetector.com/mcp"
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "com-hookdetector-hookdetector": {
      "type": "remote",
      "url": "https://api.hookdetector.com/mcp",
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add com-hookdetector-hookdetector --url 'https://api.hookdetector.com/mcp' --transport streamable-http
```

### Hermes

```yaml
mcp_servers:
  com-hookdetector-hookdetector:
    url: "https://api.hookdetector.com/mcp"
```

### Netclaw

```json
{
  "McpServers": {
    "com-hookdetector-hookdetector": {
      "Transport": "http",
      "Url": "https://api.hookdetector.com/mcp"
    }
  }
}
```

### Vellum

```bash
assistant mcp add com-hookdetector-hookdetector -t streamable-http -u 'https://api.hookdetector.com/mcp'
```

### Other

```json
{
  "mcpServers": {
    "com-hookdetector-hookdetector": {
      "type": "http",
      "url": "https://api.hookdetector.com/mcp"
    }
  }
}
```

The mcpServers block is a cross-client convention. Remote transports vary, so check your client's docs.

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-26 (score 63, 0)

- [security] The server rewrote its instructions, which are the text every model session reads
- [security] Tool “find_hooks” rewrote its description, which is the text the model reads
- [security] Tool “get_run” rewrote its description, which is the text the model reads
- [functional improvement] Stability: unverified → 0.03

### 2026-09-25 (score 63)

First indexed and scored.

## MCP tools (19)

### `create_account` (~110 tokens)

Create an account and get an API key with free credits. No human step, no
    form, no key needed for this call. label is an optional note up to 120 characters.

    Keep the api_key it returns: it is the only copy. Send it as the header
    'Authorization: Bearer hd_...' or pass it as api_key on every other tool. Signups
    are limited per address per hour. Next step: find_hooks.

Input parameters:

- `label`

Output parameters:

- `result` (string)

### `find_hooks` (~1175 tokens)

Start hook research on a topic across TikTok and Instagram.

    What it does: searches both platforms, reads what each clip says and shows, and
    returns ranked hooks. Each hook has its verbatim opening line, most quotable line,
    main idea, why it travelled, topic, on-screen text, full transcript, a watch link and
    a vertical player_url (9:16 iframe).

    Arguments: topic is 3 to 500 characters, count 1 to 30 (default 10). Instructions
    inside the topic are fine: only the subject read out of it is searched. reuse=true
    (default) starts from clips you already paid for on the same topic, so a rerun is
    close to free and often finishes in seconds. idempotency_key: any string up to 255
    characters; the same key within 24 hours returns the first run instead of starting
    and paying for a second one, so always pass one and reuse it when you retry.
    wait_seconds 0 to 50: 0 returns the run_id at once; above 0 waits for the run and
    returns the run object, as get_run does.

    Any language, dialect or country: language is optional free text up to 60 characters
    naming the language or dialect every clip must be in ("Moroccan Darija", "ar-MA",
    "Egyptian Arabic", "Brazilian Portuguese"); country is an optional ISO 3166-1 alpha-2
    code for where the creators are (MA, EG, BR). Saying it inside topic works too; these
    fields win when both are given. A country alone does not restrict the language. Each
    hook reports the language it was judged to be in.

    A region that is not a country: send its country's code and name the regional variety
    in language (Quebec: country "CA", language "Quebec French"; Flanders: "BE" and
    "Flemish"; Catalonia: "ES" and "Catalan"). The country places the TikTok search there
    and the language judge does the regional filtering; Instagram reports no country.
    language may name a mixed, code-switched variety ("Hinglish", "Taglish", "Moroccan
    Darija with French") or two joined by "or" ("Taga…

Input parameters:

- `api_key`
- `conversation_id`: attach the run to one of your conversations
- `count` (integer): how many hooks to deliver, 1 to 30
- `country`: 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…
- `idempotency_key`: 1 to 255 visible ASCII characters; the same key within 24 hours returns the first run instead of paying for a second
- `language`: 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 work…
- `reuse` (boolean): start from clips you already paid for
- `topic` (string, required): 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.
- `wait_seconds` (integer): 0 to 50: seconds to wait for the run to finish; 0 returns at once

Output parameters:

- `result` (string)

### `get_run` (~323 tokens)

A research run: its status, progress, charge and every hook it produced.

    wait_seconds 0 to 50: wait up to that long for the run to finish, returning the
    moment it is done or failed. Use wait_seconds=50 and call again while status is
    "queued" or "running"; a fresh run needs about 3 or 4 such calls. progress has
    stage, message and updated_at, plus counts once the research reports them and preview
    (hooks written so far) while a running run holds one. Pass since with the last
    progress.updated_at to return on change or completion. It must be an ISO timestamp
    with a timezone (422 invalid_request otherwise). Hooks include transcript_kind:
    speech, music, none, other, or null when unknown. conversation_id names the chat conversation the run
    belongs to (null outside one). A done run with fewer hooks than asked for, or none,
    has outcome: why in one plain paragraph, what to try next, and whether it was free
    (stats.rejections holds the counts); a full run's outcome is null, or one sentence
    when some hooks are under the view floor because the country filter set clips aside.
    Free: reading a run costs no credits.

Input parameters:

- `api_key`
- `run_id` (string, required)
- `since`
- `wait_seconds` (integer): 0 to 50: seconds to wait for the run to finish; 0 returns at once

Output parameters:

- `result` (string)

### `list_runs` (~138 tokens)

Your research runs, newest first, without their hooks, limit 1 to 100 (default 20)
    per page. Returns {"runs": [...], "next_cursor"}: pass next_cursor back as cursor for
    the next page; it is null on the last. Same shape as GET /v1/runs. Next step: open one
    with get_run, or download it with export_run. Free. Errors: 422 invalid_cursor for a
    cursor this API did not issue.

Input parameters:

- `api_key`
- `cursor`
- `limit` (integer): page size, 1 to 100

Output parameters:

- `result` (string)

### `export_run` (~191 tokens)

A finished run's hooks as a file a creator can open: format "csv" (default; one row
    per hook, opens in any spreadsheet, formula-like cells defused with a leading quote),
    "json" (the full hook objects) or "md" (Markdown notes, one section per hook).

    Returns {"filename", "content_type", "format", "content"}, where content is the whole
    file as text: write it to filename. The same bytes as GET
    /v1/runs/{run_id}/export?format=... . Free.
    Errors: 404 run_not_found, 409 run_not_finished while the run is queued or running
    (call get_run with wait_seconds=50 first), 422 invalid_request for another format.

Input parameters:

- `api_key`
- `format` (string)
- `run_id` (string, required)

Output parameters:

- `result` (string)

### `get_hook` (~59 tokens)

One hook with every field: its opening line, transcript, watch_url and the
    vertical player_url (null for a photo post). hook_id comes from get_run. Free.

Input parameters:

- `api_key`
- `hook_id` (string, required)

Output parameters:

- `result` (string)

### `embed_hook` (~104 tokens)

Official TikTok or Instagram embed HTML for a hook, so the clip plays inside your
    own page. kind is "embed", or "fallback" when the creator disabled embedding or the
    clip is a photo post (then show thumbnail, which falls back to the hook's still_url,
    and the transcript). For a plain vertical iframe use the hook's
    player_url instead. Free.

Input parameters:

- `api_key`
- `hook_id` (string, required)

Output parameters:

- `result` (string)

### `keep_hook` (~71 tokens)

Keep or reject a hook: verdict is "keep" (default) or "reject", and the last
    verdict wins. Kept hooks come back from list_keeps, across all runs. Free.

Input parameters:

- `api_key`
- `hook_id` (string, required)
- `verdict` (string)

Output parameters:

- `result` (string)

### `clear_decision` (~119 tokens)

Undo keep_hook: the hook goes back to having no verdict, so it leaves list_keeps and
    its export row carries none. Returns {"hook_id", "verdict": null}. Clearing a hook with
    no decision is the same success, so a retry is safe. Same as DELETE
    /v1/hooks/{hook_id}/decision. Free. Errors: 404 hook_not_found, 422 for an id that is
    not a UUID.

Input parameters:

- `api_key`
- `hook_id` (string, required)

Output parameters:

- `result` (string)

### `list_keeps` (~140 tokens)

The hooks you kept with keep_hook, newest decision first, across all runs or within
    one run_id, limit 1 to 100 (default 100) per page. Returns {"kept": [...], "count": n
    on this page, "next_cursor"} with full hook objects; pass next_cursor back as cursor
    for more. Same shape as GET /v1/keeps. Free. Errors: 422 for a bad run_id or cursor.

Input parameters:

- `api_key`
- `cursor`
- `limit` (integer): page size, 1 to 100
- `run_id`

Output parameters:

- `result` (string)

### `balance` (~104 tokens)

Your account and remaining credits right now: account_id, credits, key_prefix and
    key_id of the key making this call, label, created_at, and that key's scopes,
    credit_limit and expires_at. Same shape as GET /v1/me. A
    run needs at least 40 credits and reserves up to 200. For spend, credits held by
    in-flight runs and daily history, call usage. Free.

Input parameters:

- `api_key`

Output parameters:

- `result` (string)

### `delete_account` (~164 tokens)

Erase your account and everything in it, for good: every API key, run, hook, keep
    or reject decision, conversation and message. Unspent credits go with it. It cannot
    be undone.

    confirm must be exactly "delete my account", or nothing happens (422
    confirmation_required). Refused with 409 run_in_flight while a run is queued or
    running: wait for it with get_run first. Returns {"deleted": true, "account_id",
    "erased": {counts per kind}, "message"}. Every key of the account stops working at
    once; create_account starts a new one. Same as DELETE /v1/account. Free.

Input parameters:

- `api_key`
- `confirm`

Output parameters:

- `result` (string)

### `chat` (~286 tokens)

One conversational turn, for an agent relaying a person's words. It either asks
    ONE clarifying question (action "clarify"), answers about delivered hooks or the tool
    (action "answer", run null, no credits), or starts a research run (action
    "research", with the started run under "run"), exactly like POST /v1/chat.
    Every response has suggestions: 0 to 4 follow-ups, [] for research.

    message is 1 to 4000 characters. Pass conversation_id from an earlier turn to keep
    the context; leave it out to start a conversation. Cost: a turn itself is free but is
    a model call, so turns are limited per account per hour (429 rate_limited); a turn
    that starts a run reserves credits like find_hooks. Next step: on "research", call
    get_run with run.run_id and wait_seconds=50. Errors: 503 model_unavailable when the
    model is down (nothing charged, try again), 402 insufficient_credits. For direct
    research without the question step, call find_hooks instead. Needs the write
    permission, and research for a turn that starts a run (403 insufficient_scope, nothing
    reserved).

Input parameters:

- `api_key`
- `conversation_id`
- `message` (string, required)

Output parameters:

- `result` (string)

### `list_conversations` (~118 tokens)

Your chat conversations, most recently active first, limit 1 to 100 (default 30)
    per page: conversation_id, title, created_at, updated_at and messages (the count).
    Returns {"conversations": [...], "next_cursor"}; pass next_cursor back as cursor for
    more. Same shape as GET /v1/conversations. Next step: get_conversation. Free.

Input parameters:

- `api_key`
- `cursor`
- `limit` (integer): page size, 1 to 100

Output parameters:

- `result` (string)

### `get_conversation` (~91 tokens)

One chat conversation: every message in order (role, content, run_id, created_at)
    and every run it holds, with their hooks. Same shape as GET
    /v1/conversations/{conversation_id}. Free. Errors: 404 conversation_not_found, 422 for
    an id that is not a UUID.

Input parameters:

- `api_key`
- `conversation_id` (string, required)

Output parameters:

- `result` (string)

### `usage` (~93 tokens)

Your credits and what you have spent: credits (balance now), spent_total,
    reserved_now (credits held by runs still in flight, refunded in part when they
    finish), runs_total, last_30_days (one entry per UTC day: date, runs, charged) and
    recent (your 20 newest runs). Same shape as GET /v1/usage. Free.

Input parameters:

- `api_key`

Output parameters:

- `result` (string)

### `list_keys` (~113 tokens)

Your API keys and what each may do. keys: key_id, name, prefix, created_at,
    last_used_at and current (true for the key making this call). access, per key_id:
    scopes, credit_limit, credits_used, expires_at and expired. scopes: the calling key's.
    The original signup key has key_id "original". The keys themselves are never shown
    again. Needs admin. Same shape as GET /v1/keys. Free.

Input parameters:

- `api_key`

Output parameters:

- `result` (string)

### `create_key` (~296 tokens)

Create another API key on your account, for example one per agent or machine, so
    you can revoke one without touching the others. name is up to 60 characters.

    Give the key only what it needs. scopes: any of "read" (read runs, hooks, keeps,
    usage), "write" (keep, reject, undo, chat), "research" (start runs, which spend
    credits), "admin" (manage keys, delete the account); all four when left out. An agent
    that finds hooks needs ["read", "write", "research"]. credit_limit: the most credits
    runs started with this key may spend over its life, 1 to 100000 (a run then reserves
    at most what is left). expires_in_days: 1 to 365; the key then stops working (401
    key_expired).

    The new key is in this result once and never again: store it now. Needs admin. An
    account holds at most 10 active keys (409 beyond) and may create 20 per hour by
    default (429 beyond). Free.
    Next step: use the returned api_key; revoke old ones with revoke_key.

Input parameters:

- `api_key`
- `credit_limit` (integer)
- `expires_in_days` (integer)
- `name`
- `scopes` (array)

Output parameters:

- `result` (string)

### `revoke_key` (~150 tokens)

Revoke one of your API keys at once: key_id from list_keys, or "original" for the
    signup key. Any key may revoke itself (the result says so, and that key stops working
    immediately); revoking another needs admin. Never the last active key on the account
    (409): create another with create_key first.

    If a key leaked, pass key_id "others": every key of the account except the one you
    are calling with is revoked at once, and the result says how many (revoked_count) and
    which key is kept. Needs admin. Free.

Input parameters:

- `api_key`
- `key_id` (string, required)

Output parameters:

- `result` (string)

## Diagnostics

Captured diagnostic sections: TLS, DNSSEC, Authorisation, Transports. The full working is on the page: https://verifymcp.io/servers/com-hookdetector-hookdetector/api#diagnostics

## Score history

- 2026-09-26: 63
- 2026-09-25: 63

## Common questions

### What is the Hook Detector MCP server?

Hook Detector is an MCP server listed in the public MCP registry as com.hookdetector/hookdetector. Real TikTok and Instagram hooks that already work, in any language, with why each travelled. This page covers its hosted endpoint (https://api.hookdetector.com/mcp).

### Is the Hook Detector MCP server safe to use?

Hook Detector scores 63 out of 100 on VerifyMCP. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the Hook Detector MCP server expose?

Hook Detector exposes 19 tools: create_account, find_hooks, get_run, list_runs, export_run, and 14 more. Their descriptions and schemas cost roughly 3,845 tokens of context every time the server is loaded.

### Does the Hook Detector MCP server require authentication?

No. We connected to Hook Detector without credentials and it answered, so anything it exposes is reachable by anyone who knows the address.

### Is the Hook Detector MCP server still maintained?

Hook Detector is still listed as active in the MCP registry. We last reached this channel on 26 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

## Links

- Remote endpoint: https://api.hookdetector.com/mcp
- Website: https://hookdetector.com/
- Changelog RSS feed: https://verifymcp.io/servers/com-hookdetector-hookdetector/api.xml
- Changelog JSON feed: https://verifymcp.io/servers/com-hookdetector-hookdetector/api.json
- HTML version of this page: https://verifymcp.io/servers/com-hookdetector-hookdetector/api
