Featured

Deploy OpenClaw in 60 seconds β€” 20% off logoDeploy OpenClaw in 60 seconds β€” 20% off

Launch OpenClaw on Hostinger in about 60 seconds and keep your agent live 24/7. Our referral link gives you 20% off, no coupon code needed.

Launch on Hostinger β†’
Run your Hermes agent on Hostinger, fully managed logoRun your Hermes agent on Hostinger, fully managed

Launch Hermes on Hostinger in one click, fully managed, no VPS knowledge needed. Use code ZACAARON10 for 10% off.

Launch on Hostinger β†’
Crawl and scrape any site into clean data, 10% off logoCrawl and scrape any site into clean data, 10% off

Firecrawl crawls and scrapes any site into clean markdown for your agent. Get 1,000 free credits, and new users get 10% off their first purchase.

Try Firecrawl free β†’
Your own AI agent, running 24/7 with QwikClaw logoYour own AI agent, running 24/7 with QwikClaw

QwikClaw sets up and runs an always-on OpenClaw agent for you. One click, no config files, no server setup.

Deploy now β†’
One API to scrape, enrich, and extract the internet. logoOne API to scrape, enrich, and extract the internet.

Context.dev gives your agents a single API to scrape, enrich, and extract live web data β€” no proxies, no parsers, no maintenance.

Start building free β†’
SetupClaw: done-for-you OpenClaw for founders & exec teams logoSetupClaw: done-for-you OpenClaw for founders & exec teams

White-glove OpenClaw for founders and exec teams (4–50+ employees): we install, harden, integrate your tools, and maintain it β€” secured from day one.

Get it set up for you β†’
SEO data APIs for your agent, $1 free credit logoSEO data APIs for your agent, $1 free credit

DataForSEO gives your agent live access to SERP results, keyword data, backlinks, and on-page SEO data through one API. New accounts get a $1 credit, good for up to 20,000 keyword or backlink lookups.

Try DataForSEO free β†’
Reach 47,000+ AI builders

A flat monthly placement in front of developers actively installing AI tools. No lock-in, cancel anytime.

Advertise here β†’

Works with

Claude CodeClaude DesktopCursorVS CodeClineCodex CLIOpenClaw+ any MCP client

Install to Claude Code

claude mcp add liquid -- uvx liquid-mcp

Summary

