Skip to main content
Glama
creatorpiyush

mcp-web-agent

README.md
# webmesh-mcp

A token-economical web browsing, scraping, and crawling suite delivered as a
standard [MCP](https://modelcontextprotocol.io/) server. Any MCP-capable agent
(Claude, Cursor, Cline, etc.) can use it as ordinary tools with no bespoke
integration.

## Why this is cheaper than a full browser loop

| Strategy | Saving |
|---|---|
| **Tiered fetching** — static HTTP first, browser only when HTML is thin | Skips Chromium entirely for the majority of pages |
| **Markdown output, not raw HTML** — noise/nav/ads stripped server-side | 300 KB page → ~8 KB of clean markdown |
| **Schema extraction** — field → CSS selector returns only the values you asked for | No markdown conversion, no LLM reasoning |
| **Boolean verification** — `web_check` / `web_diff` never send back full page content | `pass: true` + 200-char evidence snippet |
| **ARIA snapshot over screenshot** — `web_interact` returns the accessibility tree | Image tokens only when you explicitly ask |
| **Persistent browser pool** — one Chromium process per server lifetime | ~1-2 s launch cost paid once, not per call |
| **robots.txt + rate limiting** — per-host queues and rule caching | Polite crawling without throttling your agent |

---

## Tools

| Tool | What it does | Browser needed? |
|---|---|---|
| `web_scrape` | Fetch a URL → clean markdown or schema-based JSON | Only if JS-rendered |
| `web_check` | Assert text/element present or absent → `pass: bool` + evidence | Only if JS-rendered |
| `web_diff` | Has this page changed since last check? → `changed: bool` + snippet | Only if JS-rendered |
| `web_interact` | Click / fill / select / press / waitFor sequence → ARIA snapshot | Always |
| `web_session_close` | Delete persisted cookies for a `sessionId` | No |
| `web_crawl` | BFS crawl from a seed URL → titles, links, excerpts, cached markdown | Only if JS-rendered |
| `web_crawl_get_page` | Retrieve full markdown cached by a prior `web_crawl` call | No |

---

## Installation

```bash
npm install -g webmesh-mcp
# or use directly with npx (no global install needed):
npx webmesh-mcp
```

### Chromium (optional — only needed for JS-rendered pages)

`webmesh-mcp` uses [`playwright-core`](https://www.npmjs.com/package/playwright-core)
and does **not** bundle a browser. You have three options:

**A) Install Playwright's managed Chromium** (simplest):
```bash
npx playwright install chromium
```

**B) Use your system Chrome / Edge** — set `executablePath` in your MCP config (see below).

**C) Connect to a running browser** — Playwright supports CDP attach; pass `--cdp-endpoint`
flags in `args` if you want to hook into an already-running instance.

Static pages (most blogs, docs, GitHub, npm, etc.) never trigger the browser path at all.

---

## Connecting to your agent

Add to your MCP config (e.g. `~/.claude/claude_desktop_config.json`, `.cursor/mcp.json`, etc.):

```json
{
  "mcpServers": {
    "web-agent": {
      "command": "npx",
      "args": ["webmesh-mcp"]
    }
  }
}
```

Or, if you prefer to run from source:

```json
{
  "mcpServers": {
    "web-agent": {
      "command": "npx",
      "args": ["tsx", "/absolute/path/to/webmesh-mcp/src/index.ts"]
    }
  }
}
```

---

## Agent Skill Integration

This repository ships with a pre-configured Agent Skill in [`skills/web-agent/SKILL.md`](./skills/web-agent/SKILL.md).

It provides AI coding assistants (Claude Code, Antigravity, Cursor, Windsurf, Gemini CLI, etc.) with a complete decision tree, trigger keywords, and parameter guidance for using `webmesh-mcp` tools token-efficiently.

### How to use the skill in your project

Copy or symlink the `skills/` folder into your AI assistant's skills directory:

