Turn any website or API into structured JSON with LLM-authored declarative scraping configs.
io.github.PxyUp/fitter (MCP Server)
This MCP server, io.github.PxyUp/fitter, turns any website or API into structured JSON. It uses LLM-authored declarative scraping configs to define how data is collected and output in a machine-readable format.
๐ ๏ธ Key Features
Converts website or API content into structured JSON
Fitter turns any website or API into structured JSON โ declaratively. One JSON/YAML config describes where the data lives (HTTP request, headless browser, file, static value) and what to extract (JSON paths, CSS selectors, XPath). No code, no brittle scraping scripts.
๐ Try it in your browser โ the real engine compiled to WebAssembly: live examples, a visual config builder, no install.
Because configs are plain data, LLMs can author them. The built-in MCP server lets Claude Code, Claude Desktop, or any MCP client write and run scraping pipelines on your machine, on demand:
"Get the top 5 HackerNews stories with titles and scores" โ the model authors a fitter config, validates it, runs it locally, and gets clean JSON back.
One engine, five ways to use it:
๐ค Fitter MCP
MCP server exposing fitter to Claude Code, Claude Desktop and any MCP client
๐ง Fitter Agent
AI-powered CLI: natural language โ config โ executed result
๐ฅ Fitter CLI
run configs locally for test/debug/home usage
๐ฆ Fitter Lib
embed the engine in your own Go program
โ๏ธ Fitter
long-running service mode with scheduling & notifications
Why fitter for AI agents?
Declarative & auditable โ the agent produces a config you can read, save and re-run, not throwaway code
Local-first โ all fetching happens on your machine; no third-party scraping API, no keys, no per-request billing
Batteries included โ HTTP client, headless browser (Playwright/Chromium/Docker), JSON/HTML/XML/XPath/PDF parsing, pagination, cached references, host rate-limits โ in a single static binary
Reusable โ what the agent authored today becomes tomorrow's cron job or service config
fitter demo โ declarative config to structured JSON
How to use Fitter_MCP
Fitter MCP is a Model Context Protocol server (stdio transport) which lets any MCP client โ Claude Code, Claude Desktop, IDE assistants, custom agents โ run Fitter configs and get structured JSON back.
Quick start (Claude Desktop โ one click)
Download fitter-mcp-<os>-<arch>.mcpb from the release page and open it โ Claude Desktop installs the server automatically.
Quick start (Claude Code)
bash
# 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"
Then just ask:
Get the top 5 HackerNews stories with titles and scores using fitter
The model calls fitter_config_reference, authors a config, optionally checks it with fitter_validate_config and executes it via fitter_run โ all data fetching happens locally on your machine. For a ready-made pipeline try examples/config_morning_briefing.json:
Run examples/config_morning_briefing.json with fitter and give me the briefing
The .mcpb bundle and native binary ship without browsers: HTTP, static and file connectors work out of the box, but browser configs (the playwright connector) need Playwright's browsers. A few ways to get them:
On first use (native binary / .mcpb): set "install": true in the playwright connector โ fitter downloads the driver + browser matching its built-in playwright-go version on first use (one-time, cached), so no separate install step is needed.
Ahead of time (native, optional): to avoid the first-run download, install the browsers beforehand with the same playwright-go version fitter is built against (check go.mod, currently v0.6100.0):
bash
go run github.com/mxschmitt/playwright-go/cmd/playwright@v0.6100.0 install
# Linux: append --with-deps to also install the required OS libraries
The version must match go.mod exactly โ playwright-go refuses to run against a mismatched driver. Then run configs without "install": true.
Docker: use the ghcr.io/pxyup/fitter-mcp:playwright image, which bundles Chromium, Firefox and WebKit preinstalled (no "install": true needed).
Tools
Tool
Description
fitter_run
Run a Fitter config passed inline (JSON or YAML string) and return the extracted data as JSON. Accepts an optional input value available in the config via {{{FromInput=.}}} / {{{FromInput=json.path}}}
fitter_run_file
Same as fitter_run but reads the config from a local .json/.yaml file
fitter_run_url
Same as fitter_run but downloads the config from an HTTP(S) URL, e.g. a raw GitHub link
fitter_inspect_url
Fetch a URL and return a compact structure outline + candidate selectors/paths (gjson paths for JSON; repeated-element/list-row selectors for HTML) so the model authors a config first-try instead of guessing selectors and getting nulls. Detects client-rendered SPAs and can render them in a headless browser. Read-only โ does not extract
fitter_validate_config
Validate a config without executing it (structure, response_type, connector data source, model). Useful while iterating on a config
fitter_config_reference
Return a condensed reference of the whole config format (connectors, parsers, model/field schema, placeholders, notifiers, references, limits) with working examples, so the model can author configs without external docs
The reference is also exposed as MCP resource fitter://config-reference for clients that support resources.
The config format is exactly the same as for Fitter_CLI: a top-level object with item (required), limits and references. Notifiers work too (the result is additionally pushed to http/telegram/redis/file/console); trigger_config and http_server are service-mode only and are ignored in MCP calls.
Remote / hosted mode (streamable HTTP)
By default fitter_mcp talks stdio. Pass --http to serve the streamable HTTP transport instead โ for a shared team server, a container, or any remote deployment:
bash
# 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"
FITTER_MCP_AUTH_TOKEN โ when set, every /mcp request must send Authorization: Bearer <token>; without it the endpoint is unauthenticated, so bind to localhost or put it behind a proxy
--stateless (env FITTER_MCP_STATELESS=true) โ no per-session state, so replicas can sit behind a load balancer without sticky sessions
--metrics-addr <addr> (env FITTER_MCP_METRICS_ADDR) โ serve Prometheus metrics on a separate address (e.g. 127.0.0.1:9091); works in stdio mode too; empty disables. Exposes MCP request counts by method, tool call durations by tool and outcome, and outbound HTTP request durations by host, method and status. The endpoint is unauthenticated โ bind it to localhost or a private interface
The server shuts down gracefully on SIGINT/SIGTERM.
Docker
A slim multi-arch image (linux/amd64 + linux/arm64) ships with every release:
bash
# 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
The slim image contains only the fitter binary and CA certificates: server/static/file connectors work, browser connectors (chromium/docker/playwright) do not.
For browser-based configs use the playwright variant, which bundles Playwright with Chromium, Firefox and WebKit (matched to the playwright-go version fitter is built against, so no "install": true is needed in configs):
It is built from Dockerfile.mcp-playwright; build with --build-arg PLAYWRIGHT_BROWSERS=chromium for a smaller Chromium-only image.
OAuth2 accounts in Docker
Both images ship fitter_cli, so the one-time OAuth2 login can run inside the container. Store the token on a volume mounted at /tokens (pre-created writable in the image) and share it with the MCP server:
bash
# 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
Note: 8988 is only for the one-time browser-flow login; the MCP server itself needs no port in stdio mode and only 8080 in hosted HTTP mode.
Logged-in browser sessions in Docker
Browser sessions need the playwright image (the slim one has no browsers). The one-time headed login needs a display, so run it on the host, then bind-mount the session dir into the container (the image pre-creates a writable /sessions):
bash
# 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
Use a bind mount (not a named volume): the container writes refreshed cookies back after every run, so the host copy stays current and can be re-extended with browser-login at any time.
The volume must stay writable for the server: rotated refresh tokens are written back on every refresh.
Environment variables
FITTER_PLUGINS - string[""] - path for plugins folder, same as the --plugins flag of Fitter/Fitter_CLI
FITTER_MCP_HTTP_ADDR - string[""] - listen address for remote mode, same as --http
FITTER_MCP_AUTH_TOKEN - string[""] - bearer token protecting the HTTP endpoint
FITTER_MCP_STATELESS - bool[false] - stateless HTTP transport, same as --stateless
FITTER_MCP_METRICS_ADDR - string[""] - Prometheus metrics listen address, same as --metrics-addr
Recipes
Complete, tested configs showing the main patterns. All of them run unchanged via Fitter_MCP (fitter_run_file), Fitter_CLI or the library โ more in examples/.
Scrape a page that has no API, enrich from one that does
GitHub trending has no official API โ scrape the HTML for repo slugs (html_attribute reads the href), then fan each one out into the GitHub REST API with {PL}:
When array items are objects, the join key lives inside them โ pull it out with {{{FromExp=...}}} (expr-lang over fRes, the current item). Book search โ author details, search query supplied via input:
[{"title":"Dune","year":1965,"author":{"name":"Frank Herbert","born":"8 October 1920","died":"11 February 1986"}}]
Write results to a local file
The file_storage generated field turns fields into writes โ top-5 crypto coins appended to a CSV, one row per item. Bare {{{json.path}}} placeholders read the current item; {HUMAN_INDEX} stamps the 1-based rank (items are processed in parallel, so appends land in completion order โ sort by the rank column):
response_type: "pdf" turns any fetched PDF into a JSON document โ {"text": "...", "pages": ["..."], "total_pages": N} โ so regular JSON paths (text, pages.0) and expressions work on it. The Bitcoin whitepaper, page count plus a trimmed intro:
{"intro":"Bitcoin: A Peer-to-Peer Electronic Cash SystemSatoshi Nakamotosatoshin@gmx.comwww.bitcoin.orgAbstrac...","total_pages":9}
Way to collect information
Server - parsing response from some API's or http request(usage of http.Client)
Browser - emulate real browser using chromium + docker + playwright/cypress and get DOM information
Static - parsing static string as data
Format which can be parsed
JSON - parsing JSON to get specific information
XML - parsing xml tree to get specific information
HTML - parsing dom tree to get specific information
XPath - parsing dom tree to get specific information but by xpath
PDF - extracting text from PDF documents; the content is exposed as JSON {"text": "...", "pages": ["..."], "total_pages": N} so regular JSON paths like text or pages.0 work
Use lib.ParseCtx(ctx, ...) to pass a context.Context: cancelling it aborts in-flight fetches (HTTP requests, headless browsers, docker containers) and applies deadlines end-to-end. lib.Parse is equivalent to lib.ParseCtx(context.Background(), ...).
# 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
Arguments:
--provider - preset with known endpoints: github|google|microsoft|gitlab|spotify
--client-id / --client-secret - OAuth2 app credentials (some device flows work without secret)
--token-file - where to store the received token (0600 permissions); reference the same path in oauth2.token_file
--flow - auto (device if available, else browser), device (visit a url + enter a code) or browser (localhost callback with PKCE, default port 8988 โ register http://127.0.0.1:8988/callback as the app callback url)
--scopes - comma separated scopes
--auth-url/--token-url/--device-auth-url/--auth-style - endpoint overrides for providers without preset
--port - int[8988] - browser flow callback port (env FITTER_AUTH_PORT); with the default the callback url to register at the provider is http://127.0.0.1:8988/callback
--listen - browser flow bind address, default 127.0.0.1; set 0.0.0.0 inside a container so the published port reaches the listener (env FITTER_AUTH_LISTEN)
--redirect-url - callback url registered at the provider when it differs from the listen address, e.g. docker port mapping (env FITTER_AUTH_REDIRECT_URL)
After login the command prints the ready-to-use oauth2 config block. The connector refreshes the access token automatically and writes rotated refresh tokens back to the token file, so the login is needed only once.
fitter_cli browser-login โ reuse a real login session
For sites without an API/OAuth: log in once by hand in a real (headed) browser window โ any auth scheme works, including passwords, 2FA, SSO and captchas โ and save the session for headless scraping via storage_state_file:
bash
./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
Arguments:
--url - login page to open (required)
--storage-state - where to save the session (cookies + localStorage, 0600 permissions); reference the same path in playwright.storage_state_file (required)
--browser - enum["Chromium", "FireFox", "WebKit"] default "Chromium"; use the same value as the scraping config โ sites may bind sessions to the browser fingerprint
--install - bool[false] - install playwright browsers first
--indexeddb - bool[false] - include IndexedDB in the snapshot (Firebase Auth and similar)
Re-running the command loads the existing state first, so you can extend/refresh a session without logging in from scratch. The scraping connector also writes refreshed cookies back after every run, keeping the session alive as long as it is used regularly. Needs a display: inside Docker run this command on the host and mount the file โ see browser sessions in Docker.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 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 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Refining a config
The agent keeps the conversation, so after a config is generated you can just
say what to change instead of restating the whole request:
code
> Get top 3 HackerNews stories with titles and scores
refine> Only return 5 items and also include the article URL
Use new to forget the current config and start a fresh session.
Interactive REPL Commands
help - show help message
new/reset - forget the current config and start fresh
NullOnError[false] - if set to true then all errors a ignored
ResponseType - enum["HTML", "json", "xpath", "XML", "pdf"] - in which format data comes from the connector
Attempts - how many attempts to use for fetch data by connector
Url - define which address to request. Important: can be with inject of the parent value as a stringhttps://api.open-meteo.com/v1/forecast?latitude={{{latitude}}}&longitude={{{longitude}}}&hourly=temperature_2m&forecast_days=1
JsonRawBody - body of the request in json format; value can be injected
ErrorOnStatus - optional, default false. When true, an HTTP response status >= 400 is treated as a fetch error (flowing through attempts / null_on_error) instead of parsing the error body โ so you can distinguish a failed fetch from a genuinely empty result. Leaving it false keeps the original behavior of parsing whatever body came back.
Automatically obtains an access token before the request and injects it as the Authorization header (overriding one set via headers). Tokens are cached in memory and refreshed before expiry; on a 401 response the cached token is dropped and the request is retried once with a fresh one.
TokenUrl - token endpoint URL. Also support formatting
GrantType - enum["client_credentials", "refresh_token"], default is "client_credentials". Use "refresh_token" for APIs where the user consented once (Google, Microsoft, ...) and you hold a long-lived refresh token
ClientId/ClientSecret - client credentials. Also support formatting, e.g. {{{FromEnv=CLIENT_SECRET}}}
Scopes - requested scopes
RefreshToken - required for the "refresh_token" grant. Also support formatting
EndpointParams - extra token endpoint parameters (e.g. audience for Auth0), "client_credentials" grant only
AuthStyle - enum["", "header", "params"] - how client credentials are passed to the token endpoint: basic auth header or request body; empty means auto detect
TokenFile - optional path (supports ~/) for persisting tokens between runs; the stored token is preferred over RefreshToken and rotated refresh tokens are written back โ required for providers with single-use refresh tokens (GitHub Apps and similar). Create it with fitter_cli auth
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 - address with schema of proxy server. Also support formatting
Username - username for proxy(can be empty). Also support formatting
Password - password for proxy(can be empty). Also support formatting
Browser - enum["Chromium", "FireFox", "WebKit"] - which browser to use
Install - should we install browser (downloads the driver + browser matching the built-in playwright-go version on first use; not needed with the ghcr.io/pxyup/fitter-mcp:playwright image, which ships them preinstalled)
Timeout[sec] - timeout to run playwright
Wait[sec] - timeout of page loading
TypeOfWait - enum["load", "domcontentloaded", "networkidle", "commit"] which state of page we waiting, default is "load"
PreRunScript[""] - script which will be injected via AddInitScript and executed before any page script runs (on document creation, before navigation completes). Useful for patching the environment (navigator overrides, API stubs). Cannot access the loaded DOM. Also support placeholder {PL}
PostRunScript[""] - script which will be executed after page load, before reading content of the page. Useful for DOM interaction (clicks, scrolling). Also support placeholder {PL}
Stealth[false] - add script for trying passing bot defends
StorageStateFile[""] - path (supports ~/) to a playwright storage state json (cookies + localStorage): loaded into the browser context before navigation, written back after every run so refreshed sessions stay alive. Lets headless runs reuse a real login โ create the file once with fitter_cli browser-login. Use the same browser for login and scraping: sites may bind sessions to the browser fingerprint. Also support formatting
IndexedDB[false] - include IndexedDB in the persisted storage state (some SPAs, e.g. Firebase Auth, keep tokens there)
BaseField - configuration of single/generated field
IsArray - bool[false] - force indicate that field is array(usable in case of model field with base field)
Example:
json
{"object_config":{}}
ObjectConfig
Configuration of the object and fields
go
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 - optional condition expression evaluated against the source node before resolution; when false the whole object is omitted from the parent (fields are not resolved at all)
Config can be one of:
Fields - map of each field definition; key - field name, value - configuration
Field - used for element of array; fields which will be deserialized like basic type like "string", "int" and etc (used here for case array of basic types)
ArrayConfig - used for element of array; deserialization array of array
RootPath - selector for find root element of the array or repeated element in case of html parsing, size of array will be amount of children element under the root
Reverse - bool[false] - indicate that need use reverse iteration(n to 1)
LengthLimit - fixed size of the array (generated arrays only; not for static). Note: when the source has fewer elements than the limit, the array is padded with trailing nulls to preserve the declared size (this is intentional) โ omit length_limit to get exactly the source length instead
Condition - optional condition expression evaluated against the source node before resolution; when false the whole array is omitted from the parent
ItemCondition - optional condition expression evaluated against every built item (fRes - item value, fSrc - source element, fIndex - item index); items resolving to false are dropped from the array - declarative filtering. Not applied to static_array
Config can be one of:
ItemConfig - configuration of each element of the array
FieldType - enum["null", "boolean", "string", "int", "int64", "float", "float64", "array", "object", "html", "raw_string"] - static field for parse. Important: type html will only works from connector which return HTML (HTMLAttribute - have no effect in this case). Example
Path - selector(relative in case it is array child) for parsing
HTMLAttribute - extra value which have effect only in HTML parsing via goquery. Here you can specify which attribute need to be parsed.
Condition - optional condition expression evaluated against the extracted value (fRes/fResJson/fResRaw, fIndex; fSrc - the node the field was resolved from, siblings included); when false the field is omitted from the parent object/array instead of producing null. Evaluated before Generated, so a false condition also skips generated work (sub-requests, file downloads)
Important: by default "string" type trimmed and all special chars is replaced, if you need plain string use "raw_string"
Config can be one of or empty:
Generated - field can be generated one which custom configuration
FirstOf - first not empty resolved field will be selected
Examples
json
{"generated":{"uuid":{}}}
json
{"type":"string","path":"text()"}
Conditional fields
Every field can carry a condition - an expr-lang expression (predefined values). When it evaluates to anything except true the field is omitted from the output (the key/item disappears), not set to null. An invalid expression also omits the field and logs an error.
Where the condition is evaluated:
BaseField.condition - after extraction: fRes is the extracted value, fSrc the node the field was resolved from (its siblings included) - so fSrc.on_sale == true can gate a field on data you did not extract. A false condition skips generated work entirely (no sub-request, no file download)
ObjectConfig.condition / ArrayConfig.condition - before resolution: fRes/fSrc are the source node (parsed value for json, text content for html)
ArrayConfig.item_condition - against every built item: fRes is the item, fSrc the source element it was built from, fIndex its index; false items are dropped - declarative array filtering. Use fSrc to filter on source attributes without adding them to the output
Filter array items - fSrc.in_stock reads the source element (not extracted into the output), fRes.price the built item:
Generate random UUID V4 on the flight, can be used for generate uniq id
go
type UUIDGeneratedFieldConfig struct {
Regexp string`yaml:"regexp" json:"regexp"`
}
Regexp - provide matcher which can be used for get part of generated uuid
Static
Generate static field
go
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"] - type of the field
Value - string value of the field
Raw - pure json value of the field
Example
json
{"type":"int","value":"65"}
json
{"type":"array","value":"[65,45]"}
json
{"type":"array","raw":[65,45]}
Formatted Field Config
Generate formatted field which will pass value from parent base field
go
type FormattedFieldConfig struct {
Template string`yaml:"template" json:"template"`
}
Template - template in with placeholder {PL} where parent value will be injected like string
FileName - local file name for storing file. By default, it is try get FileName from header, after that from url. Important: can be with inject of the parent value as a string.
Path - local file parent directory for storing file. Default path it is process directory. Important: can be with inject of the parent value as a string
FileName - local file name for storing file. By default, it is try get FileName from header, after that from url. Important: can be with inject of the parent value as a string.
Path - local file parent directory for storing file. Default path it is process directory. Important: can be with inject of the parent value as a string
Result of the field will be local file path as string
Field can generate different types depends from expression
go
type CalculatedConfig struct {
Type FieldType `yaml:"type" json:"type"`
Expression string`yaml:"expression" json:"expression"`
}
Type - resulting type of expression\
Expression - expression for calculation (we use this lib for calculated expression)
Predefined values
FNull - alias for builder.Nullvalue
FNil - alias for nil
isNull(value T) - function for check is value is FNull
fRes - it is raw(with proper type) result from the parsing base field
fIndex - it is index in parent array(only if parent was array field)
fResJson - it is JSON string representation of the raw result
fResRaw - result in bytes format
fSrc - only in condition/item_condition expressions: the source node the value was resolved from (parsed value for json - siblings included, text content for html). Not available in calculated/formatted/notifier expressions
type PluginFieldConfig struct {
Name string`json:"name" yaml:"name"`
Config json.RawMessage `json:"config" yaml:"config"`
}
Name - name of the plugin(without extension just name)
Config - json config of the plugin
Model Field
Field type which can be generated on the flight by news model and connector
go
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"`
}
Current time is: {PL} with token from TokenRef={{{RefName=TokenRef}}} and TokenObjectRef={{{RefName=TokenObjectRef token}}}
text
Current time is: {PL} with token from TokenRef={{{RefName=TokenRef}}} and TokenObjectRef={{{RefName=TokenObjectRef token}}}
text
TokenRef={{{RefName=TokenRef}}} and TokenObjectRef={{{RefName=TokenObjectRef token}}} Object={{{value}}} {PL} Env={{{FromEnv=TEST_VAL}}} {INDEX} {HUMAN_INDEX}
References
Special map which prefetched(before any processing) and can be user for connector or for placeholder
Can be used for:
Cache jwt token and use them in headers
Cache values
Etc
Reference
go
type Reference struct {
*ModelField
Expire *uint32`yaml:"expire" json:"expire"`
}
ModelField - is embedded struct, you can use same fields
Expire[sec] - duration when reference is expired after fetching. Not set => forever cached. Set to 0 => every time re-fetch. Set to n > 0 => cached for n second
{"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"}}}}}}}}
Optional per-item config item.notifier_config which pushes the parse result somewhere after processing. The result is still returned as usual (CLI/MCP output, service logs); the notifier additionally delivers it. Works in Fitter (service mode), Fitter_CLI and Fitter_MCP.
Expression - optional expr-lang condition: notify only when it evaluates to true. The parse result is available as fRes (parsed value), fResRaw (raw bytes), fResJson (JSON string), e.g. len(fResRaw) > 0
Force - notify even if parsing finished with an error
SendArrayByItem - if the result is an array, send each element as a separate notification
Template - optional template applied to the result before sending, placeholders allowed
Destination - exactly one of console, telegram_bot, http, redis, file