Webscope
A small MCP server (stdio, JSON-RPC 2.0, Python standard library only) that gives a coding agent short answers about a project: grouped search hits, one symbol instead of a whole file, and the output of the project's own read-only scripts, trimmed to head and tail. The goal is fewer tokens per question.
Tool names, parameters and messages are in Spanish.
Requirements
- Python 3.9 or newer (no third-party packages)
- Optional: uv, to run it with
uvxwithout cloning greponPATH(used bybuscar)
Installation
-
In your project's root, create its manifest and list the folders and scripts it may use (see Configuration and
webscope.example.json). -
Register the server. With Claude Code, from the project's root, pick one:
# with uv, no clone needed (like npx) claude mcp add --scope project webscope -- uvx --from git+https://github.com/partprogramming09/webscope webscope-mcp --manifest "$PWD/webscope.json" # with a clone and plain Python git clone https://github.com/partprogramming09/webscope.git ~/webscope claude mcp add --scope project webscope -- python3 -I ~/webscope/webscope.py --manifest "$PWD/webscope.json"
--scope projectwrites.mcp.jsonso the whole team gets it; use--scope userfor all your projects. Add-e WEBSCOPE_ESCRITURA=1before--only if a script declaresescribeand you want to allow it. -
Start
claudein the project, approve the server, and check it with/mcp:webscopeshould show as connected with the toolsbuscar,leerandcorrer.
Other MCP clients: add the same command to their configuration, for example a .mcp.json:
{
"mcpServers": {
"webscope": {
"command": "python3",
"args": ["-I", "/path/to/webscope/webscope.py", "--manifest", "/path/to/your/project/webscope.json"]
}
}
}
Instead of --manifest you can set the environment variable WEBSCOPE_MANIFEST. Without either, the server uses
./webscope.json if it exists; otherwise it serves the current folder with buscar and leer and no scripts.
Docker
docker build -t webscope .
docker run -i --rm -v "$PWD:/project:ro" -e WEBSCOPE_MANIFEST=/project/webscope.json webscope
Without WEBSCOPE_MANIFEST the image serves the bundled webscope.example.json.
Tools
| Tool | What it does |
|---|---|
buscar(texto, donde?, tope?, formato?, contexto?, regex?) |
Case-insensitive search, literal unless regex=true (extended regex). Hits grouped by file, most-referenced files first, capped at tope lines. donde limits it to a folder or file; contexto (0-5) adds lines around each hit. formato="conciso" returns only the top 5 files with counts. |
leer(archivo, simbolo?, lineas?, max_lineas?, cola?, contiene?, formato?) |
Returns only one function, method or class (PHP, TS, JS, Vue, Python) — or several, if simbolo is a list —, one Markdown section by heading, a line range (lineas="120-180"), or for .log/.txt/.out files the last cola lines and/or the lines containing contiene. Without simbolo or lineas it returns the file's index: headings or definitions with their line numbers. |
correr(nombre?, args?, detalle?) |
Runs a script declared in the manifest and returns exit N plus trimmed output. Without nombre it lists the scripts; detalle="esquema" shows one script's usage. Read-only. |
correr_escritura(nombre, args) |
Only listed when a script declares escribe. Runs a write argument; requires WEBSCOPE_ESCRITURA=1 in the server environment and is marked destructiveHint and anthropic/requiresUserInteraction. |
Every answer starts with one short line saying what it is and what to do next, and is capped at tope_tokens
(about 4 characters per token).
A malformed request, a non-UTF-8 script output or an unexpected error comes back as an error message; the server keeps
running. buscar skips files that are not valid UTF-8 when the locale is UTF-8 (grep treats them as binary).
Security model
- Project root only.
buscarandleerresolve paths against the manifest'srootand reject anything outside it. - Secret files are invisible.
buscarandleerskip.env,.env.*,*.pem,*.key,*.p12,id_rsa*,id_ed25519*,auth.jsonandcredentials*(templates ending in.examplestay visible). - Fixed commands, no shell. Scripts run as a fixed argument list from the manifest. Arguments that are absolute paths
or contain
..are rejected (unless the script setsacepta_barra, which allows URL-like/a/bbut still not..). Flags listed inrechazaare rejected. - Read-only by default.
correrrefuses any argument listed in a script'sescribe. Those go throughcorrer_escritura, which only exists when declared and only runs when the user setsWEBSCOPE_ESCRITURA=1. - Minimal environment. Scripts do not inherit the server's environment. They receive only
PATH,HOME,LANG,LC_ALL,TMPDIR, the variables named in the manifest'senv_permitidas, plusWEBSCOPE_RAIZ(project root) andWEBSCOPE_MANIFIESTO(manifest path). - Bounded output. Script output is trimmed to head and tail lines, and each response to
tope_tokens.
The scripts themselves are trusted code: Webscope limits how they are called, not what they do. Only list scripts you would run yourself.
Configuration (webscope.json)
{
"root": ".",
"recorte": { "cabeza": 10, "cola": 70 },
"tope_tokens": 5000,
"buscar": { "dirs": ["src"], "extensiones": ["py", "ts"] },
"env_permitidas": ["DATABASE_URL"],
"scripts": {
"report": {
"cmd": ["python3", "-I", "scripts/report.py"],
"descripcion": "Summarises a table",
"uso": "<table>",
"rechaza": ["--out"],
"escribe": ["--fix"],
"timeout": 120
}
}
}
| Key | Meaning |
|---|---|
root |
Project root, relative to the manifest's folder. |
recorte |
Default head (cabeza) and tail (cola) lines kept from script output. |
tope_tokens |
Maximum size of any response, in approximate tokens. |
buscar.dirs, buscar.extensiones |
Folders and file extensions searched by default. |
env_permitidas |
Extra environment variables passed to scripts. |
scripts.<name>.cmd |
Fixed command as a list; python3 is replaced by the server's interpreter. |
descripcion, uso |
Shown by correr() listings. |
rechaza |
Flags that would write files; always refused. |
escribe |
Arguments that write data; only via correr_escritura with WEBSCOPE_ESCRITURA=1. |
acepta_barra |
Allow arguments starting with / (URL paths). |
timeout, cabeza, cola |
Per-script overrides. |
Included scripts
The package ships ready-made scripts in webscope_adaptadores/. A manifest names them with {adaptadores} in cmd
(see webscope.example.json); copy the entries you need and adjust the folders.
| Script | What it answers | Stack |
|---|---|---|
vue_ts.py (mapa) |
Routes, views, API calls, composables and stores of a frontend module; --recorrido lists what can be clicked (actions with their v-if and permission, links, tabs with their API, form fields); --cobertura says which calls an e2e capture exercised |
Vue 3 + TS |
laravel.py (mapa_back) |
Endpoint, controller, service, model and table behind a frontend module or API path | Laravel |
e2e_chrome.py |
JavaScript for one javascript_tool call of Claude in Chrome: walks routes and returns per route time, rows, failed requests, console errors and UI problems, without screenshots. --llamar reuses the walk already installed in the page; --captura/--detalle record what the page sends and receives; --form installs __f to fill a form by label, press Save and read it back, plus screen helpers that replace screenshots and coordinate clicks: estado() (the screen as short text), clic(text) (click by visible text, then wait until the page settles), esperar, ir(route) (router navigation without reload) and pasos([...]) (several steps in one call). With a local receiver: --servir [port folder] starts it, --cargar [port] installs capture and __f from it, --guardar <name> [port] sends the whole capture to <folder>/<name>.txt (with one ms <ms> <method> <status> <path> line per API call; the receiver answers with the 5 slowest) and the steps __f recorded to .claude/webscope/recorridos/<name>.json, and --repetir <name> replays those steps in one call (with a fresh stamp on ZZ_E2E values) while the frontend does not change; --exportar <capture> <url> writes a Playwright test with the same steps |
Any SPA with a router |
payload_check.py |
Checks a capture against the FormRequest rules, the Resource and the table columns; flags keys without a rule, values outside in:, '' sent to a nullable column |
Laravel |
limpiar_zz.py |
Lists, and with --borrar deletes after a backup, test rows whose code starts with ZZ_ |
Laravel |
costo_sesion.py |
Cost of the latest (or a given) Claude Code session in the project: USD per model at list prices (subagents and advisor included), calls and re-read context per tool, large outputs, repeated calls, and which tool loops would have been cheaper in a subagent | Claude Code |
uso_agentes.py |
Subagent usage: turns, context, models, agents over the turn limit | Claude Code |
uso_webscope.py |
Share of file reads and searches that went through leer and buscar in sessions where Webscope was available, and the Read/sed/grep calls used instead |
Claude Code |
Project-specific settings go in the manifest's proyecto section:
| Key | Used by | Meaning |
|---|---|---|
modulos |
payload_check |
Backend modules folder (default app/Modules) |
prefijo_rutas |
e2e_chrome |
Prefix the app lives under, e.g. /demo, so routes work with or without it |
contenedor |
mapa_back |
Docker container where php artisan route:list --json runs; empty reads the route files |
sql |
payload_check, limpiar_zz |
Script that runs a read-only SQL query and prints TSV with a header |
tinker |
limpiar_zz |
Script that runs a PHP file against the database (<script> <file.php> --commit) |
php_db |
limpiar_zz |
PHP expression for the connection (default DB::connection()) |
capturas |
MCP resources | Captures folder (the one given to --servir); its .txt files are listed as webscope://capturas/<file> next to the saved walks webscope://recorridos/<file> |
The Vue index cache and limpiar_zz backups go to ~/.cache/webscope/. The e2e_chrome receiver writes captures to the
folder given to --servir (default capturas, relative to where it starts). --servir does not return: start it from a
shell in the background (the example manifest lists it in rechaza, so correr refuses it).
Using the scripts from a subagent
A subagent sees Webscope only if its definition lists the tools, for example in Claude Code's .claude/agents/<name>.md:
tools: Read, Grep, Glob, Bash, mcp__webscope__buscar, mcp__webscope__leer, mcp__webscope__correr
model: sonnet
Add mcp__webscope__correr_escritura only if the agent must run write arguments, plus the browser tools it needs.
agents/recorrido.example.md is a ready-made agent that walks one frontend module in
Chrome with mapa, e2e_chrome and limpiar_zz and returns a summary of at most 25 lines: copy it to
.claude/agents/recorrido.md and replace <webscope> (the clone's folder; the receiver runs from the clone, not from
uvx) and the captures folder.
One measurement (a browser walk of one module, one project, one run per condition) found:
- With the walk in the main session, Webscope did not change the cost (+7 % and −5 % in two runs). It changed accuracy:
the order of steps (
mapa --recorrido), the fields that were not saved (__f.verificar) and the measured coverage. - The same walk in a sonnet subagent that returns a summary of at most 25 lines cost 1.05 USD instead of 6.40, with the same coverage. About 2× came from isolating the context; the rest from the sonnet price.
- Page reads and screenshots were about 80 % of the re-read context, so the agent reads state with
javascript_tooland takes screenshots only to understand an error. - Subagents that ran for more than about 40 turns grew their context like a main session. Give one module or one bounded task per agent, and tell it to stop at about 40 tool calls and return what is done and what is pending.
costo_sesion.py --raiz <project> and uso_agentes.py --raiz <project> measure the same in another project. The USD
come from the price table at the top of costo_sesion.py (list prices, October 2026), which only lists models checked
against the cost reported by claude -p (Opus 5.5, Sonnet 5.5, Fable 5.1: within 0.01 USD on three runs); any other
model is shown as sin precio until it is added.
Hook: suggest Webscope
Agents often keep using Read, cat, sed -n and grep even when Webscope is available. hooks/sugerir_webscope.py
is a Claude Code PostToolUse hook that, after such a call, adds one line to the model's context with the equivalent
Webscope call (for example leer(archivo, lineas="120-180")). It never blocks and speaks at most 3 times per session.
Add it to the project's .claude/settings.json or .claude/settings.local.json:
{"hooks": {"PostToolUse": [{"matcher": "Read|Bash", "hooks": [{"type": "command",
"command": "python3 -I /path/to/webscope/hooks/sugerir_webscope.py"}]}]}}
uso_webscope.py measures whether it works: the share of reads and searches that go through Webscope.
Browser UI check
webscope_adaptadores/web/chequeo-ui.js is a standalone snippet for a browser automation tool (page.evaluate in Playwright, or a
JavaScript tool in a browser extension). It returns only the problems on the current page: horizontal overflow, empty
chips or badges, untranslated i18n keys, raw values (undefined, NaN…), overlapping text and unnamed buttons.
PLAYWRIGHT_DIR=<folder containing node_modules/playwright> node webscope_adaptadores/web/test_chequeo_ui.mjs
PLAYWRIGHT_DIR=<folder containing node_modules/playwright> PW_CHANNEL=chrome node webscope_adaptadores/web/test_form.mjs
PW_CHANNEL=chrome uses the installed Chrome when Playwright's own browser is missing.
Tests
python3 -I test_webscope.py
Out of scope
Webscope does not replace a language server or a browser automation server, does not run code written by the agent, and is not a test framework.
License
Metadata
Release files for webscope-mcp 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| webscope_mcp-0.2.0.tar.gz | 65.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| webscope_mcp-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 132.7 kB
Release files / webscope_mcp-0.2.0.tar.gz
| Download URL | webscope_mcp-0.2.0.tar.gz |
|---|---|
| Size | 65.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1cab674be7af61940900fc93a5d46196374d3c7104c7279bbf7fa0f8d02ded8a
|
|
BLAKE2b-256 checksum How to use checksums |
f561dfd10473d459f22c72bdb9419bc56e471955d30c196702afaf29e9f06e25
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logRelease files / webscope_mcp-0.2.0-py3-none-any.whl
| Download URL | webscope_mcp-0.2.0-py3-none-any.whl |
|---|---|
| Size | 67.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6355b3c71601bcd7c53ffa1dc2be4ca705b0d36e3707ddef84e62c3a6c141af7
|
|
BLAKE2b-256 checksum How to use checksums |
30754c07ece50e4e7d39677f9309ded218329cf252770825072c8b322ac41a23
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency log