Fitter MCP
Fitter — веб-данные для ИИ-агентов
Fitter превращает любой веб-сайт или API в структурированный JSON — декларативно. Один JSON/YAML-конфиг описывает, где находятся данные (HTTP-запрос, headless-браузер, файл, статическое значение) и что извлекать (JSON-пути, CSS-селекторы, XPath). Никакого кода, никаких хрупких скриптов для парсинга.
🚀 Попробуйте в браузере — настоящий движок, скомпилированный в WebAssembly: живые примеры, визуальный конструктор конфигов, без установки.
Поскольку конфиги — это обычные данные, LLM могут их создавать. Встроенный MCP-сервер позволяет Claude Code, Claude Desktop или любому MCP-клиенту писать и запускать пайплайны парсинга на вашей машине по требованию:
«Получи топ-5 историй HackerNews с заголовками и оценками» → модель создаёт конфиг fitter, проверяет его, запускает локально и получает чистый JSON.
Один движок, пять способов использования:
🤖 Fitter MCP | MCP-сервер, предоставляющий fitter для Claude Code, Claude Desktop и любого MCP-клиента |
🧠 Fitter Agent | CLI на основе ИИ: естественный язык → конфиг → выполненный результат |
🖥 Fitter CLI | запуск конфигов локально для тестирования/отладки/домашнего использования |
📦 Fitter Lib | встраивание движка в вашу Go-программу |
⚙️ Fitter | долго работающий сервисный режим с планированием и уведомлениями |
Почему fitter для ИИ-агентов?
Декларативность и аудируемость — агент создаёт конфиг, который можно прочитать, сохранить и перезапустить, а не одноразовый код
Локальность — все запросы выполняются на вашей машине; никаких сторонних API для парсинга, ключей и оплаты за запрос
Всё включено — HTTP-клиент, headless-браузер (Playwright/Chromium/Docker), парсинг JSON/HTML/XML/XPath/PDF, пагинация, кэшируемые ссылки, лимиты на хост — в одном статическом бинарнике
Многоразовость — то, что агент создал сегодня, завтра станет cron-задачей или сервисным конфигом
Как использовать Fitter_MCP
Fitter MCP — это сервер Model Context Protocol (транспорт stdio), который позволяет любому MCP-клиенту — Claude Code, Claude Desktop, IDE-ассистентам, пользовательским агентам — запускать конфиги Fitter и получать структурированный JSON.
Быстрый старт (Claude Desktop — один клик)
Скачайте fitter-mcp-<os>-<arch>.mcpb со страницы релизов и откройте его — Claude Desktop установит сервер автоматически.
Быстрый старт (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"Затем просто спросите:
Получи топ-5 историй HackerNews с заголовками и оценками с помощью fitter
Модель вызывает fitter_config_reference, создаёт конфиг, при необходимости проверяет его с помощью fitter_validate_config и выполняет через fitter_run — все запросы данных выполняются локально на вашей машине. Для готового пайплайна попробуйте examples/config_morning_briefing.json:
Запусти examples/config_morning_briefing.json с помощью fitter и дай мне сводку
Регистрация в Claude Desktop
{
"mcpServers": {
"fitter": {
"command": "/path/to/fitter_mcp"
}
}
}Поддержка браузера (Playwright)
Пакет .mcpb и нативный бинарник поставляются без браузеров: HTTP, статические и файловые коннекторы работают из коробки, но браузерные конфиги (коннектор playwright) требуют браузеров Playwright. Несколько способов их получить:
При первом использовании (нативный бинарник /
.mcpb): установите"install": trueв коннекторе playwright — fitter загрузит драйвер и браузер, соответствующие встроенной версииplaywright-go, при первом использовании (однократно, с кэшированием), так что отдельный шаг установки не нужен.Заранее (нативный, опционально): чтобы избежать загрузки при первом запуске, установите браузеры заранее с той же версией
playwright-go, с которой собран fitter (см.go.mod, сейчас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Версия должна точно совпадать с
go.mod—playwright-goотказывается работать с несовместимым драйвером. Затем запускайте конфиги без"install": true.Docker: используйте образ
ghcr.io/pxyup/fitter-mcp:playwright, в который предустановлены Chromium, Firefox и WebKit (не нужен"install": true).
Инструменты
Tool | Description |
| Запустить конфиг Fitter, переданный инлайн (строка JSON или YAML), и вернуть извлечённые данные в виде JSON. Принимает необязательное значение |
| То же, что |
| То же, что |
| Получить URL и вернуть компактную структуру + возможные селекторы/пути (gjson-пути для JSON; селекторы повторяющихся элементов/строк списка для HTML), чтобы модель создавала конфиг с первой попытки, а не угадывала селекторы и получала null. Определяет клиентские SPA и может |
| Проверить конфиг без выполнения (структура, |
| Вернуть сжатый справочник по всему формату конфига (коннекторы, парсеры, схема модели/полей, плейсхолдеры, уведомители, ссылки, лимиты) с рабочими примерами, чтобы модель могла создавать конфиги без внешней документации |
Справочник также доступен как MCP-ресурс fitter://config-reference для клиентов, поддерживающих ресурсы.
Формат конфига точно такой же, как для Fitter_CLI: объект верхнего уровня с item (обязательно), limits и references. Уведомители тоже работают (результат дополнительно отправляется в http/telegram/redis/file/console); trigger_config и http_server доступны только в сервисном режиме и игнорируются в MCP-вызовах.
Удалённый / размещённый режим (streamable HTTP)
По умолчанию fitter_mcp использует stdio. Передайте --http, чтобы использовать streamable HTTP transport — для общего сервера команды, контейнера или любого удалённого развёртывания:
# serve MCP at http://<host>:8080/mcp (health probe at /healthz)
FITTER_MCP_AUTH_TOKEN=my-secret fitter_mcp --http :8080
# register the remote endpoint in Claude Code
claude mcp add --transport http fitter http://localhost:8080/mcp --header "Authorization: Bearer my-secret"--http <addr>(envFITTER_MCP_HTTP_ADDR) — адрес прослушивания; при пустом значении — режим stdioFITTER_MCP_AUTH_TOKEN— если задан, каждый запрос к/mcpдолжен содержатьAuthorization: Bearer <token>; без него конечная точка не аутентифицирована, поэтому привязывайтесь к localhost или ставьте за прокси--stateless(envFITTER_MCP_STATELESS=true) — без состояния на сессию, поэтому реплики могут находиться за балансировщиком без липких сессий
Сервер корректно завершает работу по SIGINT/SIGTERM.
Docker
С каждым релизом поставляется компактный multi-arch образ (linux/amd64 + linux/arm64):
# 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Компактный образ содержит только бинарник fitter и CA-сертификаты: серверные/статические/файловые коннекторы работают, браузерные коннекторы (chromium/docker/playwright) — нет.
Для браузерных конфигов используйте вариант playwright, который включает Playwright с Chromium, Firefox и WebKit (соответствует версии playwright-go, с которой собран fitter, поэтому в конфигах не нужен "install": true):
docker run --rm -i ghcr.io/pxyup/fitter-mcp:playwright # stdio mode
# per-release tag: ghcr.io/pxyup/fitter-mcp:vX.Y.Z-playwrightОн собран из Dockerfile.mcp-playwright; собирайте с --build-arg PLAYWRIGHT_BROWSERS=chromium для меньшего образа только с Chromium.
Учётные записи OAuth2 в Docker
Оба образа содержат fitter_cli, поэтому одноразовый OAuth2-вход можно выполнить внутри контейнера. Сохраните токен на томе, смонтированном в /tokens (предварительно созданном и доступном для записи в образе), и передайте его 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Примечание: 8988 нужен только для одноразового входа через браузер; сам MCP-сервер не требует порта в режиме stdio и только 8080 в размещённом HTTP-режиме.
Сессии браузера с входом в Docker
Сессии браузера требуют образа playwright (в компактном нет браузеров). Одноразовый вход с отображением требует дисплея, поэтому запускайте его на хосте, затем смонтируйте каталог сессий в контейнер (образ предварительно создаёт доступный для записи /sessions):
# 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Используйте bind mount (не именованный том): контейнер записывает обновлённые куки после каждого запуска, поэтому копия на хосте остаётся актуальной и её можно в любой момент расширить с помощью browser-login.
Том должен оставаться доступным для записи для сервера: обновлённые refresh-токены записываются при каждом обновлении.
Переменные окружения
FITTER_PLUGINS - string[""] - путь к папке плагинов, то же, что флаг
--pluginsу Fitter/Fitter_CLIFITTER_MCP_HTTP_ADDR - string[""] - адрес прослушивания для удалённого режима, то же, что
--httpFITTER_MCP_AUTH_TOKEN - string[""] - bearer-токен, защищающий HTTP-конечную точку
FITTER_MCP_STATELESS - bool[false] - stateless HTTP-транспорт, то же, что
--stateless
Рецепты
Полные, протестированные конфигурации, демонстрирующие основные паттерны. Все они работают без изменений через Fitter_MCP (fitter_run_file), Fitter_CLI или библиотеку — больше в examples/.
Спарсить страницу без API и обогатить данными из API
У GitHub trending нет официального API — спарсите HTML для получения слагов репозиториев (html_attribute читает href), затем разверните каждый из них в GitHub REST API с помощью {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"}]Соединение по JSON-полю с выражением
Когда элементы массива являются объектами, ключ соединения находится внутри них — извлеките его с помощью {{{FromExp=...}}} (expr-lang поверх fRes, текущего элемента). Поиск книг → детали автора, поисковый запрос передаётся через 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"}}]Запись результатов в локальный файл
Сгенерированное поле file_storage превращает поля в записи — топ-5 криптомонет добавляются в CSV, по одной строке на элемент. Голые плейсхолдеры {{{json.path}}} читают текущий элемент; {HUMAN_INDEX} проставляет ранг с 1 (элементы обрабатываются параллельно, поэтому добавления попадают в порядке завершения — сортируйте по столбцу ранга):
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Извлечение текста из PDF
response_type: "pdf" превращает любой загруженный PDF в JSON-документ — {"text": "...", "pages": ["..."], "total_pages": N} — так что обычные JSON-пути (text, pages.0) и выражения работают с ним. Биткоин-бумага, количество страниц плюс обрезанное вступление:
{
"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}Способы сбора информации
Server — парсинг ответа от API или HTTP-запроса (использование http.Client)
Browser — эмуляция реального браузера с помощью chromium + docker + playwright/cypress и получение DOM-информации
Static — парсинг статической строки как данных
Форматы, которые можно парсить
JSON — парсинг JSON для получения конкретной информации
XML — парсинг XML-дерева для получения конкретной информации
HTML — парсинг DOM-дерева для получения конкретной информации
XPath — парсинг DOM-дерева для получения конкретной информации, но с помощью XPath
PDF — извлечение текста из PDF-документов; содержимое представляется как JSON
{"text": "...", "pages": ["..."], "total_pages": N}, так что обычные JSON-пути, такие какtextилиpages.0, работают
Использование как библиотеки
go get github.com/PxyUp/fitterpackage main
import (
"fmt"
"github.com/PxyUp/fitter/lib"
"github.com/PxyUp/fitter/pkg/config"
"log"
"net/http"
)
func main() {
res, err := lib.Parse(&config.Item{
ConnectorConfig: &config.ConnectorConfig{
ResponseType: config.Json,
Url: "https://random-data-api.com/api/appliance/random_appliance",
ServerConfig: &config.ServerConnectorConfig{
Method: http.MethodGet,
},
},
Model: &config.Model{
ObjectConfig: &config.ObjectConfig{
Fields: map[string]*config.Field{
"my_id": {
BaseField: &config.BaseField{
Type: config.Int,
Path: "id",
},
},
"generated_id": {
BaseField: &config.BaseField{
Generated: &config.GeneratedFieldConfig{
UUID: &config.UUIDGeneratedFieldConfig{},
},
},
},
"generated_array": {
ArrayConfig: &config.ArrayConfig{
RootPath: "@this|@keys",
ItemConfig: &config.ObjectConfig{
Field: &config.BaseField{
Type: config.String,
},
},
},
},
},
},
},
}, nil, nil, nil, nil)
if err != nil {
log.Fatal(err)
}
fmt.Println(res.ToJson())
}
Вывод:
{
"generated_array": ["id","uid","brand","equipment"],
"my_id": 6000,
"generated_id": "26b08b73-2f2e-444d-bcf2-dac77ac3130e"
}Используйте lib.ParseCtx(ctx, ...) для передачи context.Context: его отмена прерывает выполняющиеся запросы (HTTP-запросы, headless-браузеры, docker-контейнеры) и применяет дедлайны на всём пути. lib.Parse эквивалентен lib.ParseCtx(context.Background(), ...).
Как использовать Fitter
Скачайте последнюю версию со страницы релизов
или локально:
go run cmd/fitter/main.go --path=./examples/config_api.jsonАргументы
--path — string[""] — путь к конфигурации Fitter
--url — string[""] — URL для конфигурации Fitter
--verbose — bool[false] — включить логирование
--plugins — string[""] — путь к плагинам для Fitter
--log-level — enum["info", "error", "debug", "fatal"] — установить уровень логирования (только если verbose установлен в true)
Как использовать Fitter_CLI
Скачайте последнюю версию со страницы релизов
или локально:
go run cmd/cli/main.go --path=./examples/cli/config_cli.jsonАргументы
--path — string[""] — путь к конфигурации Fitter_CLI
--url — string[""] — URL для конфигурации Fitter_CLI
--copy — bool[false] — копировать информацию в буфер обмена
--pretty — bool[true] — сделать результат читабельным (также влияет на копирование)
--verbose — bool[false] — включить логирование
--omit-error-pretty — bool[false] — предоставить чистое значение, если pretty невалиден
--plugins — string[""] — путь к плагинам для Fitter
--log-level — enum["info", "error", "debug", "fatal"] — установить уровень логирования (только если verbose установлен в true)
--input — string[""] — указать входное значение для форматирования. Примеры:
--input=\""124"\"--input=124--input='{"test": 5}'
./fitter_cli_${VERSION} --path=./examples/cli/config_cli.json --copy=truefitter_cli auth — подключение OAuth2-аккаунта
Одноразовый интерактивный вход, который сохраняет (refresh) токен для конфигурации 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Аргументы:
--provider — пресет с известными эндпоинтами:
github|google|microsoft|gitlab|spotify--client-id / --client-secret — учётные данные OAuth2-приложения (некоторые device-потоки работают без секрета)
--token-file — куда сохранить полученный токен (права 0600); укажите тот же путь в
oauth2.token_file--flow —
auto(device, если доступен, иначе browser),device(посетить URL + ввести код) илиbrowser(localhost callback с PKCE, порт по умолчанию 8988 — зарегистрируйтеhttp://127.0.0.1:8988/callbackкак callback URL приложения)--scopes — разделённые запятыми scopes
--auth-url/--token-url/--device-auth-url/--auth-style — переопределения эндпоинтов для провайдеров без пресета
--port — int[8988] — порт callback для browser-потока (env
FITTER_AUTH_PORT); при значении по умолчанию callback URL для регистрации у провайдера —http://127.0.0.1:8988/callback--listen — адрес привязки для browser-потока, по умолчанию
127.0.0.1; установите0.0.0.0внутри контейнера, чтобы опубликованный порт достигал слушателя (envFITTER_AUTH_LISTEN)--redirect-url — callback URL, зарегистрированный у провайдера, если он отличается от адреса прослушивания, например, при пробросе портов Docker (env
FITTER_AUTH_REDIRECT_URL)--no-browser — только вывести URL авторизации
Запуск внутри Docker: см. OAuth2-аккаунты в Docker.
После входа команда выводит готовый к использованию блок конфигурации oauth2. Коннектор автоматически обновляет access-токен и записывает ротированные refresh-токены обратно в файл токена, так что вход нужен только один раз.
fitter_cli browser-login — повторное использование реальной сессии входа
Для сайтов без API/OAuth: войдите вручную один раз в реальном (headed) окне браузера — работает любая схема аутентификации, включая пароли, 2FA, SSO и капчи — и сохраните сессию для headless-скрейпинга через 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Аргументы:
--url — страница входа для открытия (обязательно)
--storage-state — куда сохранить сессию (cookies + localStorage, права 0600); укажите тот же путь в
playwright.storage_state_file(обязательно)--browser — enum["Chromium", "FireFox", "WebKit"] по умолчанию "Chromium"; используйте то же значение, что и в конфигурации скрейпинга — сайты могут привязывать сессии к отпечатку браузера
--install — bool[false] — сначала установить браузеры Playwright
--indexeddb — bool[false] — включить IndexedDB в снимок (Firebase Auth и подобное)
Повторный запуск команды сначала загружает существующее состояние, так что вы можете расширить/обновить сессию без входа с нуля. Коннектор скрейпинга также записывает обновлённые cookies после каждого запуска, поддерживая сессию активной, пока она регулярно используется. Требуется дисплей: внутри Docker запускайте эту команду на хосте и монтируйте файл — см. браузерные сессии в Docker.
Примеры:
Server-версия HackerNews + Quotes + Guardian News — использование API + HTML + XPath-парсинга
Chromium-версия Guardian News + Quotes — использование HTML-парсинга + эмуляции браузера
Docker-версия Docker-версия: Guardian News + Quotes — использование HTML-парсинга + браузера из Docker-образа
Playwright-версия Playwright-версия: Guardian News + Quotes — использование HTML-парсинга + браузера из Playwright
Playwright-версия Playwright-версия: England Cities + Weather — использование HTML + XPath-парсинга + браузера из Playwright
JSON-версия Генерация пагинации — использование статического коннектора для генерации массива пагинации
Server-версия Получить текущее время — получить время из URL и отформатировать его
Как использовать Fitter_Agent
Fitter Agent — это CLI на основе ИИ, который использует Claude для преобразования запросов на естественном языке в конфигурации Fitter и автоматического их выполнения.
Скачайте последнюю версию со страницы релизов
или локально:
export ANTHROPIC_API_KEY=<your-anthropic-api-key>
go run cmd/agent/main.goАргументы
--api-key — string[""] — ключ API Anthropic. Предпочтительнее использовать переменную окружения
ANTHROPIC_API_KEY, чтобы ключ не попал в историю вашей оболочки--model — string["claude-opus-4-8"] — модель Claude для использования
--effort — enum["low", "medium", "high", "xhigh", "max"] — уровень рассуждений, по умолчанию "high". Снизьте для более быстрых/дешёвых конфигураций, повысьте для более сложных извлечений
--verbose — bool[false] — включить логирование
--log-level — enum["info", "error", "debug", "fatal"] — установить уровень логирования
--plugins — string[""] — путь к плагинам для Fitter
--chromium-limit — uint[0] — ограничение одновременных экземпляров Chromium
--docker-limit — uint[0] — ограничение одновременных Docker-контейнеров
--playwright-limit — uint[0] — ограничение одновременных экземпляров Playwright
Как это работает
┌─────────────────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────────────────────────┘Уточнение конфигурации
Агент сохраняет диалог, поэтому после генерации конфигурации вы можете просто сказать, что изменить, вместо повторения всего запроса:
> Get top 3 HackerNews stories with titles and scores
refine> Only return 5 items and also include the article URLИспользуйте new, чтобы забыть текущую конфигурацию и начать новую сессию.
Интерактивные команды REPL
help — показать справочное сообщение
new/reset — забыть текущую конфигурацию и начать заново
clear — очистить экран
exit/quit/q — выйти из агента
Пример сессии
$ 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!Примеры запросов
Запрос | Что делает |
| Получает текущую цену BTC |
| HTML-скрейпинг с CSS-селекторами |
| Вложенные API-вызовы |
| Простое извлечение из API |
| Веб-скрейпинг |
Поддерживаемые возможности
Агент может генерировать конфигурации для:
JSON API — REST API с методами GET/POST
HTML-скрейпинг — извлечение на основе CSS-селекторов
XPath-скрейпинг — извлечение на основе XPath
Вложенные API-вызовы — получение деталей для каждого элемента списка
Эмуляция браузера — Playwright для страниц, отрисованных на JS
Форматированные поля — URL-шаблоны с плейсхолдерами
Ограничение массива — ограничение результатов до N элементов
Конфигурация
Коннектор
Это способ получения данных
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] — если установлено в true, все ошибки игнорируются
ResponseType — enum["HTML", "json", "xpath", "XML", "pdf"] — в каком формате данные приходят от коннектора
Attempts — сколько попыток использовать для получения данных коннектором
Url — определяет, какой адрес запрашивать. Важно: может содержать подстановку родительского значения в виде строки
https://api.open-meteo.com/v1/forecast?latitude={{{latitude}}}&longitude={{{longitude}}}&hourly=temperature_2m&forecast_days=1
Конфиг может быть одним из:
Пример:
{
"response_type": "xpath",
"attempts": 3,
"url": "https://openweathermap.org/find?q={PL}",
"browser_config": {
"playwright": {
"timeout": 30,
"wait": 30,
"install": false,
"browser": "Chromium"
}
}
}PluginConnectorConfig
Коннектор может быть определён через систему плагинов. Для этого нужно применить следующие флаги к Fitter/Cli (расположение плагинов):
... --plugins=./examples/plugin--plugins — ищет все файлы с расширением ".so" в указанной папке (подпапки исключены)
type PluginConnectorConfig struct {
Name string `json:"name" yaml:"name"`
Config json.RawMessage `json:"config" yaml:"config"`
}{
"name": "connector",
"config": {
"name": "Elon"
}
}Name — имя плагина
Config — json-конфиг плагина
Как собрать плагин
Сборка плагина
go build -buildmode=plugin -gcflags="all=-N -l" -o examples/plugin/connector.so examples/plugin/connector/connector.goУбедитесь, что вы экспортируете переменную Plugin, которая реализует интерфейс pl.ConnectorPlugin
Пример для CLI:
https://github.com/PxyUp/fitter/blob/master/examples/cli/config_plugin.json#L5
Пример плагина:
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
Коннектор, который позволяет получить предварительно загруженные данные из references
type ReferenceConnectorConfig struct {
Name string `yaml:"name" json:"name"`
}Пример
https://github.com/PxyUp/fitter/blob/master/examples/cli/config_ref.json#L66
Name — имя reference из карты references
IntSequenceConnectorConfig
Улучшенная версия static-коннектора, которая генерирует последовательность целых чисел в качестве результата
type IntSequenceConnectorConfig struct {
Start int `json:"start" yaml:"start"`
End int `json:"end" yaml:"end"`
Step int `json:"step" yaml:"step"`
}Start[0] — начальная точка генерации (включительно)
End[0] — конечная точка генерации (исключена из конечного результата, как range в любом языке)
Step[1] — интервал для последовательности
Пример
{
"start": 0,
"end": 2
// Generate [0, 1]
}FileConnectorConfig
Тип коннектора, который получает данные из указанного файла
type FileConnectorConfig struct {
Path string `yaml:"path" json:"path"`
UseFormatting bool `yaml:"use_formatting" json:"use_formatting"`
}Path — путь к файлу. Поддерживает форматирование
UseFormatting[false] — использовать форматирование содержимого файла или нет
StaticConnectorConfig
Тип коннектора, который получает данные из указанной строки
type StaticConnectorConfig struct {
Value string `json:"value" yaml:"value"`
Raw json.RawMessage `json:"raw" yaml:"raw"`
}Value — статическая строка в качестве данных, может быть html, json
Raw — принимает raw json. Пример. Также поддерживает форматирование
Пример:
https://github.com/PxyUp/fitter/blob/master/examples/cli/config_static_connector.json#L5
{
"value": "[1,2,3,4,5]"
}ServerConnectorConfig
Тип коннектора, который получает данные с помощью golang http.Client (серверный запрос, как 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 — поддерживаются все http-методы: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD
Headers — предопределённые заголовки для использования во время запроса может быть вставлено в ключ/значение
Timeout[sec] — таймаут по умолчанию 60 секунд или используется указанный
Body — тело запроса, разобранное значение может быть вставлено
JsonRawBody — тело запроса в json-формате; значение может быть вставлено
ErrorOnStatus — необязательно, по умолчанию
false. Когдаtrue, HTTP-статус ответа>= 400обрабатывается как ошибка получения (проходя черезattempts/null_on_error) вместо разбора тела ошибки — так вы можете отличить неудачный запрос от действительно пустого результата. Оставлениеfalseсохраняет исходное поведение разбора любого полученного тела.Proxy — настройка прокси для запроса конфиг
OAuth2 — автоматически получать/обновлять access token и отправлять его как заголовок
Authorizationконфиг
Запросы по умолчанию отправляют идентифицируемый
User-Agent(fitter (+https://github.com/PxyUp/fitter)); укажите свойUser-AgentвHeaders, чтобы переопределить его.
Пример:
{
"method": "GET",
"proxy": {
"server": "http://localhost:8080",
"username": "pyx"
}
}OAuth2 config
Автоматически получает access token перед запросом и вставляет его как заголовок Authorization (переопределяя тот, что задан через headers). Токены кэшируются в памяти и обновляются до истечения срока действия; при ответе 401 кэшированный токен отбрасывается, и запрос повторяется один раз с новым токеном.
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 конечной точки токена. Также поддерживает форматирование
GrantType — enum["client_credentials", "refresh_token"], по умолчанию "client_credentials". Используйте "refresh_token" для API, где пользователь дал согласие один раз (Google, Microsoft, ...) и у вас есть долгоживущий refresh token
ClientId/ClientSecret — учётные данные клиента. Также поддерживают форматирование, например
{{{FromEnv=CLIENT_SECRET}}}Scopes — запрашиваемые области доступа
RefreshToken — требуется для grant "refresh_token". Также поддерживает форматирование
EndpointParams — дополнительные параметры конечной точки токена (например,
audienceдля Auth0), только для grant "client_credentials"AuthStyle — enum["", "header", "params"] — как учётные данные клиента передаются на конечную точку токена: basic auth заголовок или тело запроса; пустое значение означает автоматическое определение
TokenFile — необязательный путь (поддерживает
~/) для сохранения токенов между запусками; сохранённый токен имеет приоритет над RefreshToken, и ротированные refresh-токены записываются обратно — требуется для провайдеров с одноразовыми refresh-токенами (GitHub Apps и подобные). Создайте его с помощью fitter_cli auth
Пример:
{
"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}}}"
}
}Proxy config
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 — адрес со схемой прокси-сервера. Также поддерживает форматирование
Username — имя пользователя для прокси (может быть пустым). Также поддерживает форматирование
Password — пароль для прокси (может быть пустым). Также поддерживает форматирование
{
"server": "http://localhost:8080",
"username": "pyx"
}Переменные окружения
FITTER_HTTP_WORKER — int[1000] — количество одновременных HTTP-воркеров по умолчанию
BrowserConnectorConfig
Тип коннектора, который эмулирует получение данных через браузер
type BrowserConnectorConfig struct {
Chromium *ChromiumConfig `json:"chromium" yaml:"chromium"`
Docker *DockerConfig `json:"docker" yaml:"docker"`
Playwright *PlaywrightConfig `json:"playwright" yaml:"playwright"`
}Конфиг может быть одним из:
Chromium — использовать локально установленный Chromium для получения данных
Docker — использовать docker как сервис для запуска контейнера для получения данных
Playwright — использовать фреймворк playwright для получения данных
Пример:
{
"docker": {
"wait": 10000,
"image": "docker.io/zenika/alpine-chrome:with-node",
"entry_point": "chromium-browser",
"purge": true
}
}Chromium
Использует локально установленный Chromium для получения данных
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 — путь к бинарному файлу Chromium
Timeout[sec] — таймаут выполнения chromium
Wait[msec] — таймаут загрузки страницы
Flags — флаги для Chromium по умолчанию: "--headless", "--proxy-auto-detect", "--temp-profile", "--incognito", "--disable-logging", "--disable-extensions", "--no-sandbox"
Пример:
{
"path": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
"wait": 10000
}Docker
Использует Docker для запуска контейнера для получения данных
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"`
}Образ Docker по умолчанию: docker.io/zenika/alpine-chrome
Image — образ для docker registry (указывается с хостом registry)
EntryPoint — команда, которая будет выполнена внутри контейнера
Timeout[sec] — таймаут запуска контейнера (без учёта скачивания образа)
Wait[msec] — таймаут загрузки страницы (работает только для контейнеров на основе Chromium)
Flags — аргументы команды для запуска контейнеров, по умолчанию для Chromium: "--no-sandbox","--headless", "--proxy-auto-detect", "--temp-profile", "--incognito", "--disable-logging", "--disable-gpu"
Purge — следует ли удалить контейнер после завершения работы (как docker rm)
NoPull — предотвращает скачивание образа
PullTimeout — задаёт таймаут для скачивания контейнера
Переменные окружения
DOCKER_HOST — string — (EnvOverrideHost) для установки URL docker-сервера.
DOCKER_API_VERSION — string — (EnvOverrideAPIVersion) для установки версии API, оставьте пустым для последней.
DOCKER_CERT_PATH — string — (EnvOverrideCertPath) для указания каталога, из которого загружаются TLS-сертификаты (ca.pem, cert.pem, key.pem).
DOCKER_TLS_VERIFY — bool — (EnvTLSVerify) для включения или отключения проверки TLS (по умолчанию выключено)
Пример:
{
"wait": 10000,
"image": "docker.io/zenika/alpine-chrome:with-node",
"entry_point": "chromium-browser",
"purge": true
}Playwright
Запуск браузеров через фреймворк 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"] — какой браузер использовать
Install — следует ли установить браузер (при первом использовании загружает драйвер + браузер, соответствующий встроенной версии
playwright-go; не требуется с образомghcr.io/pxyup/fitter-mcp:playwright, в котором они уже предустановлены)Timeout[sec] — таймаут запуска playwright
Wait[sec] — таймаут загрузки страницы
TypeOfWait — enum["load", "domcontentloaded", "networkidle", "commit"] — какое состояние страницы ожидаем, по умолчанию "load"
PreRunScript[""] — скрипт, который будет внедрён через AddInitScript и выполнен до запуска любых скриптов страницы (при создании документа, до завершения навигации). Полезен для патчинга окружения (переопределение navigator, заглушки API). Не имеет доступа к загруженному DOM. Также поддерживает placeholder {PL}
PostRunScript[""] — скрипт, который будет выполнен после загрузки страницы, перед чтением содержимого страницы. Полезен для взаимодействия с DOM (клики, прокрутка). Также поддерживает placeholder {PL}
Stealth[false] — добавить скрипт для попытки обхода защиты от ботов
StorageStateFile[""] — путь (поддерживает
~/) к json-файлу состояния хранилища playwright (cookies + localStorage): загружается в контекст браузера перед навигацией, записывается обратно после каждого запуска, чтобы обновлённые сессии оставались активными. Позволяет headless-запускам переиспользовать реальный вход в систему — создайте файл один раз с помощью fitter_cli browser-login. Используйте тот жеbrowserдля входа и для скрапинга: сайты могут привязывать сессии к отпечатку браузера. Также поддерживает форматированиеIndexedDB[false] — включать IndexedDB в сохраняемое состояние хранилища (некоторые SPA, например Firebase Auth, хранят токены там)
Proxy — настройка прокси для запроса конфиг
Пример
{
"timeout": 30,
"wait": 30,
"install": false,
"browser": "Chromium"
}Related MCP server: MCP Server Fetch Python
Model
С помощью model мы определяем результат скрапинга
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"`
}Конфиг может быть одним из:
ObjectConfig — конфигурация объектного формата
ArrayConfig — конфигурация формата массива
BaseField — конфигурация одиночного/генерируемого поля
IsArray — bool[false] — принудительно указывает, что поле является массивом (используется в случае model field с base field)
Пример:
{
"object_config": {}
}ObjectConfig
Конфигурация объекта и полей
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 — необязательное условие, вычисляемое относительно исходного узла до разрешения; при значении false весь объект опускается из родителя (поля вообще не разрешаются)
Конфиг может быть одним из:
Fields — карта определения каждого поля; ключ — имя поля, значение — конфигурация
Field — используется для элемента массива; поля, которые будут десериализованы как базовый тип, например "string", "int" и т.д. (используется здесь для случая массива базовых типов)
ArrayConfig — используется для элемента массива; десериализация массива массивов
Пример:
{
"fields": {
"title": {
"base_field": {
"type": "string",
"path": "type"
}
}
}
}ArrayConfig
Конфигурация массива и полей
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 — селектор для поиска корневого элемента массива или повторяющегося элемента в случае html-парсинга; размер массива будет равен количеству дочерних элементов под корнем
Reverse — bool[false] — указывает, что нужно использовать обратную итерацию (n до 1)
LengthLimit — фиксированный размер массива (только для генерируемых массивов; не для статических). Примечание: когда в источнике меньше элементов, чем лимит, массив дополняется завершающими
nullдля сохранения объявленного размера (это намеренно) — опуститеlength_limit, чтобы получить точную длину источникаCondition — необязательное условие, вычисляемое относительно исходного узла до разрешения; при значении false весь массив опускается из родителя
ItemCondition — необязательное условие, вычисляемое для каждого собранного элемента (fRes — значение элемента, fSrc — исходный элемент, fIndex — индекс элемента); элементы, дающие false, удаляются из массива — декларативная фильтрация. Не применяется к static_array
Конфиг может быть одним из:
ItemConfig — конфигурация каждого элемента массива
StaticConfig — конфигурация статического массива
Пример:
{
"root_path": "#content dt.quote > a",
"item_config": {
"field": {
"type": "string"
}
}
}Поле
Общие сведения о поле
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"`
}Конфигурация может быть одной из:
BaseField — поля, которые будут десериализованы как базовые типы, такие как "string", "int" и т.д.
ObjectConfig — если наше поле является вложенным объектом
ArrayConfig — если наше поле является массивом
FirstOf — будет выбрано первое непустое разрешённое поле
Пример:
{
"base_field": {
"type": "string",
"path": "div.current-temp span.heading"
}
}BaseField
В случае, если мы хотим получить статическую информацию или сгенерировать новую
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"] — статическое поле для парсинга. Важно: тип html работает только с коннектором, который возвращает HTML (HTMLAttribute в этом случае не действует). Пример
Path — селектор (относительный, если это дочерний элемент массива) для парсинга
HTMLAttribute — дополнительное значение, которое действует только при парсинге HTML через goquery. Здесь можно указать, какой атрибут нужно парсить.
Condition — необязательное условие, выражение, вычисляемое относительно извлечённого значения (fRes/fResJson/fResRaw, fIndex; fSrc — узел, из которого было разрешено поле, включая соседние элементы); при значении false поле опускается из родительского объекта/массива вместо генерации null. Вычисляется до Generated, поэтому ложное условие также пропускает сгенерированную работу (подзапросы, загрузки файлов)
Важно: по умолчанию тип "string" обрезается и все специальные символы заменяются; если нужна обычная строка, используйте "raw_string"
Конфигурация может быть одной из или пустой:
Generated — поле может быть сгенерировано с пользовательской конфигурацией
FirstOf — будет выбрано первое непустое разрешённое поле
Примеры
{
"generated": {
"uuid": {}
}
}{
"type": "string",
"path": "text()"
}Условные поля
Каждое поле может содержать condition — выражение на expr-lang (предопределённые значения). Если оно вычисляется во что-либо, кроме true, поле опускается из вывода (ключ/элемент исчезает), а не устанавливается в null. Некорректное выражение также опускает поле и записывает ошибку.
Где вычисляется условие:
BaseField.
condition— после извлечения:fRes— извлечённое значение,fSrc— узел, из которого было разрешено поле (включая его соседей) — такfSrc.on_sale == trueможет управлять полем на основе данных, которые вы не извлекали. Ложное условие полностью пропускает сгенерированную работу (без подзапроса, без загрузки файла)ObjectConfig.
condition/ ArrayConfig.condition— до разрешения:fRes/fSrc— исходный узел (распарсенное значение для json, текстовое содержимое для html)ArrayConfig.
item_condition— для каждого собранного элемента:fRes— элемент,fSrc— исходный элемент, из которого он был собран,fIndex— его индекс; ложные элементы отбрасываются — декларативная фильтрация массива. ИспользуйтеfSrcдля фильтрации по исходным атрибутам, не добавляя их в вывод.
Фильтрация элементов массива — fSrc.in_stock читает исходный элемент (не извлечённый в вывод), fRes.price — собранный элемент:
{
"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" } }
}
}
}
}Опустить ключ, если значение не проходит проверку:
{
"discount": {
"base_field": {
"type": "float",
"path": "discount_pct",
"condition": "fRes > 0"
}
}
}Особые случаи:
в статическом массиве опущенный элемент остаётся
null(позиции фиксированы по определению, индексы никогда не сдвигаются)если конфигурация корневой модели опущена, результат парсинга равен
nullвнутри first_of ветвь с ложным условием считается пустой, поэтому пробуется следующая ветвь
Рабочий пример: examples/config_conditions.json
GeneratedFieldConfig
Предоставляет функциональность генерации поля на лету
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"`
}Конфигурация может быть одной из:
UUID — генерирует случайный UUID V4
Static — генерирует статическое поле
Formatted — форматирует поле
Model — модель, сгенерированная из другого коннектора и модели
Plugin — поле плагина
Calculated — вычисляемое поле
File — файловое поле (для загрузки файла с сервера)
FileStorage — файловое поле, которое можно сохранить в локальный файл
Примеры:
{
"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
Генерирует случайный UUID V4 на лету, может использоваться для генерации уникального идентификатора
type UUIDGeneratedFieldConfig struct {
Regexp string `yaml:"regexp" json:"regexp"`
}Regexp — предоставляет сопоставитель, который можно использовать для получения части сгенерированного uuid
Static
Генерирует статическое поле
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"] — тип поля
Value — строковое значение поля
Raw — чистое json-значение поля
Пример
{
"type": "int",
"value": "65"
}{
"type": "array",
"value": "[65,45]"
}{
"type": "array",
"raw": [65,45]
}Конфигурация форматированного поля
Генерирует форматированное поле, которое передаёт значение от родительского базового поля
type FormattedFieldConfig struct {
Template string `yaml:"template" json:"template"`
}Template — шаблон с плейсхолдером {PL}, куда будет вставлено значение родительского поля как строка
Пример:
https://github.com/PxyUp/fitter/blob/master/examples/cli/config_cli.json#L98
{
"template": "https://news.ycombinator.com/item?id={PL}"
}Поле файлового хранилища
Поле может использоваться для сохранения результата поля в локальный файл
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 — строка шаблона для содержимого. Важно: может содержать вставку значения родительского поля как строки
Raw — чистое json-содержимое поля. Важно: может содержать вставку значения родительского поля как строки
FileName — локальное имя файла для сохранения. По умолчанию пытается получить FileName из заголовка, затем из URL. Важно: может содержать вставку значения родительского поля как строки.
Path — локальная родительская директория для сохранения файла. По умолчанию — директория процесса. Важно: может содержать вставку значения родительского поля как строки
Append[false] — добавлять к файлу или нет
{
"content": "{{{id}}}, {{{message}}}\n",
"append": true,
"file_name": "{{{id}}}.csv",
"path": "/Users/pxyup/fitter/examples/cli/test/csv"
}Файловое поле
Поле может использоваться для загрузки файла с сервера локально
type FileFieldConfig struct {
Config *ServerConnectorConfig `yaml:"config" json:"config"`
Url string `yaml:"url" json:"url"`
FileName string `json:"file_name" yaml:"file_name"`
Path string `json:"path" yaml:"path"`
}Config — ServerConfig использует стандартный http.Client fitter для отправки запроса
Url — URL изображения. Важно: URL в коннекторе может содержать вставку значения родительского поля как строки
FileName — локальное имя файла для сохранения. По умолчанию пытается получить FileName из заголовка, затем из URL. Важно: может содержать вставку значения родительского поля как строки.
Path — локальная родительская директория для сохранения файла. По умолчанию — директория процесса. Важно: может содержать вставку значения родительского поля как строки
Результатом поля будет локальный путь к файлу в виде строки
{
"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"
}
}С переданным URL (вставка значения родительского поля как строки)
{
"url": "https://picsum.photos{PL}",
"path": "/Users/pxyup/fitter/bin",
"config": {
"method": "GET"
}
}Пример конфигурации:
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
Вычисляемое поле
Поле может генерировать разные типы в зависимости от выражения
type CalculatedConfig struct {
Type FieldType `yaml:"type" json:"type"`
Expression string `yaml:"expression" json:"expression"`
}Type — результирующий тип выражения\
Expression — выражение для вычисления (мы используем эту библиотеку для вычисляемых выражений)
Предопределённые значения
FNull — псевдоним для builder.Nullvalue
FNil — псевдоним для nil
isNull(value T) — функция для проверки, является ли значение FNull
fRes — это сырой (с правильным типом) результат парсинга базового поля
fIndex — это индекс в родительском массиве (только если родитель был полем массива)
fResJson — это JSON-строковое представление сырого результата
fResRaw — результат в байтовом формате
fSrc — только в выражениях condition/item_condition: исходный узел, из которого было разрешено значение (распарсенное значение для json — включая соседей, текстовое содержимое для html). Недоступно в вычисляемых/форматированных/уведомляющих выражениях
FNewLine — разделитель новой строки
{
"type": "bool",
"expression": "fRes > 500"
}Поле плагина
Поле может быть внешним плагином для fitter
type PluginFieldConfig struct {
Name string `json:"name" yaml:"name"`
Config json.RawMessage `json:"config" yaml:"config"`
}Name — имя плагина (без расширения, просто имя)
Config — json-конфигурация плагина
Поле модели
Тип поля, который может быть сгенерирован на лету с помощью модели и коннектора
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 — какой коннектор использовать. Важно: URL в коннекторе может содержать вставку значения родительского поля как строки
Model — конфигурация внутренней модели
Type — enum["null", "boolean", "string", "int", "int64", "float", "float64", "array", "object"] — тип сгенерированного поля
Path — если мы не можем извлечь некоторую информацию из сгенерированного поля, мы можем использовать json-селектор для извлечения
Expression — строка, которая может использоваться для постобработки модели (игнорируя поле path)
Примеры:
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"
}
}
}
}Конфигурация статического массива
Предоставляет генерацию статического (фиксированной длины) массива
type StaticArrayConfig struct {
Items map[uint32]*Field `yaml:"items" json:"items"`
Length uint32 `yaml:"length" json:"length"`
}Items — map[uint32]*Field — ключ — индекс в массиве, значение — определение поля
Length — если задано (1+), может использоваться для определения пользовательской длины массива
Примеры:
{
"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"
}
}
}Список плейсхолдеров
{PL} — для вставки значения
{INDEX} — для вставки индекса в родительском массиве
{HUMAN_INDEX} — для вставки индекса в родительском массиве в человеческом виде
{{{json_path}}} — получит информацию из переданного поля "object"/"array"
{{{RefName=SomeName}}} — получить значение ссылки по имени. Пример
{{{RefName=SomeName json.path}}} — получить значение ссылки по имени и извлечь значение по json-пути. Пример
{{{FromEnv=ENV_KEY}}} — получить значение из переменной окружения
{{{FromExp=fRes + 5 + fIndex}}} — получить значение из выражения. Предопределённые значения
{{{FromInput=.}}} или {{{FromInput=json.path}}} — получить значение из входа триггера или библиотеки
{{{FromFile=./test_file.log}}} — получить значение из файла по пути. Содержимое файла также может содержать плейсхолдеры
{{{FromURL=http://localhost:8081}}} — получить ответ от URL
Примеры:
{{{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}Ссылки
Специальная карта, которая предварительно загружается (до любой обработки) и может использоваться для коннектора или для плейсхолдера
Может использоваться для:
Кэшировать jwt-токены и использовать их в заголовках
Кэшировать значения
И т.д.
Reference
type Reference struct {
*ModelField
Expire *uint32 `yaml:"expire" json:"expire"`
}ModelField — встроенная структура, вы можете использовать те же поля
Expire[sec] — длительность, после которой ссылка считается устаревшей после получения. Не задано => кэшируется навсегда. Установлено в 0 => перезагружается каждый раз. Установлено в n > 0 => кэшируется на n секунд
Для Fitter
type RefMap map[string]*Reference
type Config struct {
// Other Config Fields
Limits *Limits `yaml:"limits" json:"limits"`
References RefMap `json:"references" yaml:"references"`
}Для Fitter Cli
type RefMap map[string]*Reference
type CliItem struct {
// Other Config Fields
Limits *Limits `yaml:"limits" json:"limits"`
References RefMap `json:"references" yaml:"references"`
}References — map[string]*Reference — объект, где ключ — это ReferenceName (может использоваться для коннектора или плейсхолдера), а значение — Reference
Пример
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"
}
}
}
}
}
}
}
}Уведомители
Необязательная конфигурация для каждого элемента item.notifier_config, которая отправляет результат парсинга куда-либо после обработки. Результат по-прежнему возвращается как обычно (вывод CLI/MCP, журналы службы); уведомитель дополнительно доставляет его. Работает в Fitter (режим службы), Fitter_CLI и 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 — необязательное условие expr-lang: уведомлять только когда оно истинно. Результат парсинга доступен как
fRes(разобранное значение),fResRaw(сырые байты),fResJson(JSON-строка), напримерlen(fResRaw) > 0Force — уведомлять, даже если парсинг завершился с ошибкой
SendArrayByItem — если результат является массивом, отправлять каждый элемент как отдельное уведомление
Template — необязательный шаблон, применяемый к результату перед отправкой, плейсхолдеры разрешены
Destination — ровно один из
console,telegram_bot,http,redis,file
Конфигурации назначения:
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"`
}Назначение file использует тот же FileStorageField, что и тип поля file.
Пример (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
}
}
}
}Лимиты
Обеспечьте ограничение для предотвращения DDOS и большого использования памяти.
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 — ограничение на имя хоста, ключ — хост, значение — количество параллельных запросов (используется для серверного коннектора)
ChromiumInstance — количество параллельных экземпляров chromium
DockerContainers — количество параллельных экземпляров docker
PlaywrightInstance — количество параллельных экземпляров playwright
https://github.com/PxyUp/fitter/blob/master/examples/cli/config_cli.json#L2
{
"limits": {
"host_request_limiter": {
"hacker-news.firebaseio.com": 5
},
"chromium_instance": 3,
"docker_containers": 3,
"playwright_instance": 3
}
}Available Tools
6 toolsfitter_config_referenceA
Return a condensed reference of the Fitter config format (connectors, parsers, model/field schema, placeholders, notifiers, references, limits) with working examples. Use it before authoring a config for fitter_run.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description carries burden. Describes output but does not explicitly state that tool is read-only or has no side effects, though context implies safe operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with purpose, no fluff. Every part earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no parameters or output schema, description sufficiently covers purpose and usage. Could mention response format but not critical for a reference tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
No parameters, schema coverage is 100% trivially. Baseline 4 applies, and description adds value by listing what the reference includes.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it returns a condensed reference of the Fitter config format with working examples, and distinguishes itself from sibling run tools by advising use before authoring a config for fitter_run.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly recommends using before authoring a config for fitter_run, providing clear context. However, it does not mention exclusions or alternatives, but siblings are run tools making differentiation obvious.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fitter_inspect_urlA
Fetch a URL and return a compact structure outline plus candidate selectors/paths, so you can author a fitter config that matches on the first try instead of guessing selectors and getting nulls. For JSON it lists gjson paths with types and sample values; for HTML it lists repeated elements (candidate array_config root_path / list rows) and link/heading selectors. For client-rendered SPAs (content built by JavaScript), a plain fetch sees only an empty shell — the output warns when it detects one; pass render:true to render it in a headless browser first (mirrors what a browser_config scrape would see). Read-only helper that does NOT extract data — use it before fitter_run, then fitter_run to actually extract.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | HTTP(S) URL to fetch and inspect for its structure and candidate selectors. | |
| render | No | Render the page in a headless browser (Playwright/Chromium) before inspecting — needed for client-rendered SPAs whose content is built by JavaScript and is absent from the raw HTML. Requires browser support (the fitter-mcp:playwright image or a local Playwright install). | |
| response_type | No | Optional hint for how to read the response: json, HTML, xpath or XML. Empty auto-detects from the Content-Type/body. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It clearly states the tool is read-only and does not extract data, and explains behavior for different content types (JSON, HTML, SPAs) and the render option.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is detailed but each sentence adds unique value, covering purpose, output, parameter usage, and distinctions from execution tools. It is front-loaded with the primary purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no output schema, the description describes the output as a 'compact structure outline plus candidate selectors/paths' and gives specifics for JSON and HTML. It also covers the render behavior for SPAs, making the tool's behavior well understood.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description expands on each parameter beyond the schema: url as the target, render for SPAs, and response_type as an optional hint with auto-detection. It explains why the parameters matter and how they affect the output.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Fetch a URL and return a compact structure outline') and differentiates from sibling tools by positioning it as an inspection step before fitter_run. Clearly identifies the tool's role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use it ('before fitter_run') and what it does not do ('does NOT extract data'), plus provides guidance on when to set render:true for SPAs. Also mentions the response_type hint.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fitter_runA
Run a Fitter scraping/parsing config passed inline (JSON or YAML) and return the extracted data as JSON. Fitter fetches data via a connector (HTTP request, headless browser, static value, file, ...) and extracts structured data using json/HTML/XML/xpath selectors described by a declarative model. Call fitter_config_reference first if you are unsure about the config format.
| Name | Required | Description | Default |
|---|---|---|---|
| input | No | Optional input value (plain string or JSON), available in the config via {{{FromInput=.}}} or {{{FromInput=json.path}}} placeholders. | |
| config | Yes | Fitter CliItem config as a JSON or YAML string. Top-level keys: item (required), limits, references. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must disclose behaviors. It mentions fetching data via connectors and extracting data, implying network access. However, it omits potential side effects like rate limits, authentication needs, or error scenarios, which would strengthen transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is brief, with a clear structure: first sentence states the tool's purpose, second explains the underlying Fitter mechanism, third gives a usage tip. Every sentence contributes directly to understanding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with two parameters, no output schema, and no annotations, the description provides sufficient context: config format, supported selectors, and a reference to the config spec tool. It could be more complete by noting potential timeouts or result size limitations, but overall it covers the essential information.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% (both parameters described). The description adds context beyond the schema by explaining that config is JSON/YAML, highlighting top-level keys (item, limits, references), and stating that output is JSON. This adds meaningful value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the action ('Run a Fitter scraping/parsing config passed inline') and the resource (inline config). It distinguishes from siblings by specifying 'inline', contrasting with file- and URL-based tools. The purpose is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description advises calling fitter_config_reference first if unsure about the config format, providing clear guidance. However, it does not explicitly compare this tool to fitter_run_file or fitter_run_url, leaving the selection of the appropriate sibling somewhat implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fitter_run_fileA
Run a Fitter scraping/parsing config from a local JSON or YAML file and return the extracted data as JSON. Same as fitter_run but reads the config from disk.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Absolute path to a Fitter config file (.json, .yaml or .yml) with top-level keys: item (required), limits, references. | |
| input | No | Optional input value (plain string or JSON), available in the config via {{{FromInput=.}}} or {{{FromInput=json.path}}} placeholders. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description must fully disclose behavioral traits. It states the tool returns extracted data as JSON but does not mention whether modifications occur, required permissions, or error handling (e.g., file not found). The description is minimal and lacks transparency beyond the basic operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise with two sentences, front-loading the primary purpose. Every sentence adds value: first defines the tool, second clarifies the difference from a sibling. No fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has 2 parameters and no output schema, the description covers the basic purpose but omits important context like what happens if the file is invalid, permissions needed, or error scenarios. It is adequate for simple use but has gaps compared to a fully transparent description.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds minimal additional meaning beyond the schema; it only reiterates that 'input' is optional and used with placeholders, which the schema already covers. No further value is added for the 'path' parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states the verb (Run), the resource (Fitter config file), and distinguishes it from fitter_run by specifying 'reads the config from disk.' It also indicates the output format (JSON). This differentiates it from sibling tools like fitter_config_reference, fitter_run, and fitter_run_url.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly notes 'Same as fitter_run but reads the config from disk,' which helps users decide between this tool and fitter_run. However, it does not provide explicit when-not-to-use scenarios or mention other alternatives besides the direct sibling comparison.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fitter_run_urlA
Run a Fitter scraping/parsing config downloaded from an HTTP(S) URL (JSON or YAML) and return the extracted data as JSON. Same as fitter_run but fetches the config from a remote location, e.g. a raw GitHub link.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | HTTP(S) URL of a Fitter config (JSON or YAML) with top-level keys: item (required), limits, references. | |
| input | No | Optional input value (plain string or JSON), available in the config via {{{FromInput=.}}} or {{{FromInput=json.path}}} placeholders. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must fully disclose behavior. It states the tool downloads config from a URL and returns JSON, but omits important details such as network error handling, timeout limits, authentication, size restrictions, or what happens with invalid configs. This lack of transparency could lead to unexpected failures.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two concise sentences with no unnecessary words. It front-loads the action and result, then adds the key distinction from 'fitter_run'. Every sentence provides useful information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool involves remote fetching and parsing, but the description does not detail the return format beyond 'extracted data as JSON', nor does it explain error conditions or required permissions. With no output schema, more detail would be beneficial for an agent to anticipate the response structure.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already covers both parameters with descriptions (100% coverage). The description adds value by specifying the required top-level keys of the config ('item', 'limits', 'references'), which aids in understanding the expected structure beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool runs a Fitter config from an HTTP(S) URL and returns JSON data. It explicitly distinguishes itself from 'fitter_run' by noting the remote fetching behavior, making the purpose specific and differentiated from siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description indicates when to use this tool over 'fitter_run' (remote vs local config) and gives an example (raw GitHub link). However, it does not explicitly mention when not to use it or alternatives like 'fitter_run_file', though the context from the name and sibling list provides some guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fitter_validate_configA
Validate a Fitter config (JSON or YAML) without executing it. Checks the structural rules: item/connector_config/model presence, valid response_type, that the connector has a data source, and compiles every condition/item_condition expression in the model. Returns "valid" or the validation error. Cheap and safe — use it while iterating on a config before calling fitter_run.
| Name | Required | Description | Default |
|---|---|---|---|
| config | Yes | Fitter CliItem config as a JSON or YAML string to validate without executing it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully carries the burden, detailing what it checks (structural rules, condition compilation), that it is cheap and safe, and that it returns 'valid' or error. This comprehensively discloses behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: purpose, checks, and usage advice. Front-loaded and succinct with no redundancies.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the single parameter and no output schema, the description fully covers purpose, behavior, usage context, and return type. It is complete for effective tool selection.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description reinforces the config parameter but adds no new parameter-level details beyond the schema description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states 'Validate a Fitter config (JSON or YAML) without executing it,' clearly specifying the verb and resource. It distinguishes from sibling tools like fitter_run by advising use before calling fitter_run.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly advises using this tool while iterating on a config before calling fitter_run, providing clear when-to-use context. However, it does not explicitly state when not to use it or mention alternatives for different scenarios.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
1 tool update
v1.8.2- Added
fitter_inspect_url
1 tool update
v1.7.0- Added
fitter_validate_config
4 tool updates
v0.1.0- First observed
fitter_config_reference - First observed
fitter_run - First observed
fitter_run_file - First observed
fitter_run_url
TDQS
Each tool serves a distinct purpose: reference, inspection, execution (with three source variants), and validation. No overlap or ambiguity between them.
All tools follow the 'fitter_' prefix with snake_case, and the action part is consistently descriptive (inspect, run, validate). The naming pattern is uniform and predictable.
Six tools is ideal for a config-driven scraping/parsing workflow: reference, inspect, run (three variants), and validate. Not bloated or sparse.
The toolset covers the full lifecycle: learning the format (reference), inspecting target structure (inspect), validating configs (validate), and executing from inline, file, or URL sources. No missing functionality apparent.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
MCP server for web extraction and rendering via AceDataCloud WebExtrator
One MCP server for 180+ live web-data APIs returning clean JSON from sites that block scrapers.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceMCP server for interacting with Prometheus metrics and data.17MIT
- AlicenseAqualityCmaintenanceAn MCP server for fetching and transforming web content into various formats.48MIT
- FlicenseBqualityDmaintenanceAn MCP Server for Web scraping and Crawling, built using Crawl4AI224-
- AlicenseAqualityCmaintenanceMCP server for web scraping — extract clean markdown, links, and metadata from any URL. Free Firecrawl alternative.51575MIT
Appeared in Searches
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/PxyUp/fitter'
If you have feedback or need assistance with the MCP directory API, please join our Discord server