# AIHawk (pypi · aihawk)

AI browser agent: browses, clicks, types, and reads real web pages from plain-English instructions.

- Trust score: 68/100 (medium)
- Change this week: +18
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-20

## Components

- pypi · `aihawk`: 68/100 (this document), [markdown](https://verifymcp.io/servers/feder-cr-aihawk/aihawk.md), [page](https://verifymcp.io/servers/feder-cr-aihawk/aihawk)

## Channel facts

- Registry: `pypi`
- Package: `aihawk`
- Version: `0.68.9`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, 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-20.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - Runs hatchling.build at install time, a recognised native-build step with no shell scripting around it.
  - 2 of 48 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 32/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - License check failed: no license is declared.
  - Actively maintained (last published 1 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 63/100
  - AI-judged instruction clarity (good).
  - Context-footprint check failed: tool/resource definitions use about 3197 tokens (~199/item across 16 items; 16 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 27/100
  - Stability observed for 8 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 84/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 44% of tool parameters carry a description.
  - Structured output schemas are declared (81% of tools); any adoption earns full credit.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - We read all 16 captured tool definition(s), and no name or description among them implies an irreversible operation.
  - An AI judge read all 17 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### How do I install the AIHawk MCP server?

AIHawk runs locally as a PyPI package, launched with uvx aihawk. 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 feder-cr-aihawk -- uvx aihawk
```

### Cursor

```json
{
  "mcpServers": {
    "feder-cr-aihawk": {
      "command": "uvx",
      "args": [
        "aihawk"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "feder-cr-aihawk": {
      "command": "uvx",
      "args": [
        "aihawk"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add feder-cr-aihawk -- uvx aihawk
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "feder-cr-aihawk": {
      "type": "local",
      "command": [
        "uvx",
        "aihawk"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add feder-cr-aihawk --command uvx --arg aihawk
```

### Hermes

```yaml
mcp_servers:
  feder-cr-aihawk:
    command: "uvx"
    args: ["aihawk"]
```

### Netclaw

```json
{
  "McpServers": {
    "feder-cr-aihawk": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "aihawk"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add feder-cr-aihawk -t stdio -c uvx -a aihawk
```

### Other

```json
{
  "mcpServers": {
    "feder-cr-aihawk": {
      "command": "uvx",
      "args": [
        "aihawk"
      ]
    }
  }
}
```

## 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-20 (score 68, 0)

- [functional regression] Security disclosure: unverified → fail

### 2026-09-19 (score 68, +16)

- [security improvement] Malware scan: unverified → pass
- [functional regression] Security disclosure: fail → unverified

### 2026-09-18 (score 52, −15)

- [security regression] Malware scan: pass → unverified
- [security regression] Tool safety: pass → unverified
- [security regression] Stability: 0.17 → unverified
- [functional regression] Capabilities: pass → unverified
- [functional regression] Tool coverage: 100 → unverified
- [functional] First check of Schema quality: unverified
- [functional] Package version: 0.68.4 → 0.68.9
- [functional] Package version: 0.68.4 → 0.68.8
- [functional] Package version: 0.68.4 → 0.68.7

### 2026-09-17 (score 67, +16)

- [security regression] Stability: 0.13 → unverified
- [security regression] Tool safety: pass → unverified
- [security improvement] Malware scan: unverified → pass
- [functional regression] Capabilities: pass → unverified
- [functional regression] Tool coverage: 100 → unverified
- [functional] First check of Schema quality: unverified
- [functional] Package version: 0.68.3 → 0.68.4

### 2026-09-16 (score 51, −15)

- [security regression] Malware scan: pass → unverified
- [functional] Package version: 0.68.0 → 0.68.3
- [functional] Package version: 0.68.0 → 0.68.1

### 2026-09-15 (score 66, 0)

- [security regression] Stability: 0.07 → unverified
- [security regression] Tool safety: pass → unverified
- [functional regression] Capabilities: pass → unverified
- [functional regression] Tool coverage: 100 → unverified
- [functional] First check of Schema quality: unverified
- [functional] Package version: 0.64.0 → 0.68.0
- [functional] Package version: 0.64.0 → 0.67.0
- [functional] Package version: 0.64.0 → 0.66.0

### 2026-09-14 (score 66, +16)

- [security regression] Stability: 0.03 → unverified
- [security regression] Tool safety: pass → unverified
- [security improvement] Malware scan: unverified → pass
- [functional regression] Capabilities: pass → unverified
- [functional regression] Tool coverage: 100 → unverified
- [functional improvement] Tool coverage: 0% → 44%
- [functional] First check of Schema quality: unverified
- [functional] Schema quality: excellent → good
- [functional] Package version: 0.49.0 → 0.64.0
- [functional] Package version: 0.49.0 → 0.63.0
- [functional] Package version: 0.49.0 → 0.62.0
- [functional] Package version: 0.49.0 → 0.58.0
- [functional] Package version: 0.49.0 → 0.57.0
- [functional] Package version: 0.49.0 → 0.54.0
- [functional] Package version: 0.49.0 → 0.53.1
- [functional] Package version: 0.49.0 → 0.52.0
- [functional] Package version: 0.49.0 → 0.51.0
- [functional] Package version: 0.49.0 → 0.50.0

### 2026-09-13 (score 50, +12)

- [security improvement] Malware scan: unverified → pass
- [security improvement] Judged manipulation: unverified → pass
- [functional improvement] Schema quality: unverified → excellent
- [functional improvement] Stability: unverified → 0.03
- [functional] Package version: 0.43.0 → 0.49.0
- [functional] Package version: 0.43.0 → 0.48.0
- [functional] Package version: 0.43.0 → 0.47.0
- [functional] Package version: 0.43.0 → 0.45.0
- [functional] Package version: 0.43.0 → 0.44.0

## MCP tools (16)

### `browser_open` (~270 tokens)

Open `main` or `support`, or reopen one as somebody else.

\`support` is yours to manage: open it when the task needs a second
identity, and close it with browser_close as soon as the task no longer
needs it, before you answer. It is not saved.

Called on a browser that is already up, this REOPENS it with the settings
given, and what it held is gone.

seed     the identity; same seed, same fingerprint. Left out, one is drawn.
profile  a directory keeping cookies, logins and the seed between opens;
         "" means none. It keeps the SEED too, so a login does not come
         back on different hardware every visit.
proxy    the exit, `http://user:pass@host:port` or `socks5://host:port`;
         "" means this machine's own address; left out for `support`, it
         shares the exit `main` has. A profile does NOT pin its exit, and
         a login arriving from a new country is as visible as one arriving
         on new hardware.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.
- `profile`
- `proxy`
- `seed`

Output parameters:

- `result` (string)

### `browser_close` (~85 tokens)

Close one browser and free what it was holding.

The page it had is gone with it. The other browser is not touched.

Who it was is kept: browser_open with no arguments brings the same person
back. To be somebody else, pass a seed, a proxy or a profile.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.

Output parameters:

- `result` (string)

### `browser_list` (~150 tokens)

Which of the two browsers are open, where each one is, and which one
you are working in.

Answers JSON: `focus`, the browser your last command acted in, or "" when
none is open; `note`, which says how many are open and that a command
naming no browser goes to `main`; and `browsers` - each row `id`, `url`
(the page it is on) and `urls` (every page it holds, which is more than
one only when a site opened one). Only open browsers are listed, so every
row is one you can act on.

Starts nothing: it reports what is open, so asking is free.

Output parameters:

- `result` (string)

### `browser_status` (~121 tokens)

Who is browsing right now: the identity, the exit, the profile and the page.

Ask whenever you need to know which person the browser currently is, or
from where its traffic leaves. The seed is what you would pass to
\`browser_open` to become this person again, so this is also how you
record an identity worth repeating.

It starts nothing: a browser that is not open, or gone, is answered with
the sentence that says which.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.

Output parameters:

- `result` (string)

### `browser_navigate` (~189 tokens)

Go to a url in this browser's page, opening it if none exists.

Answers with the HTTP status the server gave and the url actually landed
on, which is not always the one asked for: a redirect to a login wall or a
regional domain shows up here. Read the status before trusting the page -
a 404 or a 403 still has a document, and reading it as content is the
mistake this reply exists to prevent.

wait_until is "domcontentloaded" by default, which returns as soon as the
markup is parsed. Use "load" when the page needs its images and stylesheets,
or "networkidle" for a single-page app that fetches its content after
load.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.
- `url` (string, required)
- `wait_until` (string)

Output parameters:

- `result` (string)

### `browser_read_text` (~131 tokens)

The visible text of an element, with the markup gone.

The cheapest way to read a page. Narrow the selector when you know where the
answer is; use browser_read_html instead when the structure matters, or
browser_snapshot when you need something to click.

Long text is cut at max_chars (6000 by default) and the cut is marked in
what comes back, so text that ends without that marker is the whole thing.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.
- `max_chars` (integer)
- `selector` (string)

Output parameters:

- `result` (string)

### `browser_snapshot` (~192 tokens)

Title, url, and the interactive elements that are actually visible.

Each element carries a `selector` when one can reach it: pass that string to
browser_click or browser_type VERBATIM rather than writing your own. It is
built to match exactly ONE element, which the obvious selector often does
not, and the driver acts on the first match - so a caller aiming at the
third of five identical links would silently hit the first and be told it
succeeded.

Elements with no `selector` carry `at`, the centre coordinates, for
browser_click_at.

It lists what a caller can act on, and it is not the accessibility tree:
one country `<select>` would otherwise fill the answer with its options
before the form you were looking for appears.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.
- `max_chars` (integer)

Output parameters:

- `result` (string)

### `browser_read_html` (~201 tokens)

The page's HTML, cleaned down to what is worth reading.

Use this when the STRUCTURE matters - a form and its labels, a table, what
a control is wired to. `browser_snapshot` gives a flat inventory of things
to click; this keeps the markup and the relationships inside it.

mode="form" keeps the interactive surface and the text explaining it,
mode="text" returns the prose alone, mode="full" keeps the structure with
the noise and the attribute soup removed.

Unlike browser_read_text this is NOT capped: it returns the whole reduced
page, tens of thousands of characters on a large one. Cutting markup in the
middle leaves tags that mean nothing, so it is not cut - but the answer can
be long. Reach for browser_snapshot when you only need something to click.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.
- `mode` (string)

Output parameters:

- `result` (string)

### `browser_take_screenshot` (~39 tokens)

One screenshot of this browser's page, on demand.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.

### `browser_watch` (~145 tokens)

The whole browser window as a person at the machine sees it: tab strip,
address bar, the page and the pointer, from a live capture kept running on
that page. For watching the work, not for acting on it: the picture
is window pixels, so do not feed its coordinates to browser_click_at; use
browser_take_screenshot for that.

Starts nothing. A browser that is not open has no window, so this answers
the sentence that says so, and the live panes - which call this many times
a second - read that sentence as the idle pane.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.

### `browser_click` (~75 tokens)

Click the first element matching a CSS selector.

Scrolls it into view and waits for it to be clickable. When no selector can
describe the target, use browser_click_at with coordinates from
browser_snapshot.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.
- `selector` (string, required)

Output parameters:

- `result` (string)

### `browser_click_at` (~206 tokens)

Click (or press-and-hold) a raw viewport coordinate instead of a
selector - for targets a selector cannot reliably reach: a slider track, a
canvas-drawn captcha, a precise point inside a wider element. Moves the
pointer there first (no teleport), then down, then up, holding first if
hold_seconds is set. Returns a screenshot taken right after release.

Coordinates are relative to the VIEWPORT, not to the page, so the ones in a
snapshot go stale the moment anything scrolls. Nothing raises when that
happens: the click lands on whatever is at that spot now. Take a fresh
snapshot after anything that could have moved the page, and prefer
browser_click with the element's `selector` whenever it has one.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.
- `hold_seconds` (number)
- `x` (number, required)
- `y` (number, required)

### `browser_type` (~89 tokens)

Fill a field, replacing whatever it holds.

This sets the value rather than typing key by key, so it will not fire the
per-keystroke handlers an autocomplete needs. For those, click the field and
use browser_press_key.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.
- `selector` (string, required)
- `text` (string, required)

Output parameters:

- `result` (string)

### `browser_select_option` (~108 tokens)

Choose an option in a dropdown (`<select>`), by its visible label or by
its value.

Use this rather than clicking the dropdown and pressing arrow keys: a click
plus arrows cannot tell you which row it landed on, and setting the value
through browser_evaluate changes it without the page seeing a real
interaction.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.
- `selector` (string, required)
- `value` (string, required)

Output parameters:

- `result` (string)

### `browser_press_key` (~64 tokens)

Press a key on whatever has focus: "Enter", "Tab", "Escape",
"ArrowDown", "Control+a", or a single character.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.
- `key` (string, required)

Output parameters:

- `result` (string)

### `browser_evaluate` (~220 tokens)

READ from the page with JavaScript and get the result as JSON.

For what the other tools cannot see: a computed style, a value held in a
framework's state, the length of a list.

Acting on the page is refused, and the refusal names the tool to use.
Assigning to `value`, `checked` or `selected`, or calling `click()`,
\`dispatchEvent()`, `submit()` or `requestSubmit()`, changes the page without
a real keystroke or pointer, and a page can tell. Use browser_click,
browser_type or browser_select_option instead; they do the same thing
through the pointer and the keyboard. Reading any of those properties is
fine.

The refusal catches the obvious spellings, not every possible one. A script
that slips past it is still the wrong way to do the thing: report it in your
answer rather than using it.

Input parameters:

- `browser`: Defaults to `main`; `support` is the helper beside it.
- `expression` (string, required)

Output parameters:

- `result` (string)

## Diagnostics

Captured diagnostic sections: Provenance, Install scripts, Dependencies. The full working is on the page: https://verifymcp.io/servers/feder-cr-aihawk/aihawk#diagnostics

## Score history

- 2026-09-20: 68
- 2026-09-19: 68
- 2026-09-18: 52
- 2026-09-17: 67
- 2026-09-16: 51
- 2026-09-15: 66
- 2026-09-14: 66
- 2026-09-13: 50
- 2026-09-12: 38

## Common questions

### What is the AIHawk MCP server?

AIHawk is an MCP server listed in the public MCP registry as io.github.feder-cr/aihawk. AI browser agent: browses, clicks, types, and reads real web pages from plain-English instructions. This page covers its PyPI package (aihawk).

### Is the AIHawk MCP server safe to use?

AIHawk scores 68 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 20 September 2026. 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 AIHawk MCP server expose?

AIHawk exposes 16 tools: browser_open, browser_close, browser_list, browser_status, browser_navigate, and 11 more. Their descriptions and schemas cost roughly 2,285 tokens of context every time the server is loaded.

### Is the AIHawk MCP server still maintained?

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

## Links

- PyPI project: https://pypi.org/project/aihawk/
- Socket report: https://socket.dev/pypi/package/aihawk
- Repository: https://github.com/feder-cr/AIHawk
- Changelog RSS feed: https://verifymcp.io/servers/feder-cr-aihawk/aihawk.xml
- Changelog JSON feed: https://verifymcp.io/servers/feder-cr-aihawk/aihawk.json
- HTML version of this page: https://verifymcp.io/servers/feder-cr-aihawk/aihawk
