Skip to main content
Glama

Fitter — веб-данные для ИИ-агентов

MCP Toplist

Release License: MIT Go Reference Sponsor

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 demo — declarative config to structured JSON

Как использовать 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.modplaywright-go отказывается работать с несовместимым драйвером. Затем запускайте конфиги без "install": true.

  • Docker: используйте образ ghcr.io/pxyup/fitter-mcp:playwright, в который предустановлены Chromium, Firefox и WebKit (не нужен "install": true).

Инструменты

Tool

Description

fitter_run

Запустить конфиг Fitter, переданный инлайн (строка JSON или YAML), и вернуть извлечённые данные в виде JSON. Принимает необязательное значение input, доступное в конфиге через {{{FromInput=.}}} / {{{FromInput=json.path}}}

fitter_run_file

То же, что fitter_run, но читает конфиг из локального файла .json/.yaml

fitter_run_url

То же, что fitter_run, но загружает конфиг по HTTP(S) URL, например, по ссылке на raw GitHub

fitter_inspect_url

Получить URL и вернуть компактную структуру + возможные селекторы/пути (gjson-пути для JSON; селекторы повторяющихся элементов/строк списка для HTML), чтобы модель создавала конфиг с первой попытки, а не угадывала селекторы и получала null. Определяет клиентские SPA и может render их в headless-браузере. Только чтение — не извлекает данные

fitter_validate_config

Проверить конфиг без выполнения (структура, response_type, источник данных коннектора, модель). Полезно при итерациях над конфигом

fitter_config_reference

Вернуть сжатый справочник по всему формату конфига (коннекторы, парсеры, схема модели/полей, плейсхолдеры, уведомители, ссылки, лимиты) с рабочими примерами, чтобы модель могла создавать конфиги без внешней документации

Справочник также доступен как 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> (env FITTER_MCP_HTTP_ADDR) — адрес прослушивания; при пустом значении — режим stdio

  • FITTER_MCP_AUTH_TOKEN — если задан, каждый запрос к /mcp должен содержать Authorization: Bearer <token>; без него конечная точка не аутентифицирована, поэтому привязывайтесь к localhost или ставьте за прокси

  • --stateless (env FITTER_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-токены записываются при каждом обновлении.

Переменные окружения

  1. FITTER_PLUGINS - string[""] - путь к папке плагинов, то же, что флаг --plugins у Fitter/Fitter_CLI

  2. FITTER_MCP_HTTP_ADDR - string[""] - адрес прослушивания для удалённого режима, то же, что --http

  3. FITTER_MCP_AUTH_TOKEN - string[""] - bearer-токен, защищающий HTTP-конечную точку

  4. 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) и выражения работают с ним. Биткоин-бумага, количество страниц плюс обрезанное вступление:

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}

Способы сбора информации

  1. Server — парсинг ответа от API или HTTP-запроса (использование http.Client)

  2. Browser — эмуляция реального браузера с помощью chromium + docker + playwright/cypress и получение DOM-информации

  3. Static — парсинг статической строки как данных

Форматы, которые можно парсить

  1. JSON — парсинг JSON для получения конкретной информации

  2. XML — парсинг XML-дерева для получения конкретной информации

  3. HTML — парсинг DOM-дерева для получения конкретной информации

  4. XPath — парсинг DOM-дерева для получения конкретной информации, но с помощью XPath

  5. PDF — извлечение текста из PDF-документов; содержимое представляется как JSON {"text": "...", "pages": ["..."], "total_pages": N}, так что обычные JSON-пути, такие как text или pages.0, работают

Использование как библиотеки

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())
}

Вывод:

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

Аргументы

  1. --path — string[""] — путь к конфигурации Fitter

  2. --url — string[""] — URL для конфигурации Fitter

  3. --verbose — bool[false] — включить логирование

  4. --plugins — string[""] — путь к плагинам для Fitter

  5. --log-level — enum["info", "error", "debug", "fatal"] — установить уровень логирования (только если verbose установлен в true)