- **Claude / Antigravity / Gemini CLI**: Place in `.agents/skills/web-agent/SKILL.md` or `~/.gemini/config/skills/web-agent/SKILL.md`
- **Cursor**: Copy contents to `.cursor/rules/web-agent.mdc`
- **Windsurf**: Copy contents to `.windsurfrules`

---

## Tool reference

### `web_scrape`

| Parameter | Type | Default | Description |
|---|---|---|---|
| `url` | `string` (URL) | — | Page to fetch |
| `format` | `"markdown" \| "json"` | `"markdown"` | Output format |
| `schema` | `Record<string, string>` | — | `{field: cssSelector}` — required when `format="json"` |
| `selector` | `string` | — | Scope extraction to a CSS subtree |
| `forceBrowser` | `boolean` | `false` | Skip static tier, always use Chromium |
| `ignoreRobots` | `boolean` | `false` | Bypass `robots.txt` rules |

### `web_check`

| Parameter | Type | Default | Description |
|---|---|---|---|
| `url` | `string` (URL) | — | Page to check |
| `assertion` | enum | — | `contains`, `not_contains`, `selector_exists`, `selector_not_exists`, `text_equals` |
| `value` | `string` | — | Text or CSS selector |
| `selector` | `string` | — | Scope text search to subtree |
| `forceBrowser` | `boolean` | `false` | |
| `ignoreRobots` | `boolean` | `false` | |

### `web_diff`

| Parameter | Type | Default | Description |
|---|---|---|---|
| `url` | `string` (URL) | — | Page to monitor |
| `selector` | `string` | — | Scope watch to subtree (e.g. `.price`) |
| `forceBrowser` | `boolean` | `false` | |
| `ignoreRobots` | `boolean` | `false` | |

Returns `changed: null` on the first call (nothing to compare against),
`true`/`false` on subsequent calls.

### `web_interact`

| Parameter | Type | Default | Description |
|---|---|---|---|
| `url` | `string` (URL) | — | Starting URL |
| `actions` | `Action[]` | — | Sequence of browser actions |
| `resultSelector` | `string` | — | Scope returned ARIA snapshot |
| `screenshot` | `boolean` | `false` | Return base64 PNG (costs image tokens) |
| `sessionId` | `string` | — | Persist cookies/storage across calls |
| `ignoreRobots` | `boolean` | `false` | |

**Action shape:**
```ts
{ type: "click" | "fill" | "select" | "press" | "waitFor", selector?: string, value?: string, timeoutMs?: number }
```

### `web_session_close`

| Parameter | Type | Description |
|---|---|---|
| `sessionId` | `string` | Session to delete |

### `web_crawl`

| Parameter | Type | Default | Description |
|---|---|---|---|
| `startUrl` | `string` (URL) | — | Seed URL |
| `maxDepth` | `number` | `2` | Max link depth (0 = seed only) |
| `maxPages` | `number` | `30` | Hard cap on pages visited |
| `sameHostOnly` | `boolean` | `true` | Restrict to same hostname |
| `includePatterns` | `string[]` | — | Pathname globs URLs must match, e.g. `"/docs/*"` |
| `excludePatterns` | `string[]` | — | Pathname globs URLs must NOT match |
| `contentDepth` | `"none" \| "summary" \| "full"` | `"summary"` | Output verbosity |
| `ignoreRobots` | `boolean` | `false` | |

Full markdown for every visited page is cached in SQLite for `web_crawl_get_page`.

### `web_crawl_get_page`

| Parameter | Type | Description |
|---|---|---|
| `url` | `string` (URL) | Previously crawled URL |

---

## Environment variables

| Variable | Default | Description |
|---|---|---|
| `MCP_WEB_AGENT_DATA_DIR` | `<cwd>/.webmesh-mcp` | Root directory for cache DB and session files |

Both the SQLite watch/crawl cache (`cache/watch.sqlite`) and session state files
(`sessions/`) live under `MCP_WEB_AGENT_DATA_DIR`. Override it to control where
runtime data is stored.

---

## Security

### SSRF Protection