ertad-family/liquid MCP server](https://glama.ai/mcp/servers/ertad-family/liquid/badges/score.svg)](https://glama.ai/mcp/servers/ertad-family/liquid) 🐍 ☁️ 🏠 - Connect your agent to any HTTP API on the fly β€” discovers + maps any REST API once, then...

Connect from your MCP client

One-click install

Add this server to your editor with a single click. Fill in any required credentials afterward.

Claude Code

Run this once and Claude Code registers the server for you:

claude mcp add liquid -- uvx liquid-mcp

Claude Desktop

Add this to claude_desktop_config.json under Settings β†’ Developer β†’ Edit Config:

{
  "mcpServers": {
    "liquid": {
      "command": "uvx",
      "args": [
        "liquid-mcp"
      ],
      "env": {
        "OPENAI_API_KEY": "<OPENAI_API_KEY>",
        "OPENAI_BASE_URL": "<OPENAI_BASE_URL>",
        "GEMINI_API_KEY": "<GEMINI_API_KEY>",
        "ANTHROPIC_API_KEY": "<ANTHROPIC_API_KEY>"
      }
    }
  }
}

Cursor

Add this to .cursor/mcp.json in your project (or ~/.cursor/mcp.json for all projects):

{
  "mcpServers": {
    "liquid": {
      "command": "uvx",
      "args": [
        "liquid-mcp"
      ],
      "env": {
        "OPENAI_API_KEY": "<OPENAI_API_KEY>",
        "OPENAI_BASE_URL": "<OPENAI_BASE_URL>",
        "GEMINI_API_KEY": "<GEMINI_API_KEY>",
        "ANTHROPIC_API_KEY": "<ANTHROPIC_API_KEY>"
      }
    }
  }
}

Cline and other MCP clients

Most MCP clients accept the standard mcpServers JSON block:

{
  "mcpServers": {
    "liquid": {
      "command": "uvx",
      "args": [
        "liquid-mcp"
      ],
      "env": {
        "OPENAI_API_KEY": "<OPENAI_API_KEY>",
        "OPENAI_BASE_URL": "<OPENAI_BASE_URL>",
        "GEMINI_API_KEY": "<GEMINI_API_KEY>",
        "ANTHROPIC_API_KEY": "<ANTHROPIC_API_KEY>"
      }
    }
  }
}

Codex CLI

Register the server with OpenAI's Codex CLI β€” run this once, or add the equivalent block to ~/.codex/config.toml:

codex mcp add liquid --env OPENAI_API_KEY=<OPENAI_API_KEY> --env OPENAI_BASE_URL=<OPENAI_BASE_URL> --env GEMINI_API_KEY=<GEMINI_API_KEY> --env ANTHROPIC_API_KEY=<ANTHROPIC_API_KEY> -- uvx liquid-mcp

# or add to ~/.codex/config.toml:
[mcp_servers.liquid]
command = "uvx"
args = ["liquid-mcp"]
[mcp_servers.liquid.env]
OPENAI_API_KEY = "<OPENAI_API_KEY>"
OPENAI_BASE_URL = "<OPENAI_BASE_URL>"
GEMINI_API_KEY = "<GEMINI_API_KEY>"
ANTHROPIC_API_KEY = "<ANTHROPIC_API_KEY>"

OpenClaw

OpenClaw reads MCP servers from the mcp.servers section of ~/.openclaw/openclaw.json (managed via `openclaw mcp add` or the mcporter skill):

{
  "mcp": {
    "servers": {
      "liquid": {
        "command": "uvx",
        "args": [
          "liquid-mcp"
        ],
        "env": {
          "OPENAI_API_KEY": "<OPENAI_API_KEY>",
          "OPENAI_BASE_URL": "<OPENAI_BASE_URL>",
          "GEMINI_API_KEY": "<GEMINI_API_KEY>",
          "ANTHROPIC_API_KEY": "<ANTHROPIC_API_KEY>"
        }
      }
    }
  }
}

Replace the <PLACEHOLDER> values with your own credentials β€” see the configuration table below.

Configuration

liquid reads the following environment variables:

VariableRequired
OPENAI_API_KEYOptional
OPENAI_BASE_URLOptional
GEMINI_API_KEYOptional
ANTHROPIC_API_KEYOptional

README.md

Liquid

Connect your AI agent to anything β€” with no connector to write or maintain.

Point Liquid at a URL or a database and it works out the interface for you: discovers its shape, maps it to the fields you asked for, and handles auth, pagination and normalization β€” typed records, no client code. When the upstream drifts, it re-maps and keeps going. The same small API β€” fetch Β· query Β· write Β· sense β€” reaches web APIs, databases, other agents (MCP/A2A), email, and even IoT and industrial systems (MQTT, Modbus, OPC UA, BACnet). An LLM does the learning at setup (and on drift); the data path itself makes no model call.

![PyPI](https://pypi.org/project/liquid-api/) ![License](https://github.com/ertad-family/liquid/blob/main/LICENSE) ![Python](https://www.python.org/)

---

What an agent can reach through Liquid

One agent-facing API (fetch Β· query Β· write Β· sense) over everything an agent might need to touch β€” Liquid figures out how to talk to it so the agent doesn't have to. It's the agent's senses and hands: fetch/query probe, sense perceives a live event stream, write acts on the world.

  • Web APIs & messaging β€” REST/JSON, GraphQL, SOAP/WSDL, gRPC, WebSocket,

SSE/NDJSON streams, MQTT (IoT pub/sub β€” subscribe to sense, publish to act)

  • Email β€” IMAP/SMTP (any provider, app-password or OAuth2 XOAUTH2) and the

Gmail API (OAuth2): read a mailbox, sense new mail as it arrives, and send

  • Industrial / OT β€” Modbus (PLCs, sensors) and OPC UA (Industry-4.0 nodes,

native subscriptions) for the factory floor; BACnet for buildings (HVAC/BMS) β€” read, write, and sense

  • Android devices β€” phones, TV boxes, kiosks via ADB: sense logcat, read

shell, act with input/am

  • Other agents & tools β€” any MCP server, A2A agents, ChatGPT-plugin manifests
  • Databases β€” Postgres (+ pgvector), MySQL/MariaDB, SQLite, DuckDB, SQL Server,

Neo4j (graph), MongoDB (documents), Redis (key-value)

  • People, places & things β€” a human, a home, or a car as a node via

connectors: Telegram (perceive messages, send replies), Home Assistant (perceive a whole smart home's state changes, act via call_service β€” lights, locks, media), and Smartcar (perceive a connected vehicle across ~30 brands β€” location/battery/fuel β€” and act: lock/unlock, charge)

Point it at a https://… endpoint, a postgres://… / mongodb://… / redis://… DSN, a grpc://… target, or another MCP server β€” discovery identifies the interface, learns its shape, and hands your agent typed records. The same fetch/query/write works regardless of what's underneath. No per-service connector to hand-write; the integration maintains itself when the upstream changes.

# A web API it has never seen β€” no spec, no connector, no auth
adapter = await liquid.get_or_create(
    "https://api.openbrewerydb.org/v1/breweries",
    target_model={"name": "str", "city": "str", "country": "str"},
    auto_approve=True,
)
breweries = await liquid.fetch(adapter)            # typed records

# A database is just another interface β€” same API, and it writes too
db = await liquid.get_or_create("postgresql://reader@host/shop",
                                target_model={"id": "int", "email": "str"},
                                auto_approve=True)
orders = await liquid.fetch(db, "/public/orders")
await liquid.write(db, "/public/orders", op="insert",
                   values={"email": "a@b.com", "total_cents": 9900},
                   allow_write=True)               # opt-in; mutates the store

You hand-write no connector and no schema: an LLM learns the interface once at setup (databases introspect themselves and skip even that), and the integration repairs itself when the upstream drifts. The runtime is plain deterministic transport β€” predictable cost, reproducible behavior, nothing to babysit.

Built for the constraints real agents hit

Reaching everything is half of it. The other half is that agents pay for every token, get confused by inconsistent shapes, and can't parse error prose. Liquid answers each with a concrete primitive β€” all shipped, all on PyPI.

Context-budget control

# Search / aggregate server-side instead of fetch-then-filter β€” 10-100x fewer tokens
orders = await liquid.search(adapter, "/orders",
    where={"total_cents": {"$gt": 10000}, "status": "paid"}, limit=20)

stats = await liquid.aggregate(adapter, "/orders",
    group_by="status", agg={"total_cents": "sum", "id": "count"})

hits = await liquid.text_search(adapter, "/tickets", "shipping delay")  # BM25-lite

data = await liquid.fetch(adapter, "/orders", max_tokens=2000)      # budget cap
data = await liquid.fetch(adapter, "/customers", verbosity="terse") # id + 1-2 fields

Cross-source normalization

liquid = Liquid(..., normalize_output=True)
# Stripe {amount:1000,currency:"usd"} Β· PayPal {value:"10.00",currency_code:"USD"}
#   β†’ Money(amount_cents=1000, currency="USD", amount_decimal=Decimal("10.00"))

Timestamps (Unix / ISO 8601 / RFC 2822) collapse to UTC datetime; pagination envelopes ({data:[…]} / {results:[…]} / Link headers) flatten; ID fields normalize across id / _id / uuid / *_id.

Canonical intents β€” one mental model across services

await liquid.execute_intent(adapter, "charge_customer",
    {"customer_id": "cus_xyz", "amount_cents": 9999, "currency": "USD"})
# Same intent on Stripe / Braintree / Square / Adyen β€” 71 canonical intents

Structured recovery β€” agents self-heal without parsing text

try:
    await liquid.fetch(adapter, "/orders")
except LiquidError as e:
    if e.recovery and e.recovery.next_action:
        await agent.call_tool(e.recovery.next_action.tool, e.recovery.next_action.args)

Every error carries a Recovery with next_action: ToolCall, retry_safe, and retry_after_seconds. 401 β†’ store_credentials. 404/410 β†’ repair_adapter. 429 β†’ retry after the given delay. And when the upstream's schema drifts, adapters self-heal (repair_adapter) β€” the agent keeps working.

Predictable cost β€” know before you call

est = await liquid.estimate_fetch(adapter, "/orders")
# FetchEstimate(expected_items=250, expected_tokens=52_000, confidence="high", …)
if est.expected_tokens < my_budget:
    data = await liquid.fetch(adapter, "/orders")

Tools emitted by to_tools() carry a metadata block (cost_credits, typical_latency_ms, cached, idempotent, side_effects, related_tools) so the agent can reason about which tool to pick β€” and ambient tools (liquid_check_quota, liquid_list_adapters, …) let it ask about state instead of memorizing it.

---

Measured impact

Deterministic benchmarks on realistic agent tasks (500-order, 200-ticket fixtures, mocked HTTP) β€” reproducible via python -m benchmarks.run:

| Task | Metric | Baseline | With Liquid | Delta | |---|---|---:|---:|---:| | Find 10 orders over $100 | tokens | 75,482 | 1,519 | βˆ’98% | | Revenue by status (aggregate) | tokens | 75,482 | 115 | βˆ’100% | | Fetch customer (id+email only) | tokens | 424 | 12 | βˆ’97% | | Recover from 401 | structured next_action | no | yes | β€” | | Find the shipping ticket | tokens | 14,588 | 154 | βˆ’99% | | Stripe↔PayPal consistency | field overlap | 0.11 | 1.00 | +9Γ— | | Skip wasted call via estimate | tokens | 14,943 | 0 | βˆ’100% | | max_tokens=2000 budget cap | tokens | 14,943 | 1,999 | βˆ’87% |

Full methodology + per-task breakdown: benchmarks/RESULTS.md.

Install

pip install liquid-api                 # core + bundled MCP server (the `liquid-mcp` command)
pip install 'liquid-api[discovery]'    # + an LLM for discovering spec-less REST APIs & field mapping

Do you need an LLM extra? Self-describing interfaces β€” OpenAPI, GraphQL, gRPC, MCP, A2A, WSDL β€” and all databases (introspection) discover with no LLM, and the whole runtime (fetch/query/write/sense) never calls a model. You only need an LLM backend to discover a REST API that has no machine-readable spec (heuristic + LLM) and to map its fields. [discovery] pulls LiteLLM, which reaches OpenAI / Gemini / Anthropic / local / 100+ providers; or pick one directly:

pip install 'liquid-api[gemini]'     # Google Gemini   (or [anthropic]; OpenAI/local work with no extra via base_url)
pip install 'liquid-api[grpc]'       # gRPC transport (reflection)
pip install 'liquid-api[ws]'         # WebSocket transport
pip install 'liquid-api[pg]'         # Postgres / pgvector (asyncpg)
pip install 'liquid-api[mysql]'      # MySQL / MariaDB (aiomysql); SQLite needs no extra
pip install 'liquid-api[neo4j]'      # Neo4j graph (Bolt / Cypher)
pip install 'liquid-api[duckdb]'     # DuckDB (embedded analytics)
pip install 'liquid-api[mssql]'      # SQL Server (ODBC; needs a system ODBC driver)
pip install 'liquid-api[mongodb]'    # MongoDB (collections as endpoints)
pip install 'liquid-api[redis]'      # Redis (keyspace namespaces as endpoints)
pip install 'liquid-api[mqtt]'       # MQTT (IoT pub/sub)
pip install 'liquid-api[modbus]'     # Modbus (industrial registers)
pip install 'liquid-api[opcua]'      # OPC UA (Industry-4.0 nodes + subscriptions)
pip install 'liquid-api[bacnet]'     # BACnet (building automation; ADB needs the system `adb` binary)
# Framework integration (LangChain / OpenAI / Anthropic / MCP) is built in β€” no extra package.

The core is dependency-free β€” every backend's library is an optional extra, imported only when used.

See it work β€” live, no pre-config

Point Liquid at an API it has never seen (no adapter, no OpenAPI spec, no auth) and get typed records back β€” you write no connector; discovery + mapping is the only place a model runs. Runnable end to end via examples/live_quickstart.py:

Connecting to an API Liquid has never seen:
  https://api.openbrewerydb.org/v1/breweries

  discovery method : rest_heuristic
  mapped fields    : ['name', 'city', 'state', 'country']
  LLM calls so far : 2  (discovery + mapping)

fetch() -> 50 typed records; first 3:
   {'name': '(405) Brewing Co', 'city': 'Norman', 'state': 'Oklahoma', 'country': 'United States'}
   {'name': '(512) Brewing Co', 'city': 'Austin', 'state': 'Texas', 'country': 'United States'}
   {'name': '1 of Us Brewing Company', 'city': 'Mount Pleasant', 'state': 'Wisconsin', 'country': 'United States'}

  LLM calls during fetch : 0
  LLM calls on 2nd fetch : 0

You wrote no connector, no schema, no auth glue β€” Liquid learned the interface for you, and will re-learn it if it changes. That's the point: integrations you don't build or babysit.

Run as an MCP server (open source, self-hosted)

Expose the engine to any MCP client (Claude Desktop, Cursor, Claude Code) β€” it runs in your own process, no cloud, no account, no lock-in:

![Add to Cursor](cursor://anysphere.cursor-deeplink/mcp/install?name=liquid&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyJsaXF1aWQtbWNwIl19)

One-click in Cursor (the button writes the server into your mcp.json; add your OPENAI_API_KEY in Cursor's MCP settings afterward). Or set it up manually:

pip install liquid-api
export OPENAI_API_KEY=sk-...        # or GEMINI_API_KEY / ANTHROPIC_API_KEY,
                                    # or OPENAI_BASE_URL=http://localhost:11434/v1 for local (Ollama/vLLM)
liquid-mcp                          # or: python -m liquid.mcp_server

Zero-install with uvx (the liquid-mcp package makes the command run by name) β€” Claude Code:

claude mcp add liquid --scope user -e OPENAI_API_KEY=sk-... -- uvx liquid-mcp

Claude Desktop / any MCP client:

{ "mcpServers": { "liquid": {
  "command": "uvx",
  "args": ["liquid-mcp"],
  "env": { "OPENAI_API_KEY": "sk-..." }
} } }

(Or after pip install liquid-api, drop uvx and use "command": "liquid-mcp" directly.)

One-click in Claude Desktop: install the .mcpb bundle β€” it prompts for your model key on install (stored in the OS keychain), with no JSON to edit. Requires uv on the machine.

<!-- mcp-name: io.github.ertad-family/liquid -->

Tools: liquid_connect (discover + map any interface), liquid_fetch, liquid_query (server-side search/aggregate), liquid_estimate (pre-flight cost/size, no call), liquid_list_adapters, liquid_discover. The surface is read-only by default; start the server with LIQUID_ALLOW_WRITES=1 to also expose liquid_execute (database insert/update/delete). Adapters and credentials persist under ~/.liquid. Backed by any LLM β€” OpenAI, Gemini, Anthropic, any OpenAI-compatible/local endpoint via base_url, 100+ providers via LiteLLM, or your own function through CallableBackend.

Quick start β€” LangGraph agent

from liquid import Liquid, InMemoryCache, RateLimiter
from liquid._defaults import InMemoryVault, InMemoryAdapterRegistry, CollectorSink
from liquid_langchain import LiquidToolkit
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI

liquid = Liquid(
    llm=my_llm, vault=InMemoryVault(), sink=CollectorSink(),
    registry=InMemoryAdapterRegistry(), cache=InMemoryCache(), rate_limiter=RateLimiter(),
    normalize_output=True,    # cross-source canonical shapes
    include_meta=True,        # _meta block on every response
)

adapter = await liquid.get_or_create(
    "https://api.shopify.com",
    target_model={"id": "str", "total_cents": "int", "customer_email": "str"},
    credentials={"access_token": "shpat_..."},
    auto_approve=True,
)

tools = LiquidToolkit(adapter, liquid).get_tools()
agent = create_react_agent(ChatOpenAI(model="gpt-4o-mini"), tools)
result = await agent.ainvoke(
    {"messages": [("user", "Find 5 recent orders over $100 from VIP customers")]}
)

The agent's tools come with rich descriptions (WHEN to use, NOT FOR what, return shape, cost), structured recovery on every error, and server-side search so it never pulls 500 orders to find 5.

Every interface, one API

Discovery identifies the target and tags each endpoint with a protocol; a pluggable transport driver runs it β€” but the agent-facing API (fetch, query, write, mapping, recovery, cache, rate limits) is identical across all of them.

| Interface | Runtime | Write | Install | |---|---|---|---| | REST / HTTP+JSON | βœ… | βœ… actions (POST/PUT/PATCH/DELETE) | β€” | | GraphQL | βœ… query + Relay pagination | βœ… mutations | β€” | | SOAP / WSDL | βœ… stdlib XML | β€” | β€” | | gRPC | βœ… unary + server-streaming (reflection) | β€” | liquid-api[grpc] | | WebSocket | βœ… bounded batch reads + subscribe + live sense | β€” | liquid-api[ws] | | SSE / NDJSON (HTTP server-push) | βœ… bounded batch reads + live sense | β€” | β€” | | MCP (agent) | βœ… call tools / read resources + notification sense | βœ… tool calls | β€” | | A2A (agent) | βœ… JSON-RPC message/send to AgentCard skills | β€” | β€” | | Postgres (+pgvector) | βœ… tables/views, filters, pagination, vector search | βœ… | liquid-api[pg] | | MySQL / MariaDB | βœ… tables/views, filters, pagination | βœ… | liquid-api[mysql] | | SQLite | βœ… tables/views, filters, pagination | βœ… | β€” (stdlib) | | DuckDB | βœ… tables/views, filters, pagination | βœ… | liquid-api[duckdb] | | SQL Server | βœ… tables/views, OFFSET/FETCH pagination | βœ… | liquid-api[mssql] | | Neo4j (graph) | βœ… labels/relationship types, property filters | βœ… node CRUD | liquid-api[neo4j] | | MongoDB (document) | βœ… collections, field filters, pagination | βœ… | liquid-api[mongodb] | | Redis (key-value) | βœ… keyspace namespaces, typed values, SCAN paging | βœ… SET/HSET/DEL | liquid-api[redis] | | MQTT (IoT pub/sub) | βœ… subscribe β†’ batch + live sense | βœ… publish | liquid-api[mqtt] | | Modbus (industrial) | βœ… register/coil read + delta-poll sense | βœ… register/coil write | liquid-api[modbus] | | OPC UA (industrial) | βœ… node read + native-subscription sense | βœ… node write | liquid-api[opcua] | | BACnet (buildings) | βœ… object property read + delta-poll sense | βœ… property write | liquid-api[bacnet] | | ADB (Android) | βœ… shell read + logcat sense | βœ… shell actions (input/am) | β€” (system adb) | | Email β€” IMAP/SMTP | βœ… read mailbox by UID + new-mail sense | βœ… send (MIME) | β€” (stdlib) | | Email β€” Gmail API | βœ… list/get + history sense | βœ… messages.send | β€” (OAuth2) |

Read and write. liquid.write(adapter, endpoint, op="insert", values={...}, allow_write=True) mutates any database (SQL INSERT/UPDATE/DELETE, Mongo insert/update/delete, Redis SET/HSET/DEL, Neo4j node CRUD); web/agent writes go through verified actions. Identifiers come from introspection and values are parameterized; update/delete require a where (no blanket mutations); writes are off until you opt in with allow_write=True.

Sense β€” the afferent organ. liquid.sense(adapter, endpoint) perceives a live event stream wherever one exists: SQL row deltas (and Postgres LISTEN/NOTIFY), Redis pub/sub, WebSocket frames, HTTP server-push (SSE/NDJSON), and MCP notifications β€” each yielded as a modality-agnostic event. Pointed inward, liquid.sense_webhook(port=…, verifier=…) hosts an inbound endpoint so a service (or a human, via a webhook) POSTing to the agent becomes a perceivable signal too. All bounded by max_events / max_seconds, so an agent can drain-by-pull.

The sensorimotor loop. react(stream, handler) drives a handler for each perceived event β€” with error isolation and bounded concurrency β€” so a host can perceive β†’ wake the agent β†’ act. merge_senses(*streams) fans several senses into one loop, so one agent can watch a database, a queue, and a webhook at once:

events = merge_senses(
    await liquid.sense(orders, "/orders"),
    await liquid.sense_webhook(port=8088, verifier=stripe_verifier),
)
await react(events, on_event, max_concurrency=4)

Discovery is automatic β€” and identifies on the fly. Before the pipeline runs, a fingerprint step names the target: a bare host:port is normalized by well-known port (db:5432 β†’ postgresql://db:5432), and liquid.identify(url) answers "what is this, and is its driver installed?" with an install hint when a backend is missing. (Identifying a protocol is feasible on the fly; speaking a new authenticated binary protocol isn't β€” so unknowns are named, not guessed at.)

| Discovery | Where it looks | Cost | |---|---|---| | Databases | catalog introspection (postgres://, mysql://, mongodb://, redis://, neo4j://, …) | Low | | gRPC / WebSocket / SSE | server reflection / frame sampling / content-type sniff | Low | | MCP / A2A / Plugin | /mcp, /.well-known/agent-card.json, /.well-known/ai-plugin.json | Low | | OpenAPI / GraphQL / SOAP | spec, introspection, or WSDL | Low | | REST heuristic | common paths + LLM interpretation | Medium | | Browser | Playwright capturing network | High |

Add a backend without writing code. For the SQL family the contract is declarative enough to be data: a dialect manifest (quoting, placeholder style, pagination, introspection SQL, error map, DBAPI2 module) registered via register_sql_manifest({...}) installs a working driver + discovery β€” so a new SQL / wire-compatible store (CockroachDB, ClickHouse, any DBAPI2 driver), even one fetched from the network as JSON, connects without a release. New protocols otherwise plug in via the liquid.transport.ProtocolDriver protocol; SQL backends share a dialect-aware core, so a new one is a ~80-line adapter.

Want to teach Liquid a new protocol? A complete transport driver (fetch/write/sense) is typically ~150 lines β€” see docs/ADDING_A_DRIVER.md for the walkthrough and a wishlist (CAN bus, CoAP, KNX, AMQP, NATS, SNMP, …). Contributions welcome.

2,500+ APIs are pre-discovered and pre-mapped in the global catalog β€” most popular services connect with zero discovery cost.

Architecture

URL / DSN                       Agent
   ↓                              ↑
 FINGERPRINT β†’ DISCOVERY        FETCH Β· QUERY Β· WRITE Β· SEARCH Β· AGGREGATE
   ↓                              ↑
 one ProtocolDriver per          Deterministic per-protocol transport
 interface:                        β€’ Query DSL (server-side filter)
   REST GraphQL gRPC WS SSE MQTT   β€’ Output normalization
   MCP A2A Β· SQL graph doc KV Β·    β€’ Verbosity / max_tokens / _meta
   Modbus OPC-UA BACnet ADB …      β€’ (full protocol list in the table above)
   ↓                              β€’ Structured recovery + self-heal
 APISchema                        β€’ Rate-limit-aware token bucket
   ↓                              β€’ Response cache (Cache-Control aware)
 AI MAPPING (setup only)          β€’ Empirical probing data (Cloud)
   ↓
 AdapterConfig

AI participates at setup only. Runtime is pure transport with transforms β€” no LLM per call, predictable cost, reproducible behavior (except search_nl, which caches its compilations).

Swappable components

Every cross-cutting concern is a Protocol you can replace:

from liquid.protocols import (
    Vault, LLMBackend, DataSink, KnowledgeStore, AdapterRegistry, CacheStore,
)

In-memory implementations ship for all of them; liquid-cloud provides PostgresVault, RedisCache, etc. for hosted deployments.

Framework support

adapter.to_tools(format="anthropic")   # Claude tool use
adapter.to_tools(format="openai")      # OpenAI function calling (LangChain/CrewAI consume these)
adapter.to_tools(format="mcp")         # MCP (Claude Desktop, Cursor)

Framework integration

No extra packages to install β€” it's built into liquid-api. adapter.to_tools(format="anthropic" | "openai" | "mcp") emits ready-to-use tool definitions for Claude tool use, OpenAI function calling (which LangChain / LangGraph and CrewAI consume directly), and any MCP client (Claude Desktop, Cursor, …). The bundled liquid-mcp server also exposes Liquid as MCP tools out of the box.

Comparison

| Feature | Liquid | Zapier | LangChain tool | DIY | |---|---|---|---|---| | Auto-discovers any interface (no curated connector) | yes | no | no | no | | APIs + databases + agents in one layer | yes | partial | no | no | | Read and write through one API | yes | yes | partial | no | | Server-side search / aggregate | yes | no | no | partial | | Cross-source output normalization | yes | partial | no | no | | Structured recovery with next_action | yes | no | no | no | | Self-healing on schema drift | yes | no | no | no | | Pre-flight cost estimate | yes | no | no | no | | MCP + A2A + LangChain + CrewAI native | yes | no | partial | no | | Open source | yes | no | yes | n/a |

Documentation

See related servers & alternatives β†’

Related MCP servers

Browse all β†’

Related guides

Hand-picked reading to help you choose and use Browser & Scraping servers.