Как использовать Fitter_CLI

Скачайте последнюю версию со страницы релизов

или локально:

go run cmd/cli/main.go --path=./examples/cli/config_cli.json

Аргументы

  1. --path — string[""] — путь к конфигурации Fitter_CLI

  2. --url — string[""] — URL для конфигурации Fitter_CLI

  3. --copy — bool[false] — копировать информацию в буфер обмена

  4. --pretty — bool[true] — сделать результат читабельным (также влияет на копирование)

  5. --verbose — bool[false] — включить логирование

  6. --omit-error-pretty — bool[false] — предоставить чистое значение, если pretty невалиден

  7. --plugins — string[""] — путь к плагинам для Fitter

  8. --log-level — enum["info", "error", "debug", "fatal"] — установить уровень логирования (только если verbose установлен в true)

  9. --input — string[""] — указать входное значение для форматирования. Примеры: --input=\""124"\" --input=124 --input='{"test": 5}'

./fitter_cli_${VERSION} --path=./examples/cli/config_cli.json --copy=true

fitter_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

Аргументы:

  1. --provider — пресет с известными эндпоинтами: github|google|microsoft|gitlab|spotify

  2. --client-id / --client-secret — учётные данные OAuth2-приложения (некоторые device-потоки работают без секрета)

  3. --token-file — куда сохранить полученный токен (права 0600); укажите тот же путь в oauth2.token_file

  4. --flowauto (device, если доступен, иначе browser), device (посетить URL + ввести код) или browser (localhost callback с PKCE, порт по умолчанию 8988 — зарегистрируйте http://127.0.0.1:8988/callback как callback URL приложения)

  5. --scopes — разделённые запятыми scopes

  6. --auth-url/--token-url/--device-auth-url/--auth-style — переопределения эндпоинтов для провайдеров без пресета

  7. --port — int[8988] — порт callback для browser-потока (env FITTER_AUTH_PORT); при значении по умолчанию callback URL для регистрации у провайдера — http://127.0.0.1:8988/callback

  8. --listen — адрес привязки для browser-потока, по умолчанию 127.0.0.1; установите 0.0.0.0 внутри контейнера, чтобы опубликованный порт достигал слушателя (env FITTER_AUTH_LISTEN)

  9. --redirect-url — callback URL, зарегистрированный у провайдера, если он отличается от адреса прослушивания, например, при пробросе портов Docker (env FITTER_AUTH_REDIRECT_URL)

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

Аргументы:

  1. --url — страница входа для открытия (обязательно)

  2. --storage-state — куда сохранить сессию (cookies + localStorage, права 0600); укажите тот же путь в playwright.storage_state_file (обязательно)

  3. --browser — enum["Chromium", "FireFox", "WebKit"] по умолчанию "Chromium"; используйте то же значение, что и в конфигурации скрейпинга — сайты могут привязывать сессии к отпечатку браузера

  4. --install — bool[false] — сначала установить браузеры Playwright

  5. --indexeddb — bool[false] — включить IndexedDB в снимок (Firebase Auth и подобное)

Повторный запуск команды сначала загружает существующее состояние, так что вы можете расширить/обновить сессию без входа с нуля. Коннектор скрейпинга также записывает обновлённые cookies после каждого запуска, поддерживая сессию активной, пока она регулярно используется. Требуется дисплей: внутри Docker запускайте эту команду на хосте и монтируйте файл — см. браузерные сессии в Docker.

Примеры:

  1. Server-версия HackerNews + Quotes + Guardian News — использование API + HTML + XPath-парсинга

  2. Chromium-версия Guardian News + Quotes — использование HTML-парсинга + эмуляции браузера

  3. Docker-версия Docker-версия: Guardian News + Quotes — использование HTML-парсинга + браузера из Docker-образа

  4. Playwright-версия Playwright-версия: Guardian News + Quotes — использование HTML-парсинга + браузера из Playwright

  5. Playwright-версия Playwright-версия: England Cities + Weather — использование HTML + XPath-парсинга + браузера из Playwright

  6. JSON-версия Генерация пагинации — использование статического коннектора для генерации массива пагинации

  7. Server-версия Получить текущее время — получить время из URL и отформатировать его

Как использовать Fitter_Agent

Fitter Agent — это CLI на основе ИИ, который использует Claude для преобразования запросов на естественном языке в конфигурации Fitter и автоматического их выполнения.

Скачайте последнюю версию со страницы релизов

или локально:

export ANTHROPIC_API_KEY=<your-anthropic-api-key>
go run cmd/agent/main.go

Аргументы

  1. --api-key — string[""] — ключ API Anthropic. Предпочтительнее использовать переменную окружения ANTHROPIC_API_KEY, чтобы ключ не попал в историю вашей оболочки

  2. --model — string["claude-opus-4-8"] — модель Claude для использования

  3. --effort — enum["low", "medium", "high", "xhigh", "max"] — уровень рассуждений, по умолчанию "high". Снизьте для более быстрых/дешёвых конфигураций, повысьте для более сложных извлечений

  4. --verbose — bool[false] — включить логирование

  5. --log-level — enum["info", "error", "debug", "fatal"] — установить уровень логирования

  6. --plugins — string[""] — путь к плагинам для Fitter

  7. --chromium-limit — uint[0] — ограничение одновременных экземпляров Chromium

  8. --docker-limit — uint[0] — ограничение одновременных Docker-контейнеров

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

Примеры запросов

Запрос

Что делает

Get Bitcoin price from CoinGecko API

Получает текущую цену BTC

Scrape headlines from news.ycombinator.com with links

HTML-скрейпинг с CSS-селекторами

Get top 5 stories from HackerNews with titles

Вложенные API-вызовы

Fetch weather data from wttr.in for London

Простое извлечение из API

Scrape product names and prices from example.com

Веб-скрейпинг

Поддерживаемые возможности

Агент может генерировать конфигурации для:

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

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"
}
Переменные окружения
  1. 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 — задаёт таймаут для скачивания контейнера