All outbound fetches — static HTTP, browser navigation, intermediate HTTP redirects, and crawl link-following — are strictly validated against an SSRF blocklist before network connections are established.

- **Protocol Restriction**: Rejects all non-HTTP/HTTPS schemes.
- **Loopback Addresses**: `127.x.x.x`, IPv6 `::1`, `localhost`, IPv6 unspecified `::` / `0:0:0:0:0:0:0:0`.
- **Private RFC-1918 & Unique Local IPv6**: `10.x`, `172.16–31.x`, `192.168.x`, `fc00::/7`, `fd00::/7`.
- **Link-Local & Cloud Metadata**: `169.254.x.x` (AWS/GCP/Azure IMDS), `fe80::/10`.
- **Special & Reserved Ranges**: Multicast (`224.x`), Site-Local (`fec0::/10`), Documentation (`2001:db8::/32`), and IPv4-mapped IPv6 formats (`::ffff:x.x.x.x`).
- **HTTP Redirect Hardening**: Static fetch enforces manual redirect validation loops up to 5 hops, checking `ssrfGuard.assertPublicUrl()` on every intermediate `Location` header before following.
- **Browser Route Interception**: Chromium contexts attach route interceptors (`page.route("**/*")`) to block subresource requests or redirects targeting private IP space.

---

## Architecture

For a detailed technical architecture and end-to-end data flow specification, see [`Architecture.md`](./Architecture.md).

```
index.ts (MCP server, stdio transport)
├── tools/scrape.ts        — web_scrape
├── tools/check.ts         — web_check
├── tools/diff.ts          — web_diff
├── tools/interact.ts      — web_interact
├── tools/crawl.ts         — web_crawl
├── tools/crawlGetPage.ts  — web_crawl_get_page
├── tieredFetch.ts         — static HTTP → browser escalation (manual redirect validation)
├── browserPool.ts         — singleton Chromium process + route interceptor (playwright-core)
├── ssrfGuard.ts           — SSRF protection (blocks private/reserved IPv4 & IPv6 addresses)
├── extract.ts             — HTML → clean markdown / plain text / schema JSON
├── hostGate.ts            — robots.txt parser (regex escaped) + per-host rate-limiting queue
├── sessions.ts            — disk-backed storageState persistence
├── cache.ts               — SQLite: watch hashes + crawled page markdown
└── constants.ts           — shared USER_AGENT, DATA_DIR
```

---

## Development

```bash
git clone https://github.com/creatorpiyush/webmesh-mcp
cd webmesh-mcp
npm install
npx playwright install chromium    # optional, for browser-tier testing

npm run dev        # run MCP server in dev mode (tsx, no compile step)
npm run demo -- https://example.com
npm test           # run the full integration test suite
npm run typecheck  # tsc --noEmit, no output files
npm run format     # format code with prettier
npm run build      # compile to dist/
```

---

## License

MIT — see [LICENSE](./LICENSE).

TDQS

A3.6/5.0

Scored across 7 tools

Disambiguation4/5

Most tools have distinct purposes: scraping, crawling, interaction, checking, diffing, and session management are clearly separate. However, web_scrape and web_crawl_get_page both return page content (one on-demand, one cached), and web_check vs web_diff both verify page state, creating slight overlap.

Naming Consistency3/5

All tools share a 'web_' prefix, but the structure varies: mostly verb (web_scrape, web_check, web_interact, web_crawl), but web_session_close is noun-verb and web_crawl_get_page is verb-noun-noun. The pattern is readable but not entirely consistent.

Tool Count5/5

Seven tools is well within the ideal range for a web agent server. Each tool serves a distinct function without redundancy, and the count feels appropriate for the domain of scraping, crawling, interaction, and monitoring.

Completeness4/5

The set covers core web operations: fetch, crawl, interact, check, diff, and session cleanup. Minor gaps include no explicit session creation tool (though implied by web_interact) and no dedicated screenshot tool (though web_interact supports screenshots). These are workable gaps, not critical failures.

Maintenance

ActivitySlowing
ResponsivenessNo issues