Behavioural changes only, newest first. Documentation: https://mcp.iniapp.com/docs. JSON: this URL with Accept: application/json, or GET https://mcp.iniapp.com/discover.
0.28.3 — 2026-10-18
0.28.2 — 2026-10-18
cost_usd on Google results is informational.0.28.1 — 2026-10-18
0.28.0 — 2026-10-18
enums.search_categories no longer lists images and map.homepage baseline. The response shows the baseline key used.0.27.1 — 2026-10-17
search auto-routing is now by script. Cyrillic queries (or language:"bg") go to Google results (DataForSEO); Latin-script queries go to the independent Mojeek index, which answers in ~0.3s against ~3s and scored at least as well on English in the evaluation set. Each falls back to the other on failure or zero results. provider still names the engine that answered.0.27.0 — 2026-10-17
search now answers from Google results (DataForSEO) by default, which gives full Cyrillic and Bulgarian coverage; Cyrillic queries and language:"bg" search from a Bulgarian location, other Latin-script queries from a neutral one. The independent Mojeek index is the fallback on failure or zero results (it handles no non-Latin script at all). provider in the response names which one answered; cost_usd is reported for Google results. An explicit provider is honoured without fallback.0.26.0 — 2026-10-17
get_content wraps the page text in <untrusted_content source=…> with a one-line notice that it is data, not instructions; get_content, extract, search content and crawl excerpts strip invisible characters (zero-width, bidi controls, soft hyphens, BOM) and normalise to NFC; instruction-shaped phrases ("ignore previous instructions", role reassignment, chat-template tokens, requests for secrets) produce a warning — an extra text block on get_content, a _warnings field on extract, content_warnings per search result. Heuristic: a warning, never a block.search with fetch_content now reads static pages with a plain HTTP fetch and markdown conversion before falling back to the browser for JavaScript shells and refusals; content_source says which path served each result (http | browser). Same SSRF guard as image_optimize; a private address is refused and never retried in the browser.0.25.0 — 2026-10-17
search now runs on the Mojeek Search API — an independent web index, not a Google wrapper — with results cached for one hour. New arguments: site (one domain), provider (which index; auto by default), and categories=it|science now mean site filters for developer/academic sources rather than separate engines. The response's provider names the index that answered. Known limit: this index has no Bulgarian coverage; Cyrillic queries return nothing until a second provider is configured.search_providers (monthly usage and limits per provider) instead of brave_search.0.24.0 — 2026-10-17
save_state {name} stores this session's cookies and localStorage on the server; restore_state {name} loads them into any later session so a login need not be repeated. Cookies apply at once; because the session's context already exists, localStorage is written into the page when it is on that origin and otherwise applied once on the next load of each saved origin — navigate after restoring. State files hold credentials: owner-only on disk, contents never logged or returned, names listed in both responses (and in the error for an unknown name). Shared by every agent on this server.assert checks the page and returns pass/fail PER CHECK with evidence — what was actually found — and never throws on a failed check. Checks: text_visible, element_visible (CSS), ref (from snapshot), value {selector|ref, expected}, url_contains, title_contains; at least one is required and they combine.set_clock drives the page's clock with Playwright's fake timers: fixed_time pins Date while timers run, pause_at jumps to an instant and freezes time (stable captures of countdowns, carousels, toasts), resume:true lets it flow. Applies to the session page and every page loaded after it; scripts that already read the time keep what they read.set_viewport gains locale, timezone and geolocation {latitude, longitude, accuracy?}, applied live through the same CDP session as DPR and user-agent (Emulation.setLocaleOverride / setTimezoneOverride / setGeolocationOverride; Accept-Language follows the locale; the geolocation permission is granted on the context). A rejected zone or locale id leaves the previous emulation in place and names the expected format.capture_har gains save_as:"name", which stores the recording (bodies always embedded) under the baselines volume; new replay_har {name, url?, not_found: abort|fallback, url_pattern?} serves this session's requests from it via Playwright's routeFromHAR until clear_mocks — for deterministic screenshots and visual_diff runs with no backend variance. clear_mocks now also removes context-level routes, which ends a replay.trace_start {name?, screenshots, snapshots} / trace_stop {include_zip?} record a Playwright trace of the session (actions, DOM snapshots, screenshots, console, network) and write the zip under the baselines volume (traces/), returning its path, size and duration; include_zip embeds it under 6MB. Opens in Playwright's trace viewer. One trace per session; it is lost if the session's browser reconnects.0.23.0 — 2026-10-17
browser container, official Playwright image). Each session opens an isolated context in milliseconds; the pool holds up to 12 sessions (BROWSER_MAX_SESSIONS) and a caller past that waits up to 90s before failing with a clear message. Lighthouse runs in the same container against the same Chromium.proxy and stealth arguments on the one-shot tools and on configure_session, which now only takes project.0.22.0 — 2026-10-17
help — {tool:"name"} returns that tool's parameters, example, common failures and the way out, what usually comes next, and its changelog; {query:"…"} searches tools, facts, prompts, REST endpoints and the changelog for a goal; {} is the index plus the rules worth knowing. Instant and deterministic; use it before guessing. Queries that find nothing are recorded so gaps surface.0.21.0 — 2026-10-17
feedback {tool?, outcome, note, wanted?} records what this server got wrong or lacks, for the maintainer to read. The first recovery hint a session sees names it.behaviour: what agents did after an error (retried, switched, abandoned, recovered within 2 calls), whether hints were followed, and ref vs selector targeting per client; plus feedback counts and recent notes, and archives.0.19.1 — 2026-10-17
0.19.0 — 2026-10-16
snapshot returns the current page as an accessibility tree, one line per element with role, name and a ref ([ref=e12]). interactive:true keeps only buttons, links, inputs and other controls; query:"…" keeps nodes whose line contains the text plus their subtree; selector scopes it; max_lines caps it. Far cheaper than a screenshot for finding elements.ref from the snapshot instead of selector. One of the two is required where a selector was required before. A ref that is no longer on the page fails immediately with 'not in the current snapshot' rather than timing out after 10s.0.18.2 — 2026-10-16
screenshot with a selector that matches several elements now captures the first match and adds a text block with the match count, instead of failing with a strict-mode error. click, scroll and drag already took the first match.navigate to localhost / 127.0.0.1 explains that the browser runs on the server and asks for a LAN IP or public hostname; a navigate timeout suggests wait_until:"domcontentloaded"; evaluate on a session with no page open says so (about:blank) and says to navigate first; an evaluate whose script navigated away, or dereferenced a selector that matched nothing, is told which tool to use instead. One hint per cause per session, as before.navigate description now states that localhost in a URL is the server's own container, never the caller's machine.navigate issued right after one that failed at the network level could fail with 'interrupted by another navigation to chrome-error://' — Chrome committing its own error page under the new request. navigate now retries once on that interruption.0.18.1 — 2026-09-23
0.18.0 — 2026-09-21
audit type=accessibility now works on pages with a strict Content-Security-Policy. axe-core is bundled and injected as source through the inspector instead of loaded as a <script src> from cdnjs, which the target's script-src could (and did) block.GET /metrics gains sessions — every session classified as ran_tools, listed_only (connected, listed the tools, called none) or connected_only, broken down by client. Codex initializes every configured server on every run, so its listed_only count is start-up, not agents that looked and left; judge discoverability per client.HTTP 400 (no session id and not an initialize), HTTP 404 (unknown or expired session) — and rpc_errors groups them by cause then agent. Before, ~80 HTTP 400s a week carried neither who nor why.0.17.0 — 2026-09-21
image_optimize turns an image into a web-ready rendition through the internal INI Opt service: resize, WebP/AVIF/JPEG/PNG, quality or a byte target, EXIF stripped. Source is EXACTLY ONE of url (a public image), media:{provider,id} (a media_search photo — its full-size original is fetched) or screenshot:{full_page?,selector?} (the current session page). Gated on OPT_API_KEY; unset, the tool is neither registered nor advertised.download.url on THIS host, behind the same API/MCP token (a ready curl line is included). preview:true additionally embeds the output when it is under 3MB.result (format, dimensions, bytes in/out), saved_percent, target_met and kept_original — a completed job can still be larger than the source or miss its target, and whether to use it is the caller's decision. note says so when it happens. With no preset the format and dimensions are kept and only quality changes; pass preset:"product-photo" (WebP ≤1600px) for a hero or product image.wait_ms (default 90s) is returned with a poll URL rather than failed — the upload already happened and there is no idempotency key upstream, so never resubmit it.POST /api/image-optimize (url or media source), GET /api/image/:id (status), GET /api/image/:id/download (the file; 409 not ready, 410 expired pass through). Server-side fetches refuse private addresses on every redirect hop, and SVG/GIF/HTML are rejected before upload.0.16.0 — 2026-09-04
audit type=design extracts a site's design language and brand as data. The other audits answer "is this page correct?"; this one answers "what does it look like?" so an agent can match it instead of eyeballing screenshots: palette ranked by rendered AREA (not occurrence) with primary / secondary / background / text / link / border picked out and neutrals separated from accents; fonts actually in use with their share of the page, @font-face origins (google-fonts, adobe, self-hosted...), body/paragraph/h1-h4 styles and the size + weight scales; border-radius, box-shadow and spacing scales with the base unit; content max-width; button variants (solid/outline/ghost) with samples; logo (img src or inline svg), favicons, og:image, theme-color; token-like :root custom properties; the CSS/UI/JS/site-builder stack with evidence; and dark-mode support. tokens is a flat design-tokens map (color.primary, font.body, radius.button...) for direct reuse.dark_mode.dark_palette; the media emulation is reset afterwards. A class- or toggle-switched theme cannot be triggered from an isolated load, so the result names the toggle selector: click it on the session page and re-run with no url.design acts on the CURRENT session page when url is omitted, so it can read a logged-in app, a mobile viewport, or a state reached by interaction. REST: POST /api/audit {url, type:"design"}.0.15.0 — 2026-09-02
icon_search, icon_get and icon_credits serve 337,809 icons from 237 open-source sets, hosted on this box. No API key, no rate limit. They sit next to media_search rather than inside it: stock photos are picked one at a time by eye, icons are picked as a family, and their licences differ per set.icon_search takes an abstract intent PLUS concrete terms, because the index matches icon names and not meaning — {intent:"billing history"} alone matches nothing, while terms:["invoice","receipt"] matches plenty. It fans the terms out in parallel and merges, which is also the only way to express OR against an index that ANDs the words in one query.icon_search also reports which SETS cover every term, so you can pass collections:["lucide"] and keep one family across a surface. 64 sets require attribution and 2 forbid commercial use; those are flagged, not hidden.POST /api/icon-search and POST /api/icon-credits. All of it is gated on ICONS_URL and is neither registered nor advertised when unset.icon_search reports terms_with_no_matches: coverage is measured only against terms that matched something, since the abstract intent usually matches nothing by itself. url/svg_url fields appear only when this server has a public icons host configured; otherwise use the inline svg. Right after a deploy the icons backend may answer "unreachable" for ~100s while it loads its dataset — retry, do not fall back.0.14.3 — 2026-09-02
mock_route {clear:true} (removed in 0.10.0, use clear_mocks), and that audit, check_links, extract, visual_diff and capture_har always need a url and run in an isolated page (all act on the current session page when url is omitted, since 0.13.0 to 0.14.0). They now also mention drag, clear_mocks, get_network format:'har', crawl use_session, the configure_session project label and discover. If you learned the toolset from a prompt before today, re-invoke browser_toolkit.0.14.2 — 2026-09-02
0.14.1 — 2026-08-28
get_network {format:"har"} returns a HAR 1.2 waterfall for what the page has ALREADY loaded — no reload, so SPA state, scroll position and login survive. Real transfer sizes and timings included; response bodies are not captured. Use it when reloading would destroy the state you are debugging; use capture_har when you need a full recording of a fresh load.0.14.0 — 2026-08-28
capture_har with no url reloads the page you are on, carrying your cookies and emulated viewport — so a network waterfall for a page behind a login is possible for the first time. crawl takes use_session:true for the same reason. Both stay anonymous unless you ask.0.13.1 — 2026-08-28
audit and check_links join extract and visual_diff in accepting an optional url: omit it and they act on the page this session already has open, so you can audit a logged-in view or a mobile-emulated layout rather than only a clean anonymous load. A current-page audit says so in note — response-header checks are unavailable because the page was not loaded by that call, so security headers read null rather than absent. lighthouse still needs a url and now says why.0.13.0 — 2026-08-28
extract and visual_diff now work on the page you are already on. url is optional: omit it and they act on the current session page, keeping login, emulated viewport, scroll position and mocks. Previously both REQUIRED a url and ran in a throwaway context, so neither could be used on a page you had navigated to and set up — which is why hand-written evaluate was the only way to read it. Passing url still behaves exactly as before.0.12.1 — 2026-08-28
serp_rank needs SERP_API_KEY and threw on every call without it, yet still appeared in tools/list — an option that could only fail, costing you attention and context. It is hidden while unconfigured and reappears the moment a key is set. tools/list and GET /discover agree.0.12.0 — 2026-08-28
search is the entry point when you have no URL, and its description said otherwise — it still claimed self-hosted SearXNG over Google/DDG, which stopped being true in 0.11.0 and named engines that do not work from this server at all. It now says what it is: ranked results with the top pages' content already fetched, so one call often answers a question without navigating.0.11.1 — 2026-08-28
provider switch to "searxng" and results differ slightly; that is the cap working, not an outage.0.11.0 — 2026-08-28
provider field ("brave" or "searxng"). Nothing changes in how you call search or POST /api/search; results are more reliable because we are no longer scraping engines that rate-limit and CAPTCHA this server's datacenter IP.categories: it | science | news | …) still go to the self-hosted SearXNG, which is configured with developer engines Brave has no equivalent for (github, stackoverflow, mdn, pypi, npm, arxiv). SearXNG is also the automatic fallback if Brave is unavailable for any reason, so search degrades instead of failing.0.10.1 — 2026-08-28
0.10.0 — 2026-08-28
mock_route: url_pattern is now REQUIRED and clear:true is gone — clearing moved to the new clear_mocks tool (no arguments). Every real call to mock_route in the first week failed, all of them passing no arguments, because an all-optional schema made that look valid. Both tools now state exactly what they need, so the mistake is caught by validation instead of at runtime.0.9.5 — 2026-08-28
MCP_AUTH_TOKENS=laptop:<token>,ci:<token>. Nothing changes for you as a caller — send the bare token as before, or the whole label:token line, both authenticate. Bare tokens keep working unchanged.0.9.4 — 2026-08-28
scroll, wait_for and set_viewport now say how to call them. Each takes a set of individually-optional arguments of which you must supply one, so a call with none looked schema-valid and failed at runtime. Their descriptions now state the required choice and the errors carry runnable examples — same fix as mock_route in 0.9.1.0.9.3 — 2026-08-28
configure_session now takes a project label — call it first with e.g. {project:"3mo.bg"} so your session's usage is attributed to the work it belongs to. Without a label, usage is attributed to the hostname you navigate to. For local targets the port is now kept (127.0.0.1:3051, not 127.0.0.1), because otherwise every dev server on one machine collapses into a single indistinguishable bucket.0.9.2 — 2026-08-28
screenshot now captures at the viewport you set. Previously set_viewport reported success, the PNG came back 1280×720 anyway, and the page was left reset to 1280×720 / DPR 1 with the user-agent and touch flags still emulated — so mobile and responsive captures were unobtainable, and anything measured after a screenshot returned desktop numbers while still looking like a mobile session. Applies to all three forms (default, full_page, selector) and to visual_diff. If you were re-issuing set_viewport after every screenshot as a workaround, you can stop. navigate and pdf were never affected.POST /api/screenshot takes viewport options: preset (same device list as set_viewport) or viewport: {width, height, dpr, mobile, touch, user_agent}, plus the existing full_page. Previously the REST surface could only ever produce 1280-wide captures.0.9.1 — 2026-08-28
mock_route now tells you how to call it. It failed on 100% of attempts in the first week because url_pattern is optional in the schema (clear:true omits it), so a call with no arguments looked valid. Pass either {url_pattern:"**/api/cart/**", ...} to add a mock, or {clear:true} alone to remove all of them. The error now includes a runnable example.GET /metrics gained by_client, by_caller and by_host breakdowns. Calls are tagged with your client name/version, a fingerprint of the token you presented, and the target hostname — taken from the url argument and then held for the rest of the session, so screenshots and evaluates after a navigate are attributed to the same domain. Hostname only: paths and query strings are never recorded.0.9.0 — 2026-08-23
TIMEOUT was 360s and caps TOTAL session duration, not idle time — 29% of all sessions (514 over 27 days) were reaped mid-work, after which the caller silently resumed on a blank page. Raised to 30 minutes; idle sessions are still reclaimed after 5 minutes as before. If you saw a page mysteriously reset partway through a long task, that was this.GET /metrics (REST token, ?days=N): per-tool call counts, error rates, latency and response sizes, broken down by day, UTC hour and surface. Every MCP tool call and REST request is now logged; before this the server kept no usage record at all.0.8.0 — 2026-07-30
drag: drags one element onto another with a real mouse — press, move in steps, release. Use it for kanban boards, sortable lists and any HTML5 drop target.evaluate are ignored entirely. Before this, a drop could not be exercised from a script at all — clicking worked, dragging silently did nothing, and a broken board looked identical to a working one.hold_ms covers libraries with a press delay before a drag begins.0.7.1 — 2026-07-26