Переменные окружения
  1. DOCKER_HOST — string — (EnvOverrideHost) для установки URL docker-сервера.

  2. DOCKER_API_VERSION — string — (EnvOverrideAPIVersion) для установки версии API, оставьте пустым для последней.

  3. DOCKER_CERT_PATH — string — (EnvOverrideCertPath) для указания каталога, из которого загружаются TLS-сертификаты (ca.pem, cert.pem, key.pem).

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

Результатом поля будет локальный путь к файлу в виде строки

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

Список плейсхолдеров

  1. {PL} — для вставки значения

  2. {INDEX} — для вставки индекса в родительском массиве

  3. {HUMAN_INDEX} — для вставки индекса в родительском массиве в человеческом виде

  4. {{{json_path}}} — получит информацию из переданного поля "object"/"array"

  5. {{{RefName=SomeName}}} — получить значение ссылки по имени. Пример

  6. {{{RefName=SomeName json.path}}} — получить значение ссылки по имени и извлечь значение по json-пути. Пример

  7. {{{FromEnv=ENV_KEY}}} — получить значение из переменной окружения

  8. {{{FromExp=fRes + 5 + fIndex}}} — получить значение из выражения. Предопределённые значения

  9. {{{FromInput=.}}} или {{{FromInput=json.path}}} — получить значение из входа триггера или библиотеки

  10. {{{FromFile=./test_file.log}}} — получить значение из файла по пути. Содержимое файла также может содержать плейсхолдеры

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

Ссылки

Специальная карта, которая предварительно загружается (до любой обработки) и может использоваться для коннектора или для плейсхолдера

Может использоваться для:

  1. Кэшировать jwt-токены и использовать их в заголовках

  2. Кэшировать значения

  3. И т.д.

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

Пример

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

  • Force — уведомлять, даже если парсинг завершился с ошибкой

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