Skip to main content
Glama

Fitter — datos web para agentes de IA

MCP Toplist

Release License: MIT Go Reference Sponsor

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

demo de fitter — configuración declarativa a JSON estructurado

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": true en el conector playwright — fitter descarga el controlador + navegador que coincida con su versión integrada de playwright-go en 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-go contra la que está compilado fitter (consulta go.mod, actualmente v0.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 libraries

    La versión debe coincidir exactamente con go.modplaywright-go se 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

fitter_run

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 input opcional disponible en la configuración mediante {{{FromInput=.}}} / {{{FromInput=json.path}}}

fitter_run_file

Igual que fitter_run pero lee la configuración de un archivo local .json/.yaml

fitter_run_url

Igual que fitter_run pero descarga la configuración desde una URL HTTP(S), por ejemplo, un enlace raw de GitHub

fitter_inspect_url

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 render-izarlas en un navegador sin interfaz. Solo lectura — no extrae

fitter_validate_config

Valida una configuración sin ejecutarla (estructura, response_type, fuente de datos del conector, modelo). Útil mientras se itera sobre una configuración

fitter_config_reference

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> (env FITTER_MCP_HTTP_ADDR) — dirección de escucha; modo stdio cuando está vacío

  • FITTER_MCP_AUTH_TOKEN — cuando se establece, cada petición /mcp debe enviar Authorization: Bearer <token>; sin él, el endpoint no está autenticado, así que enlaza a localhost o colócalo detrás de un proxy

  • --stateless (env FITTER_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:latest

La 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-playwright

Está 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:latest

Nota: 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:playwright

Usa 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

  1. FITTER_PLUGINS - string[""] - ruta para la carpeta de plugins, igual que la bandera --plugins de Fitter/Fitter_CLI

  2. FITTER_MCP_HTTP_ADDR - string[""] - dirección de escucha para modo remoto, igual que --http

  3. FITTER_MCP_AUTH_TOKEN - string[""] - token de portador que protege el endpoint HTTP

  4. 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,0

Extraer 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:

examples/config_pdf.json

{
  "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

  1. Server - analizar la respuesta de algunas API o solicitudes http (uso de http.Client)

  2. Browser - emular un navegador real usando chromium + docker + playwright/cypress y obtener información del DOM

  3. Static - analizar una cadena estática como datos

Formato que se puede analizar

  1. JSON - analizar JSON para obtener información específica

  2. XML - analizar el árbol XML para obtener información específica

  3. HTML - analizar el árbol DOM para obtener información específica

  4. XPath - analizar el árbol DOM para obtener información específica pero mediante xpath

  5. PDF - extraer texto de documentos PDF; el contenido se expone como JSON {"text": "...", "pages": ["..."], "total_pages": N} de modo que las rutas JSON normales como text o pages.0 funcionan

Usar como librería

go get github.com/PxyUp/fitter
package 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.json

Argumentos

  1. --path - string[""] - ruta para la configuración de Fitter

  2. --url - string[""] - url para la configuración de Fitter

  3. --verbose - bool[false] - habilitar registro

  4. --plugins - string[""] - ruta para plugins de Fitter

  5. --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.json

Argumentos

  1. --path - string[""] - ruta para la configuración de Fitter_CLI

  2. --url - string[""] - url para la configuración de Fitter_CLI

  3. --copy - bool[false] - copiar información al portapapeles

  4. --pretty - bool[true] - hacer legible el resultado (también afecta a la copia)

  5. --verbose - bool[false] - habilitar registro

  6. --omit-error-pretty - bool[false] - Proporcionar valor puro si pretty no es válido

  7. --plugins - string[""] - ruta para plugins de Fitter

  8. --log-level - enum["info", "error", "debug", "fatal"] - establecer nivel de registro (solo si verbose está activado)

  9. --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=true

fitter_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.json

Argumentos:

  1. --provider - preajuste con endpoints conocidos: github|google|microsoft|gitlab|spotify

  2. --client-id / --client-secret - credenciales de la aplicación OAuth2 (algunos flujos de dispositivo funcionan sin secreto)

  3. --token-file - dónde almacenar el token recibido (permisos 0600); referencia la misma ruta en oauth2.token_file

  4. --flow - auto (dispositivo si está disponible, si no navegador), device (visitar una url + introducir un código) o browser (callback de localhost con PKCE, puerto predeterminado 8988 — registra http://127.0.0.1:8988/callback como la url de callback de la aplicación)

  5. --scopes - ámbitos separados por comas

  6. --auth-url/--token-url/--device-auth-url/--auth-style - anulaciones de endpoints para proveedores sin preajuste

  7. --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 es http://127.0.0.1:8988/callback

  8. --listen - dirección de enlace del flujo de navegador, predeterminada 127.0.0.1; establece 0.0.0.0 dentro de un contenedor para que el puerto publicado llegue al oyente (env FITTER_AUTH_LISTEN)

  9. --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)

  10. --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 session

Argumentos:

  1. --url - página de inicio de sesión a abrir (obligatorio)

  2. --storage-state - dónde guardar la sesión (cookies + localStorage, permisos 0600); referencia la misma ruta en playwright.storage_state_file (obligatorio)

  3. --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

  4. --install - bool[false] - instalar primero los navegadores de playwright

  5. --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:

  1. Versión Server HackerNews + Quotes + Guardian News - usando análisis de API + HTML + XPath

  2. Versión Chromium Guardian News + Quotes - usando análisis de HTML + emulación de navegador

  3. Versión Docker Versión Docker: Guardian News + Quotes - usando análisis de HTML + navegador desde imagen Docker

  4. Versión Playwright Versión Playwright: Guardian News + Quotes - usando análisis de HTML + navegador desde el framework Playwright

  5. Versión Playwright Versión Playwright: England Cities + Weather - usando análisis de HTML + XPath + navegador desde el framework Playwright

  6. Versión JSON Generar paginación - usando conector estático para generar array de paginación

  7. 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.go

Argumentos

  1. --api-key - string[""] - clave de API de Anthropic. Prefiere la variable de entorno ANTHROPIC_API_KEY para que la clave no termine en el historial de tu shell

  2. --model - string["claude-opus-4-8"] - modelo de Claude a usar

  3. --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

  4. --verbose - bool[false] - habilitar registro

  5. --log-level - enum["info", "error", "debug", "fatal"] - establecer nivel de registro

  6. --plugins - string[""] - ruta para plugins de Fitter

  7. --chromium-limit - uint[0] - limitar instancias concurrentes de Chromium

  8. --docker-limit - uint[0] - limitar contenedores Docker concurrentes

  9. --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 URL

Usa 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

Get Bitcoin price from CoinGecko API

Obtiene el precio actual de BTC

Scrape headlines from news.ycombinator.com with links

Scraping HTML con selectores CSS

Get top 5 stories from HackerNews with titles

Llamadas API anidadas

Fetch weather data from wttr.in for London

Extracción simple de API

Scrape product names and prices from example.com

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.go

Asegú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]
}

