{
  "markdown": "# Liquid\n\n**Connect your AI agent to anything — with no connector to write or maintain.**\n\nPoint Liquid at a URL or a database and it works out the interface for you:\ndiscovers its shape, maps it to the fields you asked for, and handles auth,\npagination and normalization — typed records, no client code. When the upstream\ndrifts, it re-maps and keeps going. The same small API — `fetch` · `query` ·\n`write` · `sense` — reaches web APIs, databases, other agents (MCP/A2A), email,\nand even IoT and industrial systems (MQTT, Modbus, OPC UA, BACnet). An LLM does the\nlearning at setup (and on drift); the data path itself makes no model call.\n\n[![PyPI](https://img.shields.io/pypi/v/liquid-api.svg)](https://pypi.org/project/liquid-api/)\n[![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](https://github.com/ertad-family/liquid/blob/main/LICENSE)\n[![Python](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org/)\n\n---\n\n## What an agent can reach through Liquid\n\nOne agent-facing API (`fetch` · `query` · `write` · `sense`) over everything an\nagent might need to touch — Liquid figures out *how to talk to it* so the agent\ndoesn't have to. It's the agent's senses **and** hands: `fetch`/`query` probe,\n`sense` perceives a live event stream, `write` acts on the world.\n\n- **Web APIs & messaging** — REST/JSON, GraphQL, SOAP/WSDL, gRPC, WebSocket,\n  SSE/NDJSON streams, MQTT (IoT pub/sub — subscribe to sense, publish to act)\n- **Email** — IMAP/SMTP (any provider, app-password or OAuth2 `XOAUTH2`) and the\n  Gmail API (OAuth2): read a mailbox, `sense` new mail as it arrives, and send\n- **Industrial / OT** — Modbus (PLCs, sensors) and OPC UA (Industry-4.0 nodes,\n  native subscriptions) for the factory floor; BACnet for buildings (HVAC/BMS) —\n  read, write, and sense\n- **Android devices** — phones, TV boxes, kiosks via ADB: sense `logcat`, read\n  `shell`, act with `input`/`am`\n- **Other agents & tools** — any MCP server, A2A agents, ChatGPT-plugin manifests\n- **Databases** — Postgres (+ pgvector), MySQL/MariaDB, SQLite, DuckDB, SQL Server,\n  Neo4j (graph), MongoDB (documents), Redis (key-value)\n- **People, places & things** — a human, a home, or a car as a node via\n  `connectors`: Telegram (perceive messages, `send` replies), Home Assistant\n  (perceive a whole smart home's state changes, act via `call_service` — lights,\n  locks, media), and Smartcar (perceive a connected vehicle across ~30 brands —\n  location/battery/fuel — and act: `lock`/`unlock`, charge)\n\nPoint it at a `https://…` endpoint, a `postgres://…` / `mongodb://…` / `redis://…`\nDSN, a `grpc://…` target, or another MCP server — discovery identifies the\ninterface, learns its shape, and hands your agent typed records. The same\n`fetch`/`query`/`write` works regardless of what's underneath. No per-service\nconnector to hand-write; the integration maintains itself when the upstream\nchanges.\n\n```python\n# A web API it has never seen — no spec, no connector, no auth\nadapter = await liquid.get_or_create(\n    \"https://api.openbrewerydb.org/v1/breweries\",\n    target_model={\"name\": \"str\", \"city\": \"str\", \"country\": \"str\"},\n    auto_approve=True,\n)\nbreweries = await liquid.fetch(adapter)            # typed records\n\n# A database is just another interface — same API, and it writes too\ndb = await liquid.get_or_create(\"postgresql://reader@host/shop\",\n                                target_model={\"id\": \"int\", \"email\": \"str\"},\n                                auto_approve=True)\norders = await liquid.fetch(db, \"/public/orders\")\nawait liquid.write(db, \"/public/orders\", op=\"insert\",\n                   values={\"email\": \"a@b.com\", \"total_cents\": 9900},\n                   allow_write=True)               # opt-in; mutates the store\n```\n\nYou hand-write no connector and no schema: an LLM learns the interface once at\nsetup (databases introspect themselves and skip even that), and the integration\n**repairs itself** when the upstream drifts. The runtime is plain deterministic\ntransport — predictable cost, reproducible behavior, nothing to babysit.\n\n## Built for the constraints real agents hit\n\nReaching everything is half of it. The other half is that agents pay for every\ntoken, get confused by inconsistent shapes, and can't parse error prose. Liquid\nanswers each with a concrete primitive — all shipped, all on PyPI.\n\n### Context-budget control\n\n```python\n# Search / aggregate server-side instead of fetch-then-filter — 10-100x fewer tokens\norders = await liquid.search(adapter, \"/orders\",\n    where={\"total_cents\": {\"$gt\": 10000}, \"status\": \"paid\"}, limit=20)\n\nstats = await liquid.aggregate(adapter, \"/orders\",\n    group_by=\"status\", agg={\"total_cents\": \"sum\", \"id\": \"count\"})\n\nhits = await liquid.text_search(adapter, \"/tickets\", \"shipping delay\")  # BM25-lite\n\ndata = await liquid.fetch(adapter, \"/orders\", max_tokens=2000)      # budget cap\ndata = await liquid.fetch(adapter, \"/customers\", verbosity=\"terse\") # id + 1-2 fields\n```\n\n### Cross-source normalization\n\n```python\nliquid = Liquid(..., normalize_output=True)\n# Stripe {amount:1000,currency:\"usd\"} · PayPal {value:\"10.00\",currency_code:\"USD\"}\n#   → Money(amount_cents=1000, currency=\"USD\", amount_decimal=Decimal(\"10.00\"))\n```\n\nTimestamps (Unix / ISO 8601 / RFC 2822) collapse to UTC `datetime`; pagination\nenvelopes (`{data:[…]}` / `{results:[…]}` / Link headers) flatten; ID fields\nnormalize across `id` / `_id` / `uuid` / `*_id`.\n\n### Canonical intents — one mental model across services\n\n```python\nawait liquid.execute_intent(adapter, \"charge_customer\",\n    {\"customer_id\": \"cus_xyz\", \"amount_cents\": 9999, \"currency\": \"USD\"})\n# Same intent on Stripe / Braintree / Square / Adyen — 71 canonical intents\n\n```\n\n### Structured recovery — agents self-heal without parsing text\n\n```python\ntry:\n    await liquid.fetch(adapter, \"/orders\")\nexcept LiquidError as e:\n    if e.recovery and e.recovery.next_action:\n        await agent.call_tool(e.recovery.next_action.tool, e.recovery.next_action.args)\n```\n\nEvery error carries a `Recovery` with `next_action: ToolCall`, `retry_safe`, and\n`retry_after_seconds`. 401 → `store_credentials`. 404/410 → `repair_adapter`. 429\n→ retry after the given delay. And when the upstream's schema drifts, adapters\n**self-heal** (`repair_adapter`) — the agent keeps working.\n\n### Predictable cost — know before you call\n\n```python\nest = await liquid.estimate_fetch(adapter, \"/orders\")\n# FetchEstimate(expected_items=250, expected_tokens=52_000, confidence=\"high\", …)\nif est.expected_tokens < my_budget:\n    data = await liquid.fetch(adapter, \"/orders\")\n```\n\nTools emitted by `to_tools()` carry a `metadata` block (`cost_credits`,\n`typical_latency_ms`, `cached`, `idempotent`, `side_effects`, `related_tools`) so\nthe agent can reason about which tool to pick — and ambient tools\n(`liquid_check_quota`, `liquid_list_adapters`, …) let it ask about state instead\nof memorizing it.\n\n---\n\n## Measured impact\n\nDeterministic benchmarks on realistic agent tasks (500-order, 200-ticket\nfixtures, mocked HTTP) — reproducible via `python -m benchmarks.run`:\n\n| Task | Metric | Baseline | With Liquid | Delta |\n|---|---|---:|---:|---:|\n| Find 10 orders over $100 | tokens | 75,482 | 1,519 | **−98%** |\n| Revenue by status (aggregate) | tokens | 75,482 | 115 | **−100%** |\n| Fetch customer (id+email only) | tokens | 424 | 12 | **−97%** |\n| Recover from 401 | structured next_action | no | yes | — |\n| Find the shipping ticket | tokens | 14,588 | 154 | **−99%** |\n| Stripe↔PayPal consistency | field overlap | 0.11 | 1.00 | **+9×** |\n| Skip wasted call via estimate | tokens | 14,943 | 0 | **−100%** |\n| `max_tokens=2000` budget cap | tokens | 14,943 | 1,999 | **−87%** |\n\nFull methodology + per-task breakdown: [`benchmarks/RESULTS.md`](benchmarks/RESULTS.md).\n\n## Install\n\n```bash\npip install liquid-api                 # core + bundled MCP server (the `liquid-mcp` command)\npip install 'liquid-api[discovery]'    # + an LLM for discovering spec-less REST APIs & field mapping\n```\n\n**Do you need an LLM extra?** Self-describing interfaces — OpenAPI, GraphQL,\ngRPC, MCP, A2A, WSDL — and **all databases** (introspection) discover with **no\nLLM**, and the whole runtime (`fetch`/`query`/`write`/`sense`) never calls a\nmodel. You only need an LLM backend to **discover a REST API that has no\nmachine-readable spec** (heuristic + LLM) and to **map** its fields. `[discovery]`\npulls LiteLLM, which reaches OpenAI / Gemini / Anthropic / local / 100+ providers;\nor pick one directly:\n\n```bash\npip install 'liquid-api[gemini]'     # Google Gemini   (or [anthropic]; OpenAI/local work with no extra via base_url)\npip install 'liquid-api[grpc]'       # gRPC transport (reflection)\npip install 'liquid-api[ws]'         # WebSocket transport\npip install 'liquid-api[pg]'         # Postgres / pgvector (asyncpg)\npip install 'liquid-api[mysql]'      # MySQL / MariaDB (aiomysql); SQLite needs no extra\npip install 'liquid-api[neo4j]'      # Neo4j graph (Bolt / Cypher)\npip install 'liquid-api[duckdb]'     # DuckDB (embedded analytics)\npip install 'liquid-api[mssql]'      # SQL Server (ODBC; needs a system ODBC driver)\npip install 'liquid-api[mongodb]'    # MongoDB (collections as endpoints)\npip install 'liquid-api[redis]'      # Redis (keyspace namespaces as endpoints)\npip install 'liquid-api[mqtt]'       # MQTT (IoT pub/sub)\npip install 'liquid-api[modbus]'     # Modbus (industrial registers)\npip install 'liquid-api[opcua]'      # OPC UA (Industry-4.0 nodes + subscriptions)\npip install 'liquid-api[bacnet]'     # BACnet (building automation; ADB needs the system `adb` binary)\n# Framework integration (LangChain / OpenAI / Anthropic / MCP) is built in — no extra package.\n```\n\nThe core is dependency-free — every backend's library is an optional extra,\nimported only when used.\n\n## See it work — live, no pre-config\n\nPoint Liquid at an API it has never seen (no adapter, no OpenAPI spec, no auth)\nand get typed records back — you write no connector; discovery + mapping is the\nonly place a model runs. Runnable end to end via\n[`examples/live_quickstart.py`](examples/live_quickstart.py):\n\n```text\nConnecting to an API Liquid has never seen:\n  https://api.openbrewerydb.org/v1/breweries\n\n  discovery method : rest_heuristic\n  mapped fields    : ['name', 'city', 'state', 'country']\n  LLM calls so far : 2  (discovery + mapping)\n\nfetch() -> 50 typed records; first 3:\n   {'name': '(405) Brewing Co', 'city': 'Norman', 'state': 'Oklahoma', 'country': 'United States'}\n   {'name': '(512) Brewing Co', 'city': 'Austin', 'state': 'Texas', 'country': 'United States'}\n   {'name': '1 of Us Brewing Company', 'city': 'Mount Pleasant', 'state': 'Wisconsin', 'country': 'United States'}\n\n  LLM calls during fetch : 0\n  LLM calls on 2nd fetch : 0\n```\n\nYou wrote no connector, no schema, no auth glue — Liquid learned the interface\nfor you, and will re-learn it if it changes. That's the point: integrations you\ndon't build or babysit.\n\n## Run as an MCP server (open source, self-hosted)\n\nExpose the engine to any MCP client (Claude Desktop, Cursor, Claude Code) — it\nruns **in your own process**, no cloud, no account, no lock-in:\n\n[![Add to Cursor](https://img.shields.io/badge/Add%20to-Cursor-000?logo=cursor&logoColor=white)](cursor://anysphere.cursor-deeplink/mcp/install?name=liquid&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyJsaXF1aWQtbWNwIl19)\n\nOne-click in Cursor (the button writes the server into your `mcp.json`; add your\n`OPENAI_API_KEY` in Cursor's MCP settings afterward). Or set it up manually:\n\n```bash\npip install liquid-api\nexport OPENAI_API_KEY=sk-...        # or GEMINI_API_KEY / ANTHROPIC_API_KEY,\n                                    # or OPENAI_BASE_URL=http://localhost:11434/v1 for local (Ollama/vLLM)\nliquid-mcp                          # or: python -m liquid.mcp_server\n```\n\nZero-install with `uvx` (the [`liquid-mcp`](https://pypi.org/project/liquid-mcp/)\npackage makes the command run by name) — Claude Code:\n\n```bash\nclaude mcp add liquid --scope user -e OPENAI_API_KEY=sk-... -- uvx liquid-mcp\n```\n\nClaude Desktop / any MCP client:\n\n```json\n{ \"mcpServers\": { \"liquid\": {\n  \"command\": \"uvx\",\n  \"args\": [\"liquid-mcp\"],\n  \"env\": { \"OPENAI_API_KEY\": \"sk-...\" }\n} } }\n```\n\n(Or after `pip install liquid-api`, drop `uvx` and use `\"command\": \"liquid-mcp\"` directly.)\n\n**One-click in Claude Desktop:** install the [`.mcpb` bundle](packages/liquid-mcp/mcpb) —\nit prompts for your model key on install (stored in the OS keychain), with no JSON\nto edit. Requires `uv` on the machine.\n\n<!-- mcp-name: io.github.ertad-family/liquid -->\n\nTools: `liquid_connect` (discover + map any interface), `liquid_fetch`,\n`liquid_query` (server-side search/aggregate), `liquid_estimate` (pre-flight\ncost/size, no call), `liquid_list_adapters`, `liquid_discover`. The surface is\n**read-only by default**; start the server with `LIQUID_ALLOW_WRITES=1` to also\nexpose `liquid_execute` (database insert/update/delete). Adapters and credentials\npersist under `~/.liquid`. Backed by **any LLM** — OpenAI, Gemini, Anthropic, any\nOpenAI-compatible/local endpoint via `base_url`, **100+ providers via LiteLLM**,\nor your own function through `CallableBackend`.\n\n## Quick start — LangGraph agent\n\n```python\nfrom liquid import Liquid, InMemoryCache, RateLimiter\nfrom liquid._defaults import InMemoryVault, InMemoryAdapterRegistry, CollectorSink\nfrom liquid_langchain import LiquidToolkit\nfrom langgraph.prebuilt import create_react_agent\nfrom langchain_openai import ChatOpenAI\n\nliquid = Liquid(\n    llm=my_llm, vault=InMemoryVault(), sink=CollectorSink(),\n    registry=InMemoryAdapterRegistry(), cache=InMemoryCache(), rate_limiter=RateLimiter(),\n    normalize_output=True,    # cross-source canonical shapes\n    include_meta=True,        # _meta block on every response\n)\n\nadapter = await liquid.get_or_create(\n    \"https://api.shopify.com\",\n    target_model={\"id\": \"str\", \"total_cents\": \"int\", \"customer_email\": \"str\"},\n    credentials={\"access_token\": \"shpat_...\"},\n    auto_approve=True,\n)\n\ntools = LiquidToolkit(adapter, liquid).get_tools()\nagent = create_react_agent(ChatOpenAI(model=\"gpt-4o-mini\"), tools)\nresult = await agent.ainvoke(\n    {\"messages\": [(\"user\", \"Find 5 recent orders over $100 from VIP customers\")]}\n)\n```\n\nThe agent's tools come with rich descriptions (WHEN to use, NOT FOR what, return\nshape, cost), structured recovery on every error, and server-side search so it\nnever pulls 500 orders to find 5.\n\n## Every interface, one API\n\nDiscovery identifies the target and tags each endpoint with a protocol; a\npluggable transport driver runs it — but the agent-facing API (`fetch`, `query`,\n`write`, mapping, recovery, cache, rate limits) is identical across all of them.\n\n| Interface | Runtime | Write | Install |\n|---|---|---|---|\n| REST / HTTP+JSON | ✅ | ✅ actions (POST/PUT/PATCH/DELETE) | — |\n| GraphQL | ✅ query + Relay pagination | ✅ mutations | — |\n| SOAP / WSDL | ✅ stdlib XML | — | — |\n| gRPC | ✅ unary + server-streaming (reflection) | — | `liquid-api[grpc]` |\n| WebSocket | ✅ bounded batch reads + subscribe + live `sense` | — | `liquid-api[ws]` |\n| SSE / NDJSON (HTTP server-push) | ✅ bounded batch reads + live `sense` | — | — |\n| MCP (agent) | ✅ call tools / read resources + notification `sense` | ✅ tool calls | — |\n| A2A (agent) | ✅ JSON-RPC `message/send` to AgentCard skills | — | — |\n| Postgres (+pgvector) | ✅ tables/views, filters, pagination, vector search | ✅ | `liquid-api[pg]` |\n| MySQL / MariaDB | ✅ tables/views, filters, pagination | ✅ | `liquid-api[mysql]` |\n| SQLite | ✅ tables/views, filters, pagination | ✅ | — (stdlib) |\n| DuckDB | ✅ tables/views, filters, pagination | ✅ | `liquid-api[duckdb]` |\n| SQL Server | ✅ tables/views, OFFSET/FETCH pagination | ✅ | `liquid-api[mssql]` |\n| Neo4j (graph) | ✅ labels/relationship types, property filters | ✅ node CRUD | `liquid-api[neo4j]` |\n| MongoDB (document) | ✅ collections, field filters, pagination | ✅ | `liquid-api[mongodb]` |\n| Redis (key-value) | ✅ keyspace namespaces, typed values, SCAN paging | ✅ SET/HSET/DEL | `liquid-api[redis]` |\n| MQTT (IoT pub/sub) | ✅ subscribe → batch + live `sense` | ✅ publish | `liquid-api[mqtt]` |\n| Modbus (industrial) | ✅ register/coil read + delta-poll `sense` | ✅ register/coil write | `liquid-api[modbus]` |\n| OPC UA (industrial) | ✅ node read + native-subscription `sense` | ✅ node write | `liquid-api[opcua]` |\n| BACnet (buildings) | ✅ object property read + delta-poll `sense` | ✅ property write | `liquid-api[bacnet]` |\n| ADB (Android) | ✅ shell read + logcat `sense` | ✅ shell actions (input/am) | — (system `adb`) |\n| Email — IMAP/SMTP | ✅ read mailbox by UID + new-mail `sense` | ✅ send (MIME) | — (stdlib) |\n| Email — Gmail API | ✅ list/get + `history` `sense` | ✅ `messages.send` | — (OAuth2) |\n\n**Read and write.** `liquid.write(adapter, endpoint, op=\"insert\", values={...},\nallow_write=True)` mutates any database (SQL `INSERT`/`UPDATE`/`DELETE`, Mongo\ninsert/update/delete, Redis `SET`/`HSET`/`DEL`, Neo4j node CRUD); web/agent\nwrites go through verified actions. Identifiers come from introspection and\nvalues are parameterized; `update`/`delete` require a `where` (no blanket\nmutations); writes are **off until you opt in** with `allow_write=True`.\n\n**Sense — the afferent organ.** `liquid.sense(adapter, endpoint)` perceives a live\nevent stream wherever one exists: SQL row deltas (and Postgres `LISTEN/NOTIFY`),\nRedis pub/sub, WebSocket frames, HTTP server-push (SSE/NDJSON), and MCP\nnotifications — each yielded as a modality-agnostic event. Pointed *inward*,\n`liquid.sense_webhook(port=…, verifier=…)` hosts an inbound endpoint so a service\n(or a human, via a webhook) POSTing to the agent becomes a perceivable signal\ntoo. All bounded by `max_events` / `max_seconds`, so an agent can drain-by-pull.\n\n**The sensorimotor loop.** `react(stream, handler)` drives a handler for each\nperceived event — with error isolation and bounded concurrency — so a host can\n*perceive → wake the agent → act*. `merge_senses(*streams)` fans several senses\ninto one loop, so one agent can watch a database, a queue, and a webhook at once:\n\n```python\nevents = merge_senses(\n    await liquid.sense(orders, \"/orders\"),\n    await liquid.sense_webhook(port=8088, verifier=stripe_verifier),\n)\nawait react(events, on_event, max_concurrency=4)\n```\n\n**Discovery is automatic — and identifies on the fly.** Before the pipeline runs,\na fingerprint step names the target: a bare `host:port` is normalized by\nwell-known port (`db:5432` → `postgresql://db:5432`), and `liquid.identify(url)`\nanswers \"what is this, and is its driver installed?\" with an install hint when a\nbackend is missing. (Identifying a protocol is feasible on the fly; *speaking* a\nnew authenticated binary protocol isn't — so unknowns are named, not guessed at.)\n\n| Discovery | Where it looks | Cost |\n|---|---|---|\n| Databases | catalog introspection (`postgres://`, `mysql://`, `mongodb://`, `redis://`, `neo4j://`, …) | Low |\n| gRPC / WebSocket / SSE | server reflection / frame sampling / content-type sniff | Low |\n| MCP / A2A / Plugin | `/mcp`, `/.well-known/agent-card.json`, `/.well-known/ai-plugin.json` | Low |\n| OpenAPI / GraphQL / SOAP | spec, introspection, or WSDL | Low |\n| REST heuristic | common paths + LLM interpretation | Medium |\n| Browser | Playwright capturing network | High |\n\n**Add a backend without writing code.** For the SQL family the contract is\ndeclarative enough to be *data*: a **dialect manifest** (quoting, placeholder\nstyle, pagination, introspection SQL, error map, DBAPI2 module) registered via\n`register_sql_manifest({...})` installs a working driver + discovery — so a new\nSQL / wire-compatible store (CockroachDB, ClickHouse, any DBAPI2 driver), even one\nfetched from the network as JSON, connects without a release. New protocols\notherwise plug in via the `liquid.transport.ProtocolDriver` protocol; SQL backends\nshare a dialect-aware core, so a new one is a ~80-line adapter.\n\n**Want to teach Liquid a new protocol?** A complete transport driver\n(`fetch`/`write`/`sense`) is typically ~150 lines — see\n[docs/ADDING_A_DRIVER.md](docs/ADDING_A_DRIVER.md) for the walkthrough and a\nwishlist (CAN bus, CoAP, KNX, AMQP, NATS, SNMP, …). Contributions welcome.\n\n2,500+ APIs are pre-discovered and pre-mapped in the\n[global catalog](https://liquid.ertad.family/catalog) — most popular services\nconnect with zero discovery cost.\n\n## Architecture\n\n```\nURL / DSN                       Agent\n   ↓                              ↑\n FINGERPRINT → DISCOVERY        FETCH · QUERY · WRITE · SEARCH · AGGREGATE\n   ↓                              ↑\n one ProtocolDriver per          Deterministic per-protocol transport\n interface:                        • Query DSL (server-side filter)\n   REST GraphQL gRPC WS SSE MQTT   • Output normalization\n   MCP A2A · SQL graph doc KV ·    • Verbosity / max_tokens / _meta\n   Modbus OPC-UA BACnet ADB …      • (full protocol list in the table above)\n   ↓                              • Structured recovery + self-heal\n APISchema                        • Rate-limit-aware token bucket\n   ↓                              • Response cache (Cache-Control aware)\n AI MAPPING (setup only)          • Empirical probing data (Cloud)\n   ↓\n AdapterConfig\n```\n\n**AI participates at setup only.** Runtime is pure transport with transforms — no\nLLM per call, predictable cost, reproducible behavior (except `search_nl`, which\ncaches its compilations).\n\n## Swappable components\n\nEvery cross-cutting concern is a `Protocol` you can replace:\n\n```python\nfrom liquid.protocols import (\n    Vault, LLMBackend, DataSink, KnowledgeStore, AdapterRegistry, CacheStore,\n)\n```\n\nIn-memory implementations ship for all of them; `liquid-cloud` provides\n`PostgresVault`, `RedisCache`, etc. for hosted deployments.\n\n## Framework support\n\n```python\nadapter.to_tools(format=\"anthropic\")   # Claude tool use\nadapter.to_tools(format=\"openai\")      # OpenAI function calling (LangChain/CrewAI consume these)\nadapter.to_tools(format=\"mcp\")         # MCP (Claude Desktop, Cursor)\n```\n\n## Framework integration\n\nNo extra packages to install — it's built into `liquid-api`.\n`adapter.to_tools(format=\"anthropic\" | \"openai\" | \"mcp\")` emits ready-to-use tool\ndefinitions for Claude tool use, OpenAI function calling (which LangChain /\nLangGraph and CrewAI consume directly), and any MCP client (Claude Desktop,\nCursor, …). The bundled `liquid-mcp` server also exposes Liquid as MCP tools out\nof the box.\n\n## Comparison\n\n| Feature | Liquid | Zapier | LangChain tool | DIY |\n|---|---|---|---|---|\n| Auto-discovers any interface (no curated connector) | yes | no | no | no |\n| APIs + databases + agents in one layer | yes | partial | no | no |\n| Read **and** write through one API | yes | yes | partial | no |\n| Server-side search / aggregate | yes | no | no | partial |\n| Cross-source output normalization | yes | partial | no | no |\n| Structured recovery with next_action | yes | no | no | no |\n| Self-healing on schema drift | yes | no | no | no |\n| Pre-flight cost estimate | yes | no | no | no |\n| MCP + A2A + LangChain + CrewAI native | yes | no | partial | no |\n| Open source | yes | no | yes | n/a |\n\n## Documentation\n\n- [Quickstart](docs/QUICKSTART.md) — discover → map → fetch, plus the **no-LLM runtime**\n- [OSS vs. Cloud](docs/OSS-VS-CLOUD.md) — the honest boundary: free/self-hosted vs. hosted\n- [Architecture](docs/ARCHITECTURE.md)\n- [Extending](docs/EXTENDING.md) — implement your own Vault / LLM / Sink\n- [Write operations spec](docs/SPEC-WRITE-OPERATIONS.md)\n",
  "bytes": 23463,
  "sha": "8321511886ffb4d5441a8abedb6022e677a7d50fe8c4525e7a9d0f1b8b00a608",
  "repo_slug": "ertad-family/liquid",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_ertad_family_liquid_935fa3ca/readme"
}