Fitter MCP
Fitter — datos web para agentes de IA
Fitter convierte cualquier sitio web o API en JSON estructurado — de forma declarativa. Un solo archivo de configuración JSON/YAML describe dónde viven los datos (petición HTTP, navegador sin interfaz, archivo, valor estático) y qué extraer (rutas JSON, selectores CSS, XPath). Sin código, sin scripts de scraping frágiles.
🚀 Pruébalo en tu navegador — el motor real compilado a WebAssembly: ejemplos en vivo, un constructor visual de configuraciones, sin instalación.
Como las configuraciones son datos simples, los LLM pueden crearlas. El servidor MCP integrado permite que Claude Code, Claude Desktop o cualquier cliente MCP escriba y ejecute pipelines de scraping en tu máquina, bajo demanda:
"Obtén las 5 mejores historias de HackerNews con títulos y puntuaciones" → el modelo crea una configuración de fitter, la valida, la ejecuta localmente y obtiene JSON limpio.
Un motor, cinco formas de usarlo:
🤖 Fitter MCP | Servidor MCP que expone fitter a Claude Code, Claude Desktop y cualquier cliente MCP |
🧠 Fitter Agent | CLI impulsado por IA: lenguaje natural → configuración → resultado ejecutado |
🖥 Fitter CLI | ejecuta configuraciones localmente para pruebas/depuración/uso doméstico |
📦 Fitter Lib | integra el motor en tu propio programa Go |
⚙️ Fitter | modo de servicio de larga duración con programación y notificaciones |
¿Por qué fitter para agentes de IA?
Declarativo y auditable — el agente produce una configuración que puedes leer, guardar y re-ejecutar, no código desechable
Local primero — toda la obtención de datos ocurre en tu máquina; sin API de scraping de terceros, sin claves, sin facturación por petición
Con todo incluido — cliente HTTP, navegador sin interfaz (Playwright/Chromium/Docker), análisis de JSON/HTML/XML/XPath/PDF, paginación, referencias en caché, límites de tasa por host — en un solo binario estático
Reutilizable — lo que el agente creó hoy se convierte en el trabajo cron o la configuración de servicio de mañana
Cómo usar Fitter_MCP
Fitter MCP es un servidor Model Context Protocol (transporte stdio) que permite a cualquier cliente MCP — Claude Code, Claude Desktop, asistentes de IDE, agentes personalizados — ejecutar configuraciones de Fitter y obtener JSON estructurado.
Inicio rápido (Claude Desktop — un clic)
Descarga fitter-mcp-<os>-<arch>.mcpb desde la página de lanzamientos y ábrelo — Claude Desktop instala el servidor automáticamente.
Inicio rápido (Claude Code)
# 1. get the binary: download fitter_mcp_<version>-<os>-<arch> from the release page
# https://github.com/PxyUp/fitter/releases — or build it from source:
go build -o fitter_mcp ./cmd/mcp
# 2. register it once, available in every project
claude mcp add fitter -s user -- "$(pwd)/fitter_mcp"Luego solo pregunta:
Obtén las 5 mejores historias de HackerNews con títulos y puntuaciones usando fitter
El modelo llama a fitter_config_reference, crea una configuración, opcionalmente la verifica con fitter_validate_config y la ejecuta mediante fitter_run — toda la obtención de datos ocurre localmente en tu máquina. Para un pipeline listo, prueba examples/config_morning_briefing.json:
Ejecuta examples/config_morning_briefing.json con fitter y dame el resumen
Registrar en Claude Desktop
{
"mcpServers": {
"fitter": {
"command": "/path/to/fitter_mcp"
}
}
}Soporte de navegador (Playwright)
El paquete .mcpb y el binario nativo se distribuyen sin navegadores: los conectores HTTP, estático y de archivo funcionan de inmediato, pero las configuraciones de navegador (el conector playwright) necesitan los navegadores de Playwright. Algunas formas de obtenerlos:
En el primer uso (binario nativo /
.mcpb): establece"install": trueen el conector playwright — fitter descarga el controlador + navegador que coincida con su versión integrada deplaywright-goen el primer uso (una sola vez, en caché), por lo que no se necesita un paso de instalación separado.Con antelación (nativo, opcional): para evitar la descarga en el primer uso, instala los navegadores de antemano con la misma versión de
playwright-gocontra la que está compilado fitter (consultago.mod, actualmentev0.6100.0):go run github.com/mxschmitt/playwright-go/cmd/playwright@v0.6100.0 install # Linux: append --with-deps to also install the required OS librariesLa versión debe coincidir exactamente con
go.mod—playwright-gose niega a ejecutarse con un controlador que no coincida. Luego ejecuta configuraciones sin"install": true.Docker: usa la imagen
ghcr.io/pxyup/fitter-mcp:playwright, que incluye Chromium, Firefox y WebKit preinstalados (no se necesita"install": true).
Herramientas
Herramienta | Descripción |
| Ejecuta una configuración de Fitter pasada en línea (cadena JSON o YAML) y devuelve los datos extraídos como JSON. Acepta un valor |
| Igual que |
| Igual que |
| Obtiene una URL y devuelve un esquema compacto de la estructura + selectores/rutas candidatos (rutas gjson para JSON; selectores de elementos repetidos/filas de listas para HTML) para que el modelo cree una configuración al primer intento en lugar de adivinar selectores y obtener nulos. Detecta SPAs renderizadas en cliente y puede |
| Valida una configuración sin ejecutarla (estructura, |
| Devuelve una referencia condensada de todo el formato de configuración (conectores, analizadores, esquema de modelo/campo, marcadores de posición, notificadores, referencias, límites) con ejemplos funcionales, para que el modelo pueda crear configuraciones sin documentación externa |
La referencia también se expone como recurso MCP fitter://config-reference para clientes que soporten recursos.
El formato de configuración es exactamente el mismo que para Fitter_CLI: un objeto de nivel superior con item (obligatorio), limits y references. Los notificadores también funcionan (el resultado se envía adicionalmente a http/telegram/redis/file/console); trigger_config y http_server son solo para modo de servicio y se ignoran en las llamadas MCP.
Modo remoto / alojado (HTTP transmisible)
Por defecto, fitter_mcp usa stdio. Pasa --http para servir el transporte HTTP transmisible en su lugar — para un servidor de equipo compartido, un contenedor o cualquier despliegue remoto:
# serve MCP at http://<host>:8080/mcp (health probe at /healthz)
FITTER_MCP_AUTH_TOKEN=my-secret fitter_mcp --http :8080
# register the remote endpoint in Claude Code
claude mcp add --transport http fitter http://localhost:8080/mcp --header "Authorization: Bearer my-secret"--http <addr>(envFITTER_MCP_HTTP_ADDR) — dirección de escucha; modo stdio cuando está vacíoFITTER_MCP_AUTH_TOKEN— cuando se establece, cada petición/mcpdebe enviarAuthorization: Bearer <token>; sin él, el endpoint no está autenticado, así que enlaza a localhost o colócalo detrás de un proxy--stateless(envFITTER_MCP_STATELESS=true) — sin estado por sesión, por lo que las réplicas pueden estar detrás de un balanceador de carga sin sesiones fijas
El servidor se apaga correctamente con SIGINT/SIGTERM.
Docker
Una imagen multiarquitectura ligera (linux/amd64 + linux/arm64) se distribuye con cada lanzamiento:
# hosted HTTP mode
docker run --rm -p 8080:8080 \
-e FITTER_MCP_HTTP_ADDR=:8080 \
-e FITTER_MCP_AUTH_TOKEN=my-secret \
ghcr.io/pxyup/fitter-mcp:latest
# or stdio mode, spawned by the MCP client
claude mcp add fitter -s user -- docker run --rm -i ghcr.io/pxyup/fitter-mcp:latestLa imagen ligera contiene solo el binario de fitter y certificados CA: los conectores de servidor/estático/archivo funcionan, los conectores de navegador (chromium/docker/playwright) no.
Para configuraciones basadas en navegador, usa la variante playwright, que incluye Playwright con Chromium, Firefox y WebKit (coincidiendo con la versión de playwright-go contra la que está compilado fitter, por lo que no se necesita "install": true en las configuraciones):
docker run --rm -i ghcr.io/pxyup/fitter-mcp:playwright # stdio mode
# per-release tag: ghcr.io/pxyup/fitter-mcp:vX.Y.Z-playwrightEstá construida desde Dockerfile.mcp-playwright; compila con --build-arg PLAYWRIGHT_BROWSERS=chromium para una imagen más pequeña solo con Chromium.
Cuentas OAuth2 en Docker
Ambas imágenes incluyen fitter_cli, por lo que el inicio de sesión OAuth2 de una sola vez puede ejecutarse dentro del contenedor. Guarda el token en un volumen montado en /tokens (pre-creado y escribible en la imagen) y compártelo con el servidor MCP:
# one-time login, device flow: no ports needed — open the printed url on any device
docker run --rm -it -v fitter-tokens:/tokens --entrypoint fitter_cli \
ghcr.io/pxyup/fitter-mcp:latest \
auth --provider github --client-id <ID> --client-secret <SECRET> --token-file /tokens/github.json
# or browser flow (device flow not enabled for the app): publish the callback port and
# bind on 0.0.0.0 so the published port reaches the listener; the browser still visits 127.0.0.1
docker run --rm -it -p 8988:8988 -e FITTER_AUTH_LISTEN=0.0.0.0 \
-v fitter-tokens:/tokens --entrypoint fitter_cli ghcr.io/pxyup/fitter-mcp:latest \
auth --provider github --client-id <ID> --client-secret <SECRET> --token-file /tokens/github.json
# then run the MCP server with the same volume; configs reference "token_file": "/tokens/github.json"
# stdio mode (spawned by the MCP client, no port):
docker run --rm -i -v fitter-tokens:/tokens ghcr.io/pxyup/fitter-mcp:latest
# hosted HTTP mode (MCP endpoint on 8080, like the run examples above):
docker run --rm -p 8080:8080 -v fitter-tokens:/tokens \
-e FITTER_MCP_HTTP_ADDR=:8080 \
-e FITTER_MCP_AUTH_TOKEN=my-secret \
ghcr.io/pxyup/fitter-mcp:latestNota: 8988 es solo para el inicio de sesión único del flujo de navegador; el servidor MCP en sí no necesita puerto en modo stdio y solo 8080 en modo HTTP alojado.
Sesiones de navegador iniciadas en Docker
Las sesiones de navegador necesitan la imagen playwright (la ligera no tiene navegadores). El inicio de sesión con ventana única necesita una pantalla, así que ejecútalo en el host, luego monta el directorio de sesiones en el contenedor (la imagen pre-crea un /sessions escribible):
# on the host: log in once, save the session
fitter_cli browser-login --url https://example.com/login --storage-state ~/.fitter/sessions/example.json
# run the MCP server with the sessions dir mounted; configs reference "storage_state_file": "/sessions/example.json"
docker run --rm -i -v ~/.fitter/sessions:/sessions ghcr.io/pxyup/fitter-mcp:playwrightUsa un montaje de enlace (no un volumen con nombre): el contenedor escribe cookies actualizadas después de cada ejecución, por lo que la copia del host se mantiene actualizada y puede ampliarse con browser-login en cualquier momento.
El volumen debe permanecer escribible para el servidor: los tokens de refresco rotados se escriben de nuevo en cada refresco.
Variables de entorno
FITTER_PLUGINS - string[""] - ruta para la carpeta de plugins, igual que la bandera
--pluginsde Fitter/Fitter_CLIFITTER_MCP_HTTP_ADDR - string[""] - dirección de escucha para modo remoto, igual que
--httpFITTER_MCP_AUTH_TOKEN - string[""] - token de portador que protege el endpoint HTTP
FITTER_MCP_STATELESS - bool[false] - transporte HTTP sin estado, igual que
--stateless
Recetas
Configuraciones completas y probadas que muestran los patrones principales. Todas se ejecutan sin cambios mediante Fitter_MCP (fitter_run_file), Fitter_CLI o la librería — más en examples/.
Extraer una página que no tiene API y enriquecerla desde una que sí la tiene
GitHub trending no tiene API oficial — extrae el HTML para obtener los slugs de los repos (html_attribute lee el href), y luego distribuye cada uno hacia la API REST de GitHub con {PL}:
examples/config_github_trending.json
{
"item": {
"connector_config": {
"response_type": "HTML",
"url": "https://github.com/trending",
"server_config": { "method": "GET", "headers": { "User-Agent": "Mozilla/5.0 (fitter demo)" } }
},
"model": {
"array_config": {
"root_path": "article.Box-row h2 a",
"length_limit": 5,
"item_config": {
"field": {
"type": "string",
"html_attribute": "href",
"generated": { "model": {
"type": "object",
"connector_config": {
"response_type": "json",
"url": "https://api.github.com/repos{PL}",
"server_config": { "method": "GET", "headers": { "User-Agent": "fitter-demo" } },
"null_on_error": true
},
"model": { "object_config": { "fields": {
"repo": { "base_field": { "type": "string", "path": "full_name" } },
"stars": { "base_field": { "type": "int", "path": "stargazers_count" } },
"language": { "base_field": { "type": "string", "path": "language" } }
} } }
} }
}
}
}
}
},
"limits": { "host_request_limiter": { "api.github.com": 2 } }
}[{"repo": "block/buzz", "stars": 6214, "language": "Rust"}, {"repo": "koala73/worldmonitor", "stars": 71179, "language": "TypeScript"}]Unir por un campo JSON con una expresión
Cuando los elementos del array son objetos, la clave de unión vive dentro de ellos — extráela con {{{FromExp=...}}} (expr-lang sobre fRes, el elemento actual). Búsqueda de libros → detalles del autor, la consulta de búsqueda se proporciona mediante input:
examples/config_book_authors.json
"url": "https://openlibrary.org/authors/{{{FromExp=fromJSON(fRes).author_key[0]}}}.json"./fitter_cli --path=examples/config_book_authors.json --input=dune[{"title": "Dune", "year": 1965, "author": {"name": "Frank Herbert", "born": "8 October 1920", "died": "11 February 1986"}}]Escribir resultados en un archivo local
El campo generado file_storage convierte campos en escrituras — las 5 criptomonedas principales se añaden a un CSV, una fila por elemento. Los placeholders simples {{{json.path}}} leen el elemento actual; {HUMAN_INDEX} marca el rango basado en 1 (los elementos se procesan en paralelo, por lo que las adiciones llegan en orden de finalización — ordena por la columna de rango):
examples/config_crypto_csv.json
"file_storage": {
"content": "{HUMAN_INDEX},{{{name}}},{{{current_price}}},{{{price_change_percentage_24h}}}\n",
"file_name": "coins.csv",
"path": "/tmp/fitter-report",
"append": true
}$ sort -n /tmp/fitter-report/coins.csv
1,Bitcoin,64778,-2.3
2,Ethereum,1881.01,-3.4
3,Tether,0.999265,0Extraer texto de un PDF
response_type: "pdf" convierte cualquier PDF obtenido en un documento JSON — {"text": "...", "pages": ["..."], "total_pages": N} — de modo que las rutas JSON normales (text, pages.0) y las expresiones funcionan sobre él. El whitepaper de Bitcoin, el número de páginas más una introducción recortada:
{
"item": {
"connector_config": {
"response_type": "pdf",
"url": "https://bitcoin.org/bitcoin.pdf",
"server_config": { "method": "GET" }
},
"model": {
"object_config": {
"fields": {
"total_pages": { "base_field": { "type": "int", "path": "total_pages" } },
"intro": {
"base_field": {
"type": "string",
"path": "pages.0",
"generated": {
"calculated": {
"type": "string",
"expression": "trim(fRes[:100]) + \"...\""
}
}
}
}
}
}
}
}
}{"intro": "Bitcoin: A Peer-to-Peer Electronic Cash SystemSatoshi Nakamotosatoshin@gmx.comwww.bitcoin.orgAbstrac...", "total_pages": 9}Forma de recopilar información
Server - analizar la respuesta de algunas API o solicitudes http (uso de http.Client)
Browser - emular un navegador real usando chromium + docker + playwright/cypress y obtener información del DOM
Static - analizar una cadena estática como datos
Formato que se puede analizar
JSON - analizar JSON para obtener información específica
XML - analizar el árbol XML para obtener información específica
HTML - analizar el árbol DOM para obtener información específica
XPath - analizar el árbol DOM para obtener información específica pero mediante xpath
PDF - extraer texto de documentos PDF; el contenido se expone como JSON
{"text": "...", "pages": ["..."], "total_pages": N}de modo que las rutas JSON normales comotextopages.0funcionan
Usar como librería
go get github.com/PxyUp/fitterpackage main
import (
"fmt"
"github.com/PxyUp/fitter/lib"
"github.com/PxyUp/fitter/pkg/config"
"log"
"net/http"
)
func main() {
res, err := lib.Parse(&config.Item{
ConnectorConfig: &config.ConnectorConfig{
ResponseType: config.Json,
Url: "https://random-data-api.com/api/appliance/random_appliance",
ServerConfig: &config.ServerConnectorConfig{
Method: http.MethodGet,
},
},
Model: &config.Model{
ObjectConfig: &config.ObjectConfig{
Fields: map[string]*config.Field{
"my_id": {
BaseField: &config.BaseField{
Type: config.Int,
Path: "id",
},
},
"generated_id": {
BaseField: &config.BaseField{
Generated: &config.GeneratedFieldConfig{
UUID: &config.UUIDGeneratedFieldConfig{},
},
},
},
"generated_array": {
ArrayConfig: &config.ArrayConfig{
RootPath: "@this|@keys",
ItemConfig: &config.ObjectConfig{
Field: &config.BaseField{
Type: config.String,
},
},
},
},
},
},
},
}, nil, nil, nil, nil)
if err != nil {
log.Fatal(err)
}
fmt.Println(res.ToJson())
}
Salida:
{
"generated_array": ["id","uid","brand","equipment"],
"my_id": 6000,
"generated_id": "26b08b73-2f2e-444d-bcf2-dac77ac3130e"
}Usa lib.ParseCtx(ctx, ...) para pasar un context.Context: cancelarlo aborta las descargas en curso (solicitudes HTTP, navegadores headless, contenedores docker) y aplica plazos de extremo a extremo. lib.Parse es equivalente a lib.ParseCtx(context.Background(), ...).
Cómo usar Fitter
Descarga la última versión desde la página de lanzamientos
o localmente:
go run cmd/fitter/main.go --path=./examples/config_api.jsonArgumentos
--path - string[""] - ruta para la configuración de Fitter
--url - string[""] - url para la configuración de Fitter
--verbose - bool[false] - habilitar registro
--plugins - string[""] - ruta para plugins de Fitter
--log-level - enum["info", "error", "debug", "fatal"] - establecer nivel de registro (solo si verbose está activado)
Cómo usar Fitter_CLI
Descarga la última versión desde la página de lanzamientos
o localmente:
go run cmd/cli/main.go --path=./examples/cli/config_cli.jsonArgumentos
--path - string[""] - ruta para la configuración de Fitter_CLI
--url - string[""] - url para la configuración de Fitter_CLI
--copy - bool[false] - copiar información al portapapeles
--pretty - bool[true] - hacer legible el resultado (también afecta a la copia)
--verbose - bool[false] - habilitar registro
--omit-error-pretty - bool[false] - Proporcionar valor puro si pretty no es válido
--plugins - string[""] - ruta para plugins de Fitter
--log-level - enum["info", "error", "debug", "fatal"] - establecer nivel de registro (solo si verbose está activado)
--input - string[""] - especificar valor de entrada para formato. Ejemplos:
--input=\""124"\"--input=124--input='{"test": 5}'
./fitter_cli_${VERSION} --path=./examples/cli/config_cli.json --copy=truefitter_cli auth — conectar una cuenta OAuth2
Inicio de sesión interactivo de una sola vez que almacena un token (de actualización) para el configurador de conector oauth2:
# device flow (default when the provider supports it): no callback, works headless
./fitter_cli_${VERSION} auth --provider github --client-id <ID> --client-secret <SECRET> --token-file ~/.fitter/tokens/github.json
# custom provider without preset
./fitter_cli_${VERSION} auth --auth-url https://.../authorize --token-url https://.../token --client-id <ID> --token-file ./token.jsonArgumentos:
--provider - preajuste con endpoints conocidos:
github|google|microsoft|gitlab|spotify--client-id / --client-secret - credenciales de la aplicación OAuth2 (algunos flujos de dispositivo funcionan sin secreto)
--token-file - dónde almacenar el token recibido (permisos 0600); referencia la misma ruta en
oauth2.token_file--flow -
auto(dispositivo si está disponible, si no navegador),device(visitar una url + introducir un código) obrowser(callback de localhost con PKCE, puerto predeterminado 8988 — registrahttp://127.0.0.1:8988/callbackcomo la url de callback de la aplicación)--scopes - ámbitos separados por comas
--auth-url/--token-url/--device-auth-url/--auth-style - anulaciones de endpoints para proveedores sin preajuste
--port - int[8988] - puerto de callback del flujo de navegador (env
FITTER_AUTH_PORT); con el predeterminado, la url de callback a registrar en el proveedor eshttp://127.0.0.1:8988/callback--listen - dirección de enlace del flujo de navegador, predeterminada
127.0.0.1; establece0.0.0.0dentro de un contenedor para que el puerto publicado llegue al oyente (envFITTER_AUTH_LISTEN)--redirect-url - url de callback registrada en el proveedor cuando difiere de la dirección de escucha, p. ej. mapeo de puertos de docker (env
FITTER_AUTH_REDIRECT_URL)--no-browser - solo imprimir la url de autorización
Ejecutar dentro de Docker: consulta Cuentas OAuth2 en Docker.
Después del inicio de sesión, el comando imprime el bloque de configuración oauth2 listo para usar. El conector actualiza automáticamente el token de acceso y escribe los tokens de actualización rotados de vuelta al archivo de token, por lo que el inicio de sesión solo es necesario una vez.
fitter_cli browser-login — reutilizar una sesión de inicio de sesión real
Para sitios sin API/OAuth: inicia sesión manualmente una vez en una ventana de navegador real (con interfaz) — cualquier esquema de autenticación funciona, incluidas contraseñas, 2FA, SSO y captchas — y guarda la sesión para el scraping headless mediante storage_state_file:
./fitter_cli_${VERSION} browser-login --url https://example.com/login --storage-state ~/.fitter/sessions/example.json
# a browser window opens; log in, then press Enter in the terminal to save the sessionArgumentos:
--url - página de inicio de sesión a abrir (obligatorio)
--storage-state - dónde guardar la sesión (cookies + localStorage, permisos 0600); referencia la misma ruta en
playwright.storage_state_file(obligatorio)--browser - enum["Chromium", "FireFox", "WebKit"] predeterminado "Chromium"; usa el mismo valor que la configuración de scraping — los sitios pueden vincular sesiones a la huella del navegador
--install - bool[false] - instalar primero los navegadores de playwright
--indexeddb - bool[false] - incluir IndexedDB en la instantánea (Firebase Auth y similares)
Re-ejecutar el comando carga primero el estado existente, por lo que puedes extender/refrescar una sesión sin iniciar sesión desde cero. El conector de scraping también escribe cookies actualizadas de vuelta después de cada ejecución, manteniendo la sesión viva mientras se use regularmente. Necesita una pantalla: dentro de Docker ejecuta este comando en el host y monta el archivo — consulta sesiones de navegador en Docker.
Ejemplos:
Versión Server HackerNews + Quotes + Guardian News - usando análisis de API + HTML + XPath
Versión Chromium Guardian News + Quotes - usando análisis de HTML + emulación de navegador
Versión Docker Versión Docker: Guardian News + Quotes - usando análisis de HTML + navegador desde imagen Docker
Versión Playwright Versión Playwright: Guardian News + Quotes - usando análisis de HTML + navegador desde el framework Playwright
Versión Playwright Versión Playwright: England Cities + Weather - usando análisis de HTML + XPath + navegador desde el framework Playwright
Versión JSON Generar paginación - usando conector estático para generar array de paginación
Versión Server Obtener hora actual - obtener hora de la url y formatearla
Cómo usar Fitter_Agent
Fitter Agent es una CLI impulsada por IA que usa Claude para convertir solicitudes en lenguaje natural en configuraciones de Fitter y ejecutarlas automáticamente.
Descarga la última versión desde la página de lanzamientos
o localmente:
export ANTHROPIC_API_KEY=<your-anthropic-api-key>
go run cmd/agent/main.goArgumentos
--api-key - string[""] - clave de API de Anthropic. Prefiere la variable de entorno
ANTHROPIC_API_KEYpara que la clave no termine en el historial de tu shell--model - string["claude-opus-4-8"] - modelo de Claude a usar
--effort - enum["low", "medium", "high", "xhigh", "max"] - esfuerzo de razonamiento, predeterminado "high". Bájalo para configuraciones más rápidas/baratas, súbelo para extracciones más difíciles
--verbose - bool[false] - habilitar registro
--log-level - enum["info", "error", "debug", "fatal"] - establecer nivel de registro
--plugins - string[""] - ruta para plugins de Fitter
--chromium-limit - uint[0] - limitar instancias concurrentes de Chromium
--docker-limit - uint[0] - limitar contenedores Docker concurrentes
--playwright-limit - uint[0] - limitar instancias concurrentes de Playwright
Cómo funciona
┌─────────────────────────────────────────────────────────────────┐
│ 1. User enters natural language request │
│ "Get top 5 HackerNews stories with titles and scores" │
│ ↓ │
│ 2. Claude returns a config in a schema-constrained response │
│ ↓ │
│ 3. Agent validates it; on failure the error is handed back │
│ to Claude to repair (up to 3 attempts) │
│ ↓ │
│ 4. Agent displays config and asks for confirmation │
│ ↓ │
│ 5. On confirmation, executes via lib.Parse() │
│ ↓ │
│ 6. Returns structured JSON result │
└─────────────────────────────────────────────────────────────────┘Refinar una configuración
El agente mantiene la conversación, así que después de generar una configuración puedes simplemente decir qué cambiar en lugar de repetir toda la solicitud:
> Get top 3 HackerNews stories with titles and scores
refine> Only return 5 items and also include the article URLUsa new para olvidar la configuración actual y comenzar una sesión nueva.
Comandos interactivos del REPL
help - mostrar mensaje de ayuda
new/reset - olvidar la configuración actual y comenzar de nuevo
clear - limpiar la pantalla
exit/quit/q - salir del agente
Sesión de ejemplo
$ export ANTHROPIC_API_KEY=sk-ant-...
$ ./fitter_agent
╔══════════════════════════════════════════════════════════════╗
║ Fitter Agent - AI-Powered Data Extraction ║
╚══════════════════════════════════════════════════════════════╝
Describe what you want to extract. Follow-up messages refine the
previous config. Type 'help' for commands.
> Get top 3 HackerNews stories with titles and scores
┌─ Generated Fitter Config ───────────────────────────────────────
{
"item": {
"connector_config": {
"response_type": "json",
"url": "https://hacker-news.firebaseio.com/v0/topstories.json",
"server_config": { "method": "GET" }
},
"model": {
"array_config": {
"root_path": "@this",
"length_limit": 3,
"item_config": {
"fields": {
"id": { "base_field": { "type": "int" } },
"story": {
"base_field": {
"type": "int",
"generated": {
"model": {
"type": "object",
"connector_config": {
"response_type": "json",
"url": "https://hacker-news.firebaseio.com/v0/item/{PL}.json",
"server_config": { "method": "GET" }
},
"model": {
"object_config": {
"fields": {
"title": { "base_field": { "type": "string", "path": "title" } },
"score": { "base_field": { "type": "int", "path": "score" } }
}
}
}
}
}
}
}
}
}
}
}
}
}
└──────────────────────────────────────────────────────────────────
Execute this config? [y/n]: y
┌─ Result ────────────────────────────────────────────────────────
[
{
"id": 46740029,
"story": { "title": "Show HN: Open-source project", "score": 161 }
},
{
"id": 46737630,
"story": { "title": "Interesting article", "score": 237 }
},
{
"id": 46735644,
"story": { "title": "New technology release", "score": 192 }
}
]
└──────────────────────────────────────────────────────────────────
> exit
Goodbye!Solicitudes de ejemplo
Solicitud | Qué hace |
| Obtiene el precio actual de BTC |
| Scraping HTML con selectores CSS |
| Llamadas API anidadas |
| Extracción simple de API |
| Scraping web |
Capacidades compatibles
El agente puede generar configuraciones para:
APIs JSON - APIs REST con métodos GET/POST
Scraping HTML - extracción basada en selectores CSS
Scraping XPath - extracción basada en XPath
Llamadas API anidadas - obtener detalles para cada elemento de una lista
Emulación de navegador - Playwright para páginas renderizadas con JS
Campos formateados - plantillas de URL con placeholders
Limitación de arrays - limitar resultados a N elementos
Configuración
Conector
Es la forma en que obtienes los datos
type ConnectorConfig struct {
ResponseType ParserType `json:"response_type" yaml:"response_type"`
Url string `json:"url" yaml:"url"`
Attempts uint32 `json:"attempts" yaml:"attempts"`
NullOnError bool `yaml:"null_on_error" json:"null_on_error"`
StaticConfig *StaticConnectorConfig `json:"static_config" yaml:"static_config"`
IntSequenceConfig *IntSequenceConnectorConfig `json:"int_sequence_config" yaml:"int_sequence_config"`
ServerConfig *ServerConnectorConfig `json:"server_config" yaml:"server_config"`
BrowserConfig *BrowserConnectorConfig `yaml:"browser_config" json:"browser_config"`
PluginConnectorConfig *PluginConnectorConfig `json:"plugin_connector_config" yaml:"plugin_connector_config"`
ReferenceConfig *ReferenceConnectorConfig `yaml:"reference_config" json:"reference_config"`
FileConfig *FileConnectorConfig `json:"file_config" yaml:"file_config"`
}NullOnError[false] - si se establece en true, todos los errores se ignoran
ResponseType - enum["HTML", "json", "xpath", "XML", "pdf"] - en qué formato llegan los datos del conector
Attempts - cuántos intentos se usan para obtener datos mediante el conector
Url - define qué dirección solicitar. Importante: puede incluir inyección del valor del padre como cadena
https://api.open-meteo.com/v1/forecast?latitude={{{latitude}}}&longitude={{{longitude}}}&hourly=temperature_2m&forecast_days=1
La configuración puede ser una de:
Ejemplo:
{
"response_type": "xpath",
"attempts": 3,
"url": "https://openweathermap.org/find?q={PL}",
"browser_config": {
"playwright": {
"timeout": 30,
"wait": 30,
"install": false,
"browser": "Chromium"
}
}
}PluginConnectorConfig
El conector se puede definir mediante el sistema de plugins. Para usarlo, debes aplicar las siguientes banderas a Fitter/Cli (ubicación de los plugins):
... --plugins=./examples/plugin--plugins - busca todos los archivos con extensión ".so" en la carpeta proporcionada (subdirectorios excluidos)
type PluginConnectorConfig struct {
Name string `json:"name" yaml:"name"`
Config json.RawMessage `json:"config" yaml:"config"`
}{
"name": "connector",
"config": {
"name": "Elon"
}
}Name - nombre del plugin
Config - configuración json del plugin
Cómo construir un plugin
Construir el plugin
go build -buildmode=plugin -gcflags="all=-N -l" -o examples/plugin/connector.so examples/plugin/connector/connector.goAsegúrate de exportar la variable Plugin que implementa la interfaz pl.ConnectorPlugin
Ejemplo para CLI:
https://github.com/PxyUp/fitter/blob/master/examples/cli/config_plugin.json#L5
Ejemplo de plugin:
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/PxyUp/fitter/pkg/config"
"github.com/PxyUp/fitter/pkg/logger"
"github.com/PxyUp/fitter/pkg/builder"
pl "github.com/PxyUp/fitter/pkg/plugins/plugin"
)
var (
_ pl.ConnectorPlugin = &plugin{}
Plugin plugin
)
type plugin struct {
log logger.Logger
Name string `json:"name" yaml:"name"`
}
func (pl *plugin) Get(ctx context.Context, parsedValue builder.Interfacable, index *uint32, input builder.Interfacable) ([]byte, error) {
return []byte(fmt.Sprintf(`{"name": "%s"}`, pl.Name)), nil
}
func (pl *plugin) SetConfig(cfg *config.PluginConnectorConfig, logger logger.Logger) {
pl.log = logger
if cfg.Config != nil {
err := json.Unmarshal(cfg.Config, pl)
if err != nil {
pl.log.Errorw("cant unmarshal plugin configuration", "error", err.Error())
return
}
}
}ReferenceConnectorConfig
Conector que permite obtener datos precargados de references
type ReferenceConnectorConfig struct {
Name string `yaml:"name" json:"name"`
}Ejemplo
https://github.com/PxyUp/fitter/blob/master/examples/cli/config_ref.json#L66
Name - nombre de la referencia del mapa references
IntSequenceConnectorConfig
Versión mejorada del conector estático que genera una secuencia de enteros como resultado
type IntSequenceConnectorConfig struct {
Start int `json:"start" yaml:"start"`
End int `json:"end" yaml:"end"`
Step int `json:"step" yaml:"step"`
}Start[0] - punto de inicio para la generación (incluido)
End[0] - punto final para la generación (excluido del resultado final, como range en cualquier lenguaje)
Step[1] - intervalo para la secuencia
Ejemplo
{
"start": 0,
"end": 2
// Generate [0, 1]
}FileConnectorConfig
Tipo de conector que obtiene datos del archivo proporcionado
type FileConnectorConfig struct {
Path string `yaml:"path" json:"path"`
UseFormatting bool `yaml:"use_formatting" json:"use_formatting"`
}Path - ruta del archivo. Soporta formato
UseFormatting[false] - usar formato en el contenido del archivo o no
StaticConnectorConfig
Tipo de conector que obtiene datos de la cadena proporcionada
type StaticConnectorConfig struct {
Value string `json:"value" yaml:"value"`
Raw json.RawMessage `json:"raw" yaml:"raw"`
}Value - cadena estática como datos, puede ser html, json
Ejemplo:
https://github.com/PxyUp/fitter/blob/master/examples/cli/config_static_connector.json#L5
{
"value": "[1,2,3,4,5]"
}ServerConnectorConfig
Tipo de conector que obtiene datos usando http.Client de golang (solicitud del lado del servidor, como curl)
type ServerConnectorConfig struct {
Method string `json:"method" yaml:"method"`
Headers map[string]string `yaml:"headers" json:"headers"`
Timeout uint32 `yaml:"timeout" json:"timeout"`
JsonRawBody json.RawMessage `json:"json_raw_body" yaml:"json_raw_body"`
Body string `yaml:"body" json:"body"`
ErrorOnStatus bool `json:"error_on_status" yaml:"error_on_status"`
Proxy *ProxyConfig `yaml:"proxy" json:"proxy"`
OAuth2 *OAuth2Config `yaml:"oauth2" json:"oauth2"`
}Method - soporta todos los métodos http: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD
Headers - cabeceras predefinidas para usar durante la solicitud se puede inyectar en clave/valor
Timeout[sec] - tiempo de espera predeterminado de 60 segundos o el proporcionado
Body - cuerpo de la solicitud, valor analizado se puede inyectar
JsonRawBody - cuerpo de la solicitud en formato json; valor se puede inyectar
ErrorOnStatus - opcional, por defecto
false. Cuando estrue, un estado de respuesta HTTP>= 400se trata como un error de obtención (fluyendo a través deattempts/null_on_error) en lugar de analizar el cuerpo del error, de modo que puedes distinguir una obtención fallida de un resultado genuinamente vacío. Dejarlo enfalsemantiene el comportamiento original de analizar cualquier cuerpo que haya llegado.Proxy - configura el proxy para la solicitud config
OAuth2 - obtener/renovar automáticamente un token de acceso y enviarlo como cabecera
Authorizationconfig
Las solicitudes envían un
User-Agentidentificable (fitter (+https://github.com/PxyUp/fitter)) por defecto; establece tu propioUser-AgentenHeaderspara sobrescribirlo.
Ejemplo:
{
"method": "GET",
"proxy": {
"server": "http://localhost:8080",
"username": "pyx"
}
}Configuración de OAuth2
Obtiene automáticamente un token de acceso antes de la solicitud y lo inyecta como cabecera Authorization (sobrescribiendo el establecido mediante headers). Los tokens se almacenan en caché en memoria y se renuevan antes de que expiren; en una respuesta 401, el token en caché se descarta y la solicitud se reintenta una vez con uno nuevo.
type OAuth2Config struct {
TokenUrl string `json:"token_url" yaml:"token_url"`
GrantType OAuth2GrantType `json:"grant_type" yaml:"grant_type"`
ClientId string `json:"client_id" yaml:"client_id"`
ClientSecret string `json:"client_secret" yaml:"client_secret"`
Scopes []string `json:"scopes" yaml:"scopes"`
RefreshToken string `json:"refresh_token" yaml:"refresh_token"`
EndpointParams map[string]string `json:"endpoint_params" yaml:"endpoint_params"`
AuthStyle string `json:"auth_style" yaml:"auth_style"`
TokenFile string `json:"token_file" yaml:"token_file"`
}TokenUrl - URL del endpoint del token. También soporta formato
GrantType - enum["client_credentials", "refresh_token"], por defecto es "client_credentials". Usa "refresh_token" para APIs donde el usuario dio su consentimiento una vez (Google, Microsoft, ...) y tienes un token de refresco de larga duración
ClientId/ClientSecret - credenciales del cliente. También soporta formato, p. ej.
{{{FromEnv=CLIENT_SECRET}}}Scopes - ámbitos solicitados
RefreshToken - requerido para el grant "refresh_token". También soporta formato
EndpointParams - parámetros extra del endpoint del token (p. ej.
audiencepara Auth0), solo para el grant "client_credentials"AuthStyle - enum["", "header", "params"] - cómo se pasan las credenciales del cliente al endpoint del token: cabecera de autenticación básica o cuerpo de la solicitud; vacío significa detección automática
TokenFile - ruta opcional (soporta
~/) para persistir tokens entre ejecuciones; el token almacenado se prefiere sobre RefreshToken y los tokens de refresco rotados se escriben de vuelta — requerido para proveedores con tokens de refresco de un solo uso (GitHub Apps y similares). Créalo con fitter_cli auth
Ejemplo:
{
"method": "GET",
"oauth2": {
"token_url": "https://oauth2.googleapis.com/token",
"grant_type": "refresh_token",
"client_id": "{{{FromEnv=GOOGLE_CLIENT_ID}}}",
"client_secret": "{{{FromEnv=GOOGLE_CLIENT_SECRET}}}",
"refresh_token": "{{{FromEnv=GOOGLE_REFRESH_TOKEN}}}"
}
}Configuración de proxy
type ProxyConfig struct {
// Proxy to be used for all requests. HTTP and SOCKS proxies are supported, for example
// `http://myproxy.com:3128` or `socks5://myproxy.com:3128`. Short form `myproxy.com:3128`
// is considered an HTTP proxy.
Server string `json:"server" yaml:"server"`
// Optional username to use if HTTP proxy requires authentication.
Username string `json:"username" yaml:"username"`
// Optional password to use if HTTP proxy requires authentication.
Password string `json:"password" yaml:"password"`
}Server - dirección con esquema del servidor proxy. También soporta formato
Username - nombre de usuario para el proxy (puede estar vacío). También soporta formato
Password - contraseña para el proxy (puede estar vacía). También soporta formato
{
"server": "http://localhost:8080",
"username": "pyx"
}Variables de entorno
FITTER_HTTP_WORKER - int[1000] - trabajadores HTTP concurrentes por defecto
BrowserConnectorConfig
Tipo de conector que emula la obtención de datos mediante un navegador
type BrowserConnectorConfig struct {
Chromium *ChromiumConfig `json:"chromium" yaml:"chromium"`
Docker *DockerConfig `json:"docker" yaml:"docker"`
Playwright *PlaywrightConfig `json:"playwright" yaml:"playwright"`
}La configuración puede ser una de:
Chromium - usa Chromium instalado localmente para obtener datos
Docker - usa docker como servicio para levantar un contenedor para obtener datos
Playwright - usa el framework playwright para obtener datos
Ejemplo:
{
"docker": {
"wait": 10000,
"image": "docker.io/zenika/alpine-chrome:with-node",
"entry_point": "chromium-browser",
"purge": true
}
}Chromium
Usa Chromium instalado localmente para obtener los datos
type ChromiumConfig struct {
Path string `yaml:"path" json:"path"`
Timeout uint32 `yaml:"timeout" json:"timeout"`
Wait uint32 `yaml:"wait" json:"wait"`
Flags []string `yaml:"flags" json:"flags"`
}Path - ruta al binario de Chromium
Timeout[sec] - tiempo de espera para la ejecución de Chromium
Wait[msec] - tiempo de espera para la carga de la página
Flags - banderas para Chromium, por defecto: "--headless", "--proxy-auto-detect", "--temp-profile", "--incognito", "--disable-logging", "--disable-extensions", "--no-sandbox"
Ejemplo:
{
"path": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
"wait": 10000
}Docker
Usa Docker para levantar un contenedor para obtener datos
type DockerConfig struct {
Image string `yaml:"image" json:"image"`
EntryPoint string `json:"entry_point" yaml:"entry_point"`
Timeout uint32 `yaml:"timeout" json:"timeout"`
Wait uint32 `yaml:"wait" json:"wait"`
Flags []string `yaml:"flags" json:"flags"`
Purge bool `json:"purge" yaml:"purge"`
NoPull bool `yaml:"no_pull" json:"no_pull"`
PullTimeout uint32 `yaml:"pull_timeout" json:"pull_timeout"`
}Imagen Docker por defecto: docker.io/zenika/alpine-chrome
Image - imagen para el registro docker (proporcionar con el host del registro)
EntryPoint - comando que se ejecutará dentro del contenedor
Timeout[sec] - tiempo de espera para ejecutar el contenedor (sin incluir la descarga de la imagen)
Wait[msec] - tiempo de espera para la carga de la página (funciona solo con contenedores basados en Chromium)
Flags - argumentos de comando para ejecutar contenedores, por defecto para los basados en Chromium: "--no-sandbox","--headless", "--proxy-auto-detect", "--temp-profile", "--incognito", "--disable-logging", "--disable-gpu"
Purge - si debemos eliminar el contenedor después de terminar el trabajo (como docker rm)
NoPull - evitar la descarga de la imagen
PullTimeout - define el tiempo de espera para la descarga del contenedor
Variables de entorno
DOCKER_HOST - string - (EnvOverrideHost) para establecer la URL del servidor docker.
DOCKER_API_VERSION - string - (EnvOverrideAPIVersion) para establecer la versión de la API a usar, dejar vacío para la última.
DOCKER_CERT_PATH - string - (EnvOverrideCertPath) para especificar el directorio desde el que cargar los certificados TLS (ca.pem, cert.pem, key.pem).
DOCKER_TLS_VERIFY - bool - (EnvTLSVerify) para habilitar o deshabilitar la verificación TLS (deshabilitada por defecto)
Ejemplo:
{
"wait": 10000,
"image": "docker.io/zenika/alpine-chrome:with-node",
"entry_point": "chromium-browser",
"purge": true
}Playwright
Ejecutar navegadores mediante el framework playwright
type PlaywrightConfig struct {
Browser PlaywrightBrowser `json:"browser" yaml:"browser"`
Install bool `yaml:"install" json:"install"`
Timeout uint32 `yaml:"timeout" json:"timeout"`
Wait uint32 `yaml:"wait" json:"wait"`
TypeOfWait *playwright.WaitUntilState `json:"type_of_wait" yaml:"type_of_wait"`
PreRunScript string `json:"pre_run_script" yaml:"pre_run_script"`
PostRunScript string `json:"post_run_script" yaml:"post_run_script"`
Stealth bool `json:"stealth" yaml:"stealth"`
StorageStateFile string `json:"storage_state_file" yaml:"storage_state_file"`
IndexedDB bool `json:"indexed_db" yaml:"indexed_db"`
Proxy *ProxyConfig `yaml:"proxy" json:"proxy"`
}Browser - enum["Chromium", "FireFox", "WebKit"] - qué navegador usar
Install - si debemos instalar el navegador (descarga el controlador + el navegador que coincide con la versión integrada de
playwright-goen el primer uso; no es necesario con la imagenghcr.io/pxyup/fitter-mcp:playwright, que los incluye preinstalados)Timeout[sec] - tiempo de espera para ejecutar playwright
Wait[sec] - tiempo de espera para la carga de la página
TypeOfWait - enum["load", "domcontentloaded", "networkidle", "commit"] qué estado de la página esperamos, por defecto es "load"
PreRunScript[""] - script que se inyectará mediante AddInitScript y se ejecutará antes de que se ejecute cualquier script de la página (en la creación del documento, antes de que se complete la navegación). Útil para parchear el entorno (anulaciones de navigator, stubs de API). No puede acceder al DOM cargado. También soporta placeholder {PL}
PostRunScript[""] - script que se ejecutará después de la carga de la página, antes de leer el contenido de la página. Útil para interacción con el DOM (clics, desplazamiento). También soporta placeholder {PL}
Stealth[false] - añadir script para intentar superar defensas de bots
StorageStateFile[""] - ruta (soporta
~/) a un json de estado de almacenamiento de playwright (cookies + localStorage): se carga en el contexto del navegador antes de la navegación, y se escribe de vuelta después de cada ejecución para que las sesiones renovadas sigan vivas. Permite que las ejecuciones headless reutilicen un inicio de sesión real — crea el archivo una vez con fitter_cli browser-login. Usa el mismobrowserpara el inicio de sesión y el scraping: los sitios pueden vincular las sesiones a la huella del navegador. También soporta formatoIndexedDB[false] - incluir IndexedDB en el estado de almacenamiento persistido (algunos SPA, p. ej. Firebase Auth, mantienen tokens allí)
Proxy - configura el proxy para la solicitud config
Ejemplo
{
"timeout": 30,
"wait": 30,
"install": false,
"browser": "Chromium"
}Related MCP server: MCP Server Fetch Python
Model
Con model definimos el resultado del scraping
type Model struct {
ObjectConfig *ObjectConfig `yaml:"object_config" json:"object_config"`
ArrayConfig *ArrayConfig `json:"array_config" yaml:"array_config"`
BaseField *BaseField `json:"base_field" yaml:"base_field"`
IsArray bool `json:"is_array" yaml:"is_array"`
}La configuración puede ser una de:
ObjectConfig - configuración del formato de objeto
ArrayConfig - configuración del formato de array
BaseField - configuración de un campo único/generado
IsArray - bool[false] - fuerza indicar que el campo es un array (útil en caso de model field con base field)
Ejemplo:
{
"object_config": {}
}ObjectConfig
Configuración del objeto y sus campos
type ObjectConfig struct {
Fields map[string]*Field `json:"fields" yaml:"fields"`
Field *BaseField `json:"field" yaml:"field"`
ArrayConfig *ArrayConfig `json:"array_config" yaml:"array_config"`
Condition string `json:"condition" yaml:"condition"`
}Condition - expresión condition opcional evaluada contra el nodo fuente antes de la resolución; cuando es false, todo el objeto se omite del padre (los campos no se resuelven en absoluto)
La configuración puede ser una de:
Fields - mapa de la definición de cada campo; clave - nombre del campo, valor - configuración
Field - usado para el elemento de un array; campos que se deserializarán como tipo básico como "string", "int", etc. (usado aquí para el caso de arrays de tipos básicos)
ArrayConfig - usado para el elemento de un array; deserialización de array de arrays
Ejemplo:
{
"fields": {
"title": {
"base_field": {
"type": "string",
"path": "type"
}
}
}
}ArrayConfig
Configuración del array y sus campos
type ArrayConfig struct {
RootPath string `json:"root_path" yaml:"root_path"`
Reverse bool `yaml:"reverse" json:"reverse"`
ItemConfig *ObjectConfig `json:"item_config" yaml:"item_config"`
LengthLimit uint32 `json:"length_limit" yaml:"length_limit"`
Condition string `json:"condition" yaml:"condition"`
ItemCondition string `json:"item_condition" yaml:"item_condition"`
StaticConfig *StaticArrayConfig `json:"static_array" yaml:"static_array"`
}RootPath - selector para encontrar el elemento raíz del array o el elemento repetido en caso de análisis html; el tamaño del array será la cantidad de elementos hijos bajo la raíz
Reverse - bool[false] - indica que se necesita usar iteración inversa (n a 1)
LengthLimit - tamaño fijo del array (solo para arrays generados; no para estáticos). Nota: cuando la fuente tiene menos elementos que el límite, el array se rellena con
nulls al final para preservar el tamaño declarado (esto es intencional) — omitelength_limitpara obtener exactamente la longitud de la fuente en su lugarCondition - expresión condition opcional evaluada contra el nodo fuente antes de la resolución; cuando es false, todo el array se omite del padre
ItemCondition - expresión condition opcional evaluada contra cada elemento construido (fRes - valor del elemento, fSrc - elemento fuente, fIndex - índice del elemento); los elementos que resuelven a false se eliminan del array - filtrado declarativo. No se aplica a static_array
La configuración puede ser una de:
ItemConfig - configuración de cada elemento del array
StaticConfig - configuración del array estático
Ejemplo:
{
"root_path": "#content dt.quote > a",
"item_config": {
"field": {
"type": "string"
}
}
}Campo
Común del campo
type Field struct {
BaseField *BaseField `json:"base_field" yaml:"base_field"`
ObjectConfig *ObjectConfig `json:"object_config" yaml:"object_config"`
ArrayConfig *ArrayConfig `json:"array_config" yaml:"array_config"`
FirstOf []*Field `json:"first_of" yaml:"first_of"`
}La configuración puede ser una de:
BaseField - campos que se deserializarán como tipo básico como "string", "int", etc.
ObjectConfig - en caso de que nuestro campo sea un objeto anidado
ArrayConfig - en caso de que nuestro campo sea un array
FirstOf - se seleccionará el primer campo resuelto no vacío
Ejemplo:
{
"base_field": {
"type": "string",
"path": "div.current-temp span.heading"
}
}BaseField
En caso de que queramos obtener información estática o generar una nueva
type BaseField struct {
Type FieldType `yaml:"type" json:"type"`
Path string `yaml:"path" json:"path"`
HTMLAttribute string `json:"html_attribute" yaml:"html_attribute"`
Condition string `json:"condition" yaml:"condition"`
Generated *GeneratedFieldConfig `yaml:"generated" json:"generated"`
FirstOf []*BaseField `json:"first_of" yaml:"first_of"`
}FieldType - enum["null", "boolean", "string", "int", "int64", "float", "float64", "array", "object", "html", "raw_string"] - campo estático para parsear. Importante: el tipo html solo funciona con conectores que devuelvan HTML (HTMLAttribute - no tiene efecto en este caso). Ejemplo
Path - selector (relativo en caso de que sea hijo de un array) para el parseo
HTMLAttribute - valor extra que solo tiene efecto en el parseo de HTML mediante goquery. Aquí puedes especificar qué atributo se debe parsear.
Condition - condición opcional, expresión evaluada contra el valor extraído (fRes/fResJson/fResRaw, fIndex; fSrc - el nodo del que se resolvió el campo, incluidos los hermanos); cuando es falsa, el campo se omite del objeto/array padre en lugar de producir null. Se evalúa antes de Generated, por lo que una condición falsa también omite el trabajo generado (sub-solicitudes, descargas de archivos)
Importante: por defecto, el tipo "string" se recorta y todos los caracteres especiales se reemplazan; si necesitas una cadena simple, usa "raw_string"
La configuración puede ser una de o vacía:
Generated - el campo puede ser generado con configuración personalizada
FirstOf - se seleccionará el primer campo resuelto no vacío
Ejemplos
{
"generated": {
"uuid": {}
}
}{
"type": "string",
"path": "text()"
}Campos condicionales
Cada campo puede llevar una condition - una expresión expr-lang (valores predefinidos). Cuando se evalúa como cualquier cosa excepto true, el campo se omite de la salida (la clave/el elemento desaparece), no se establece como null. Una expresión inválida también omite el campo y registra un error.
Dónde se evalúa la condición:
BaseField.
condition- después de la extracción:fReses el valor extraído,fSrcel nodo del que se resolvió el campo (incluidos sus hermanos) - así quefSrc.on_sale == truepuede condicionar un campo a datos que no extrajiste. Una condición falsa omite por completo el trabajo generado (sin sub-solicitud, sin descarga de archivos)ObjectConfig.
condition/ ArrayConfig.condition- antes de la resolución:fRes/fSrcson el nodo fuente (valor parseado para json, contenido de texto para html)ArrayConfig.
item_condition- contra cada elemento construido:fReses el elemento,fSrcel elemento fuente del que se construyó,fIndexsu índice; los elementos falsos se descartan - filtrado declarativo de arrays. UsafSrcpara filtrar por atributos fuente sin añadirlos a la salida
Filtrar elementos del array - fSrc.in_stock lee el elemento fuente (no extraído en la salida), fRes.price el elemento construido:
{
"array_config": {
"root_path": "products",
"item_condition": "fSrc.in_stock && fRes.price > 0",
"item_config": {
"fields": {
"title": { "base_field": { "type": "string", "path": "title" } },
"price": { "base_field": { "type": "float", "path": "price" } }
}
}
}
}Omitir una clave a menos que el valor pase una comprobación:
{
"discount": {
"base_field": {
"type": "float",
"path": "discount_pct",
"condition": "fRes > 0"
}
}
}Casos especiales:
en un array estático, un elemento omitido permanece como
null(las posiciones están fijas por definición, los índices nunca se desplazan)si la configuración del modelo raíz se omite, el resultado del parseo es
nulldentro de first_of, una rama con condición falsa cuenta como vacía, por lo que se prueba la siguiente rama
Ejemplo ejecutable: examples/config_conditions.json
GeneratedFieldConfig
Proporciona la funcionalidad de generar campos sobre la marcha
type GeneratedFieldConfig struct {
UUID *UUIDGeneratedFieldConfig `yaml:"uuid" json:"uuid"`
Static *StaticGeneratedFieldConfig `yaml:"static" json:"static"`
Formatted *FormattedFieldConfig `json:"formatted" yaml:"formatted"`
Plugin *PluginFieldConfig `yaml:"plugin" json:"plugin"`
Calculated *CalculatedConfig `yaml:"calculated" json:"calculated"`
File *FileFieldConfig `yaml:"file" json:"file"`
Model *ModelField `yaml:"model" json:"model"`
FileStorageField *FileStorageField `json:"file_storage" yaml:"file_storage"`
}La configuración puede ser una de:
UUID - genera un UUID V4 aleatorio
Static - genera un campo estático
Formatted - formatea el campo
Model - modelo generado desde otro conector y modelo
Plugin - campo de plugin
Calculated - campo calculado
File - campo de archivo (para descargar archivos del servidor)
FileStorage - campo de archivo que se puede guardar en un archivo local
Ejemplos:
{
"uuid": {}
}https://github.com/PxyUp/fitter/blob/master/examples/cli/config_cli.json#L58
{
"model": {
"type": "array",
"model": {
"array_config": {
"root_path": "#content dt.quote > a",
"item_config": {
"field": {
"type": "string"
}
}
}
},
"connector_config": {
"response_type": "HTML",
"url": "http://www.quotationspage.com/random.php",
"attempts": 3,
"browser_config": {
"chromium": {
"path": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
"wait": 10000
}
}
}
}
}UUID
Genera un UUID V4 aleatorio sobre la marcha, se puede usar para generar un id único
type UUIDGeneratedFieldConfig struct {
Regexp string `yaml:"regexp" json:"regexp"`
}Regexp - proporciona un matcher que se puede usar para obtener parte del UUID generado
Static
Genera un campo estático
type StaticGeneratedFieldConfig struct {
Type FieldType `yaml:"type" json:"type"`
Value string `json:"value" yaml:"value"`
Raw json.RawMessage `json:"raw" yaml:"raw"`
}Type - enum["null", "boolean", "string", "int","int64","float","float64", "array", "object"] - tipo del campo
Value - valor de cadena del campo
Raw - valor json puro del campo
Ejemplo
{
"type": "int",
"value": "65"
}{
"type": "array",
"value": "[65,45]"
}{
"type": "array",
"raw": [65,45]
}Config de campo formateado
Genera un campo formateado que pasará el valor del campo base padre
type FormattedFieldConfig struct {
Template string `yaml:"template" json:"template"`
}Ejemplo: https://github.com/PxyUp/fitter/blob/master/examples/cli/config_cli.json#L98
{
"template": "https://news.ycombinator.com/item?id={PL}"
}Campo de almacenamiento de archivos
El campo se puede usar para almacenar el resultado del campo como archivo local
type FileStorageField struct {
Content string `json:"content" yaml:"content"`
Raw json.RawMessage `yaml:"raw" yaml:"raw"`
FileName string `json:"file_name" yaml:"file_name"`
Path string `json:"path" yaml:"path"`
Append bool `json:"append" yaml:"append"`
}Content - plantilla de cadena para el contenido. Importante: puede incluir inyección del valor padre como cadena
Raw - contenido json puro del campo. Importante: puede incluir inyección del valor padre como cadena
FileName - nombre de archivo local para almacenar el archivo. Por defecto, intenta obtener el FileName del encabezado y, después, de la url. Importante: puede incluir inyección del valor padre como cadena.
Path - directorio padre local para almacenar el archivo. La ruta por defecto es el directorio del proceso. Importante: puede incluir inyección del valor padre como cadena
Append[false] - añadir al archivo o no
{
"content": "{{{id}}}, {{{message}}}\n",
"append": true,
"file_name": "{{{id}}}.csv",
"path": "/Users/pxyup/fitter/examples/cli/test/csv"
}Campo de archivo
El campo se puede usar para descargar archivos del servidor localmente
type FileFieldConfig struct {
Config *ServerConnectorConfig `yaml:"config" json:"config"`
Url string `yaml:"url" json:"url"`
FileName string `json:"file_name" yaml:"file_name"`
Path string `json:"path" yaml:"path"`
}Config - ServerConfig usa el http.Client por defecto de fitter para enviar la solicitud
Url - url de la imagen. Importante: la URL en el conector puede incluir inyección del valor padre como cadena
FileName - nombre de archivo local para almacenar el archivo. Por defecto, intenta obtener el FileName del encabezado y, después, de la url. Importante: puede incluir inyección del valor padre como cadena.
Path - directorio padre local para almacenar el archivo. La ruta por defecto es el directorio del proceso. Importante: puede incluir inyección del valor padre como cadena
El resultado del campo será la ruta del archivo local como cadena
{
"url": "https://images.shcdn.de/resized/w680/p/dekostoff-gobelinstoff-panel-oriental-cat-46-x-46_P19-KP_2.jpg",
"path": "/Users/pxyup/fitter/bin",
"config": {
"method": "GET"
}
}Con URL propagada (inyección del valor padre como cadena)
{
"url": "https://picsum.photos{PL}",
"path": "/Users/pxyup/fitter/bin",
"config": {
"method": "GET"
}
}Ejemplo de configuración:
https://github.com/PxyUp/fitter/blob/master/examples/cli/config_image.json
https://github.com/PxyUp/fitter/blob/master/examples/cli/config_image_multiple.json
Campo calculado
El campo puede generar diferentes tipos según la expresión
type CalculatedConfig struct {
Type FieldType `yaml:"type" json:"type"`
Expression string `yaml:"expression" json:"expression"`
}Type - tipo resultante de la expresión\
Expression - expresión para el cálculo (usamos esta librería para la expresión calculada)
Valores predefinidos
FNull - alias para builder.Nullvalue
FNil - alias para nil
isNull(value T) - función para comprobar si el valor es FNull
fRes - es el resultado bruto (con el tipo adecuado) del parseo del campo base
fIndex - es el índice en el array padre (solo si el padre era un campo de array)
fResJson - es la representación JSON en cadena del resultado bruto
fResRaw - resultado en formato de bytes
fSrc - solo en expresiones de condition/item_condition: el nodo fuente del que se resolvió el valor (valor parseado para json - incluidos los hermanos, contenido de texto para html). No disponible en expresiones calculadas/formateadas/notifier
FNewLine - separador de nueva línea
{
"type": "bool",
"expression": "fRes > 500"
}Campo de plugin
El campo puede ser algún plugin externo para fitter
type PluginFieldConfig struct {
Name string `json:"name" yaml:"name"`
Config json.RawMessage `json:"config" yaml:"config"`
}Name - nombre del plugin (sin extensión, solo el nombre)
Config - configuración json del plugin
Campo de modelo
Tipo de campo que se puede generar sobre la marcha mediante un nuevo modelo y conector
type ModelField struct {
// Type of parsing
ConnectorConfig *ConnectorConfig `yaml:"connector_config" json:"connector_config"`
// Model of the response
Model *Model `yaml:"model" json:"model"`
Type FieldType `yaml:"type" json:"type"`
Path string `yaml:"path" json:"path"`
Expression string `yaml:"expression" json:"expression"`
}ConnectorConfig - qué conector usar. Importante: la URL en el conector puede incluir inyección del valor padre como cadena
Model - configuración del modelo subyacente
Type - enum["null", "boolean", "string", "int", "int64", "float", "float64", "array", "object"] - tipo del campo generado
Path - en caso de que no podamos extraer información del campo generado, podemos usar un selector json para extraerla
Expression - cadena que se puede usar para el post-procesamiento del Model (ignora el campo path)
Ejemplos:
https://github.com/PxyUp/fitter/blob/master/examples/cli/config_cli.json#L60
{
"type": "array",
"model": {
"array_config": {
"root_path": "#content dt.quote > a",
"item_config": {
"field": {
"type": "string"
}
}
}
}
}https://github.com/PxyUp/fitter/blob/master/examples/cli/config_weather.json#L37
{
"type": "string",
"path": "temp.temp",
"model": {
"object_config": {
"fields": {
"temp": {
"base_field": {
"type": "string",
"path": "//div[@id='forecast_list_ul']//td/b/a/@href",
"generated": {
"model": {
"type": "string",
"model": {
"object_config": {
"fields": {
"temp": {
"base_field": {
"type": "string",
"path": "div.current-temp span.heading"
}
}
}
}
},
"connector_config": {
"response_type": "HTML",
"attempts": 4,
"url": "https://openweathermap.org{PL}",
"browser_config": {
"playwright": {
"timeout": 30,
"wait": 30,
"install": false,
"browser": "FireFox",
"type_of_wait": "networkidle"
}
}
}
}
}
}
}
}
}
},
"connector_config": {
"response_type": "xpath",
"attempts": 3,
"url": "https://openweathermap.org/find?q={PL}",
"browser_config": {
"playwright": {
"timeout": 30,
"wait": 30,
"install": false,
"browser": "Chromium"
}
}
}
}Config de array estático
Proporciona la generación de arrays estáticos (de longitud fija)
type StaticArrayConfig struct {
Items map[uint32]*Field `yaml:"items" json:"items"`
Length uint32 `yaml:"length" json:"length"`
}Items - map[uint32]*Field - la clave es el índice en el array, el valor es la definición del campo
Length - si se establece (1+), se puede usar para definir la longitud personalizada del array
Ejemplos:
{
"0": {
"base_field": {
"type": "string",
"path": "div.current-temp span.heading"
}
}
}{
"length": 4,
"0": {
"base_field": {
"type": "string",
"path": "div.current-temp span.heading"
}
}
}{
"length": 4,
"2": {
"base_field": {
"type": "string",
"path": "div.current-temp span.heading"
}
}
}Lista de placeholders
{PL} - para inyectar valor
{INDEX} - para inyectar el índice en el array padre
{HUMAN_INDEX} - para inyectar el índice en el array padre de forma legible
{{{json_path}}} - obtendrá información del campo "object"/"array" propagado
{{{RefName=SomeName}}} - obtiene el valor de referencia por nombre. Ejemplo
{{{RefName=SomeName json.path}}} - obtiene el valor de referencia por nombre y extrae el valor mediante la ruta json. Ejemplo
{{{FromEnv=ENV_KEY}}} - obtiene el valor de una variable de entorno
{{{FromExp=fRes + 5 + fIndex}}} - obtiene el valor de la expresión. Valores predefinidos
{{{FromInput=.}}} o {{{FromInput=json.path}}} - obtiene el valor de la entrada del trigger o de la librería
{{{FromFile=./test_file.log}}} - obtiene el valor de un archivo por ruta. El contenido del archivo también puede contener placeholders
{{{FromURL=http://localhost:8081}}} - obtiene la respuesta de una url
Ejemplos:
{{{FromExp="{{{FromEnv=TEST_VAL}}}" + "hello"}}}Current time is: {PL} with token from TokenRef={{{RefName=TokenRef}}} and TokenObjectRef={{{RefName=TokenObjectRef token}}}Current time is: {PL} with token from TokenRef={{{RefName=TokenRef}}} and TokenObjectRef={{{RefName=TokenObjectRef token}}}TokenRef={{{RefName=TokenRef}}} and TokenObjectRef={{{RefName=TokenObjectRef token}}} Object={{{value}}} {PL} Env={{{FromEnv=TEST_VAL}}} {INDEX} {HUMAN_INDEX}Referencias
Mapa especial que se precarga (antes de cualquier procesamiento) y se puede usar para conector o para placeholder
Se puede usar para:
Cachear tokens jwt y usarlos en los encabezados
Cachear valores
Etc.
Referencia
type Reference struct {
*ModelField
Expire *uint32 `yaml:"expire" json:"expire"`
}ModelField - es una estructura incrustada, puedes usar los mismos campos
Expire[sec] - duración en la que la referencia caduca después de la obtención. No establecido => cacheado para siempre. Establecido a 0 => re-obtener cada vez. Establecido a n > 0 => cacheado durante n segundos
Para Fitter
type RefMap map[string]*Reference
type Config struct {
// Other Config Fields
Limits *Limits `yaml:"limits" json:"limits"`
References RefMap `json:"references" yaml:"references"`
}Para Fitter Cli
type RefMap map[string]*Reference
type CliItem struct {
// Other Config Fields
Limits *Limits `yaml:"limits" json:"limits"`
References RefMap `json:"references" yaml:"references"`
}References - map[string]*Reference - objeto donde la clave es ReferenceName (se puede usar para conector o placeholder) y el valor es Reference
Ejemplo
https://github.com/PxyUp/fitter/blob/master/examples/cli/config_ref.json#L2
{
"references": {
"TokenRef": {
"expire": 10,
"connector_config": {
"response_type": "json",
"static_config": {
"value": "\"plain token\""
}
},
"model": {
"base_field": {
"type": "string"
}
}
},
"TokenObjectRef": {
"connector_config": {
"response_type": "json",
"static_config": {
"value": "{\"token\":\"token from object\"}"
}
},
"model": {
"object_config": {
"fields": {
"token": {
"base_field": {
"type": "string",
"path": "token"
}
}
}
}
}
}
}
}Notificadores
Configuración opcional por elemento item.notifier_config que envía el resultado del parseo a algún lugar después del procesamiento. El resultado se devuelve igualmente como de costumbre (salida CLI/MCP, registros del servicio); el notificador además lo entrega. Funciona en Fitter (modo servicio), Fitter_CLI y Fitter_MCP.
type NotifierConfig struct {
Expression string `yaml:"expression" json:"expression"`
Force bool `json:"force" yaml:"force"`
SendArrayByItem bool `yaml:"send_array_by_item" json:"send_array_by_item"`
Template string `yaml:"template" json:"template"`
// exactly ONE destination:
Console *ConsoleConfig `yaml:"console" json:"console"`
TelegramBot *TelegramBotConfig `yaml:"telegram_bot" json:"telegram_bot"`
Http *HttpConfig `yaml:"http" json:"http"`
Redis *RedisNotifierConfig `json:"redis" yaml:"redis"`
File *FileStorageField `json:"file" yaml:"file"`
}Expression - condición opcional expr-lang: notificar solo cuando se evalúe como verdadero. El resultado del análisis está disponible como
fRes(valor analizado),fResRaw(bytes crudos),fResJson(cadena JSON), p. ej.len(fResRaw) > 0Force - notificar incluso si el análisis finalizó con un error
SendArrayByItem - si el resultado es un array, enviar cada elemento como una notificación separada
Template - plantilla opcional aplicada al resultado antes de enviar, placeholders permitidos
Destination - exactamente uno de
console,telegram_bot,http,redis,file
Configuraciones de destino:
type HttpConfig struct {
Url string `yaml:"url" json:"url"`
Method string `json:"method" yaml:"method"`
Headers map[string]string `yaml:"headers" json:"headers"`
Timeout uint32 `yaml:"timeout" json:"timeout"`
}
type TelegramBotConfig struct {
Token string `json:"token" yaml:"token"`
UsersId []int64 `json:"users_id" yaml:"users_id"`
Pretty bool `json:"pretty" yaml:"pretty"`
OnlyMsg bool `json:"only_msg" yaml:"only_msg"`
}
type RedisNotifierConfig struct {
Addr string `json:"addr" yaml:"addr"`
Password string `json:"password" yaml:"password"`
DB int `json:"db" yaml:"db"`
Channel string `json:"channel" yaml:"channel"`
}
type ConsoleConfig struct {
OnlyResult bool `json:"only_result" yaml:"only_result"`
}El destino file utiliza el mismo FileStorageField que el tipo de campo de archivo.
Ejemplo (examples/config_telegram.json):
{
"item": {
"connector_config": { "...": "..." },
"model": { "...": "..." },
"notifier_config": {
"expression": "len(fResRaw) > 0",
"telegram_bot": {
"token": "{{{FromEnv=TG_TOKEN}}}",
"users_id": [123456],
"pretty": true
}
}
}
}Límites
Proporciona limitaciones para prevenir DDOS y un gran uso de memoria.
type Limits struct {
HostRequestLimiter HostRequestLimiter `yaml:"host_request_limiter" json:"host_request_limiter"`
ChromiumInstance uint32 `yaml:"chromium_instance" json:"chromium_instance"`
DockerContainers uint32 `yaml:"docker_containers" json:"docker_containers"`
PlaywrightInstance uint32 `yaml:"playwright_instance" json:"playwright_instance"`
}HostRequestLimiter - map[string]int64 - limitación por nombre de host, la clave es el host, el valor es la cantidad de solicitudes paralelas (uso para server connector)
ChromiumInstance - cantidad de instancias chromium en paralelo
DockerContainers - cantidad de instancias docker en paralelo
PlaywrightInstance - cantidad de instancias playwright en paralelo
https://github.com/PxyUp/fitter/blob/master/examples/cli/config_cli.json#L2
{
"limits": {
"host_request_limiter": {
"hacker-news.firebaseio.com": 5
},
"chromium_instance": 3,
"docker_containers": 3,
"playwright_instance": 3
}
}Available Tools
6 toolsfitter_config_referenceA
Return a condensed reference of the Fitter config format (connectors, parsers, model/field schema, placeholders, notifiers, references, limits) with working examples. Use it before authoring a config for fitter_run.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description carries burden. Describes output but does not explicitly state that tool is read-only or has no side effects, though context implies safe operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with purpose, no fluff. Every part earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no parameters or output schema, description sufficiently covers purpose and usage. Could mention response format but not critical for a reference tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
No parameters, schema coverage is 100% trivially. Baseline 4 applies, and description adds value by listing what the reference includes.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it returns a condensed reference of the Fitter config format with working examples, and distinguishes itself from sibling run tools by advising use before authoring a config for fitter_run.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly recommends using before authoring a config for fitter_run, providing clear context. However, it does not mention exclusions or alternatives, but siblings are run tools making differentiation obvious.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fitter_inspect_urlA
Fetch a URL and return a compact structure outline plus candidate selectors/paths, so you can author a fitter config that matches on the first try instead of guessing selectors and getting nulls. For JSON it lists gjson paths with types and sample values; for HTML it lists repeated elements (candidate array_config root_path / list rows) and link/heading selectors. For client-rendered SPAs (content built by JavaScript), a plain fetch sees only an empty shell — the output warns when it detects one; pass render:true to render it in a headless browser first (mirrors what a browser_config scrape would see). Read-only helper that does NOT extract data — use it before fitter_run, then fitter_run to actually extract.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | HTTP(S) URL to fetch and inspect for its structure and candidate selectors. | |
| render | No | Render the page in a headless browser (Playwright/Chromium) before inspecting — needed for client-rendered SPAs whose content is built by JavaScript and is absent from the raw HTML. Requires browser support (the fitter-mcp:playwright image or a local Playwright install). | |
| response_type | No | Optional hint for how to read the response: json, HTML, xpath or XML. Empty auto-detects from the Content-Type/body. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It clearly states the tool is read-only and does not extract data, and explains behavior for different content types (JSON, HTML, SPAs) and the render option.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is detailed but each sentence adds unique value, covering purpose, output, parameter usage, and distinctions from execution tools. It is front-loaded with the primary purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no output schema, the description describes the output as a 'compact structure outline plus candidate selectors/paths' and gives specifics for JSON and HTML. It also covers the render behavior for SPAs, making the tool's behavior well understood.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description expands on each parameter beyond the schema: url as the target, render for SPAs, and response_type as an optional hint with auto-detection. It explains why the parameters matter and how they affect the output.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Fetch a URL and return a compact structure outline') and differentiates from sibling tools by positioning it as an inspection step before fitter_run. Clearly identifies the tool's role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use it ('before fitter_run') and what it does not do ('does NOT extract data'), plus provides guidance on when to set render:true for SPAs. Also mentions the response_type hint.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fitter_runA
Run a Fitter scraping/parsing config passed inline (JSON or YAML) and return the extracted data as JSON. Fitter fetches data via a connector (HTTP request, headless browser, static value, file, ...) and extracts structured data using json/HTML/XML/xpath selectors described by a declarative model. Call fitter_config_reference first if you are unsure about the config format.
| Name | Required | Description | Default |
|---|---|---|---|
| input | No | Optional input value (plain string or JSON), available in the config via {{{FromInput=.}}} or {{{FromInput=json.path}}} placeholders. | |
| config | Yes | Fitter CliItem config as a JSON or YAML string. Top-level keys: item (required), limits, references. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must disclose behaviors. It mentions fetching data via connectors and extracting data, implying network access. However, it omits potential side effects like rate limits, authentication needs, or error scenarios, which would strengthen transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is brief, with a clear structure: first sentence states the tool's purpose, second explains the underlying Fitter mechanism, third gives a usage tip. Every sentence contributes directly to understanding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with two parameters, no output schema, and no annotations, the description provides sufficient context: config format, supported selectors, and a reference to the config spec tool. It could be more complete by noting potential timeouts or result size limitations, but overall it covers the essential information.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% (both parameters described). The description adds context beyond the schema by explaining that config is JSON/YAML, highlighting top-level keys (item, limits, references), and stating that output is JSON. This adds meaningful value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the action ('Run a Fitter scraping/parsing config passed inline') and the resource (inline config). It distinguishes from siblings by specifying 'inline', contrasting with file- and URL-based tools. The purpose is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description advises calling fitter_config_reference first if unsure about the config format, providing clear guidance. However, it does not explicitly compare this tool to fitter_run_file or fitter_run_url, leaving the selection of the appropriate sibling somewhat implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fitter_run_fileA
Run a Fitter scraping/parsing config from a local JSON or YAML file and return the extracted data as JSON. Same as fitter_run but reads the config from disk.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Absolute path to a Fitter config file (.json, .yaml or .yml) with top-level keys: item (required), limits, references. | |
| input | No | Optional input value (plain string or JSON), available in the config via {{{FromInput=.}}} or {{{FromInput=json.path}}} placeholders. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description must fully disclose behavioral traits. It states the tool returns extracted data as JSON but does not mention whether modifications occur, required permissions, or error handling (e.g., file not found). The description is minimal and lacks transparency beyond the basic operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise with two sentences, front-loading the primary purpose. Every sentence adds value: first defines the tool, second clarifies the difference from a sibling. No fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has 2 parameters and no output schema, the description covers the basic purpose but omits important context like what happens if the file is invalid, permissions needed, or error scenarios. It is adequate for simple use but has gaps compared to a fully transparent description.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds minimal additional meaning beyond the schema; it only reiterates that 'input' is optional and used with placeholders, which the schema already covers. No further value is added for the 'path' parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states the verb (Run), the resource (Fitter config file), and distinguishes it from fitter_run by specifying 'reads the config from disk.' It also indicates the output format (JSON). This differentiates it from sibling tools like fitter_config_reference, fitter_run, and fitter_run_url.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly notes 'Same as fitter_run but reads the config from disk,' which helps users decide between this tool and fitter_run. However, it does not provide explicit when-not-to-use scenarios or mention other alternatives besides the direct sibling comparison.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fitter_run_urlA
Run a Fitter scraping/parsing config downloaded from an HTTP(S) URL (JSON or YAML) and return the extracted data as JSON. Same as fitter_run but fetches the config from a remote location, e.g. a raw GitHub link.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | HTTP(S) URL of a Fitter config (JSON or YAML) with top-level keys: item (required), limits, references. | |
| input | No | Optional input value (plain string or JSON), available in the config via {{{FromInput=.}}} or {{{FromInput=json.path}}} placeholders. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must fully disclose behavior. It states the tool downloads config from a URL and returns JSON, but omits important details such as network error handling, timeout limits, authentication, size restrictions, or what happens with invalid configs. This lack of transparency could lead to unexpected failures.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two concise sentences with no unnecessary words. It front-loads the action and result, then adds the key distinction from 'fitter_run'. Every sentence provides useful information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool involves remote fetching and parsing, but the description does not detail the return format beyond 'extracted data as JSON', nor does it explain error conditions or required permissions. With no output schema, more detail would be beneficial for an agent to anticipate the response structure.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already covers both parameters with descriptions (100% coverage). The description adds value by specifying the required top-level keys of the config ('item', 'limits', 'references'), which aids in understanding the expected structure beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool runs a Fitter config from an HTTP(S) URL and returns JSON data. It explicitly distinguishes itself from 'fitter_run' by noting the remote fetching behavior, making the purpose specific and differentiated from siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description indicates when to use this tool over 'fitter_run' (remote vs local config) and gives an example (raw GitHub link). However, it does not explicitly mention when not to use it or alternatives like 'fitter_run_file', though the context from the name and sibling list provides some guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fitter_validate_configA
Validate a Fitter config (JSON or YAML) without executing it. Checks the structural rules: item/connector_config/model presence, valid response_type, that the connector has a data source, and compiles every condition/item_condition expression in the model. Returns "valid" or the validation error. Cheap and safe — use it while iterating on a config before calling fitter_run.
| Name | Required | Description | Default |
|---|---|---|---|
| config | Yes | Fitter CliItem config as a JSON or YAML string to validate without executing it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully carries the burden, detailing what it checks (structural rules, condition compilation), that it is cheap and safe, and that it returns 'valid' or error. This comprehensively discloses behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: purpose, checks, and usage advice. Front-loaded and succinct with no redundancies.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the single parameter and no output schema, the description fully covers purpose, behavior, usage context, and return type. It is complete for effective tool selection.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description reinforces the config parameter but adds no new parameter-level details beyond the schema description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states 'Validate a Fitter config (JSON or YAML) without executing it,' clearly specifying the verb and resource. It distinguishes from sibling tools like fitter_run by advising use before calling fitter_run.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly advises using this tool while iterating on a config before calling fitter_run, providing clear when-to-use context. However, it does not explicitly state when not to use it or mention alternatives for different scenarios.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
1 tool update
v1.8.2- Added
fitter_inspect_url
1 tool update
v1.7.0- Added
fitter_validate_config
4 tool updates
v0.1.0- First observed
fitter_config_reference - First observed
fitter_run - First observed
fitter_run_file - First observed
fitter_run_url
TDQS
Each tool serves a distinct purpose: reference, inspection, execution (with three source variants), and validation. No overlap or ambiguity between them.
All tools follow the 'fitter_' prefix with snake_case, and the action part is consistently descriptive (inspect, run, validate). The naming pattern is uniform and predictable.
Six tools is ideal for a config-driven scraping/parsing workflow: reference, inspect, run (three variants), and validate. Not bloated or sparse.
The toolset covers the full lifecycle: learning the format (reference), inspecting target structure (inspect), validating configs (validate), and executing from inline, file, or URL sources. No missing functionality apparent.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
MCP server for web extraction and rendering via AceDataCloud WebExtrator
One MCP server for 180+ live web-data APIs returning clean JSON from sites that block scrapers.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceMCP server for interacting with Prometheus metrics and data.17MIT
- AlicenseAqualityCmaintenanceAn MCP server for fetching and transforming web content into various formats.48MIT
- FlicenseBqualityDmaintenanceAn MCP Server for Web scraping and Crawling, built using Crawl4AI224-
- AlicenseAqualityCmaintenanceMCP server for web scraping — extract clean markdown, links, and metadata from any URL. Free Firecrawl alternative.51575MIT
Appeared in Searches
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/PxyUp/fitter'
If you have feedback or need assistance with the MCP directory API, please join our Discord server