Ejemplo de configuración

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

  • Raw - acepta json crudo. Ejemplo. También soporta formato

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 es true, un estado de respuesta HTTP >= 400 se trata como un error de obtención (fluyendo a través de attempts / 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 en false mantiene 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 Authorization config

Las solicitudes envían un User-Agent identificable (fitter (+https://github.com/PxyUp/fitter)) por defecto; establece tu propio User-Agent en Headers para 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. audience para 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
  1. 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
  1. DOCKER_HOST - string - (EnvOverrideHost) para establecer la URL del servidor docker.

  2. DOCKER_API_VERSION - string - (EnvOverrideAPIVersion) para establecer la versión de la API a usar, dejar vacío para la última.

  3. DOCKER_CERT_PATH - string - (EnvOverrideCertPath) para especificar el directorio desde el que cargar los certificados TLS (ca.pem, cert.pem, key.pem).

  4. 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-go en el primer uso; no es necesario con la imagen ghcr.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 mismo browser para el inicio de sesión y el scraping: los sitios pueden vincular las sesiones a la huella del navegador. También soporta formato

  • IndexedDB[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:

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) — omite length_limit para obtener exactamente la longitud de la fuente en su lugar

  • Condition - 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:

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: fRes es el valor extraído, fSrc el nodo del que se resolvió el campo (incluidos sus hermanos) - así que fSrc.on_sale == true puede 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/fSrc son el nodo fuente (valor parseado para json, contenido de texto para html)

  • ArrayConfig.item_condition - contra cada elemento construido: fRes es el elemento, fSrc el elemento fuente del que se construyó, fIndex su índice; los elementos falsos se descartan - filtrado declarativo de arrays. Usa fSrc para 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 null

  • dentro 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"`
}
  • Template - plantilla con el placeholder {PL} donde se inyectará el valor padre como cadena

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": "{{{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"`
}

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

Más

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

  1. {PL} - para inyectar valor

  2. {INDEX} - para inyectar el índice en el array padre

  3. {HUMAN_INDEX} - para inyectar el índice en el array padre de forma legible

  4. {{{json_path}}} - obtendrá información del campo "object"/"array" propagado

  5. {{{RefName=SomeName}}} - obtiene el valor de referencia por nombre. Ejemplo

  6. {{{RefName=SomeName json.path}}} - obtiene el valor de referencia por nombre y extrae el valor mediante la ruta json. Ejemplo

  7. {{{FromEnv=ENV_KEY}}} - obtiene el valor de una variable de entorno

  8. {{{FromExp=fRes + 5 + fIndex}}} - obtiene el valor de la expresión. Valores predefinidos

  9. {{{FromInput=.}}} o {{{FromInput=json.path}}} - obtiene el valor de la entrada del trigger o de la librería

  10. {{{FromFile=./test_file.log}}} - obtiene el valor de un archivo por ruta. El contenido del archivo también puede contener placeholders

  11. {{{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:

  1. Cachear tokens jwt y usarlos en los encabezados

  2. Cachear valores

  3. 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"`
}

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"
              }
            }
          }
        }
      }
    }
  }
}

Ejemplo

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) > 0

  • Force - 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 tools
fitter_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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesHTTP(S) URL to fetch and inspect for its structure and candidate selectors.
renderNoRender 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_typeNoOptional hint for how to read the response: json, HTML, xpath or XML. Empty auto-detects from the Content-Type/body.

TDQS

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
inputNoOptional input value (plain string or JSON), available in the config via {{{FromInput=.}}} or {{{FromInput=json.path}}} placeholders.
configYesFitter CliItem config as a JSON or YAML string. Top-level keys: item (required), limits, references.

TDQS

A4.2/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYesAbsolute path to a Fitter config file (.json, .yaml or .yml) with top-level keys: item (required), limits, references.
inputNoOptional input value (plain string or JSON), available in the config via {{{FromInput=.}}} or {{{FromInput=json.path}}} placeholders.

TDQS

A3.7/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesHTTP(S) URL of a Fitter config (JSON or YAML) with top-level keys: item (required), limits, references.
inputNoOptional input value (plain string or JSON), available in the config via {{{FromInput=.}}} or {{{FromInput=json.path}}} placeholders.

TDQS

A3.9/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
configYesFitter CliItem config as a JSON or YAML string to validate without executing it.

TDQS

A4.5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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. 1 tool updatev1.8.2
    • Addedfitter_inspect_url
  2. 1 tool updatev1.7.0
    • Addedfitter_validate_config
  3. 4 tool updatesv0.1.0
    • First observedfitter_config_reference
    • First observedfitter_run
    • First observedfitter_run_file
    • First observedfitter_run_url

TDQS

A4.4/5.0
Disambiguation5/5

Each tool serves a distinct purpose: reference, inspection, execution (with three source variants), and validation. No overlap or ambiguity between them.

Naming Consistency5/5

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.

Tool Count5/5

Six tools is ideal for a config-driven scraping/parsing workflow: reference, inspect, run (three variants), and validate. Not bloated or sparse.

Completeness5/5

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

ActivityMaintained
ResponsivenessUnresponsive

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

Related MCP Servers

Latest Blog Posts

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