Liquid

io.github.ertad-family由 GitHub 账号 ertad-family 发布v0.2.0更新于 Oct 25, 2026

Connect your AI agent to anything — APIs, databases, and agents; read and write, auto-discovered.

概览

AI 生成的概览

Liquid 是一个自托管的 MCP 服务器,让助手通过统一的 fetch/query/write/sense 接口自动发现并访问 Web API、数据库、其他代理、邮件和物联网系统。

可以做什么
从 URL、DSN 或代理端点发现并映射接口,列出和查看已保存的适配器,并在取数前估算调用开销。按令牌预算取数,支持服务端搜索、聚合和跨来源输出规范化。读写数据库(Postgres、MySQL、SQLite、DuckDB、SQL Server、Neo4j、MongoDB、Redis),并调用 Web API、GraphQL、gRPC、WebSocket、MCP 和 A2A 目标。感知行变更、发布订阅、SSE 和 MCP 通知等实时事件流,可选发送邮件或操作物联网、工业与 Android 目标。
适用场景
适合需要访问多种服务又不想手写连接器的场景,尤其是数据库和无规范文档的 REST API,或对令牌成本和输出一致性有要求时。也适合需要监听实时事件流或向数据存储写回的代理。
运行要求
以本地 Python 进程运行(uvx 或 pip install liquid-api 安装 liquid-mcp)。仅当发现没有机器可读规范的 REST API 时才需要 LLM 密钥:OPENAI_API_KEY、GEMINI_API_KEY 或 ANTHROPIC_API_KEY,或通过 OPENAI_BASE_URL 指定兼容端点。各后端驱动为可选附加包;部分目标需要自身凭据或系统程序。适配器和凭据保存在用户主目录下。
安装前请注意
写入默认关闭;设置 LIQUID_ALLOW_WRITES=1 会暴露可插入、更新和删除数据库记录的工具,其他目标还可发送邮件、发布消息或操作设备。所连服务的凭据保存在本地用户主目录。发现无规范 REST API 时会把端点信息发送给所选 LLM 提供商。只授予各连接系统所需的最小权限。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Liquid,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

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] [License] [Python]


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.

python
# A web API it has never seen — no spec, no connector, no authadapter = 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 toodb = 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": "[email protected]", "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

python
# Search / aggregate server-side instead of fetch-then-filter — 10-100x fewer tokensorders = 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 capdata = await liquid.fetch(adapter, "/customers", verbosity="terse") # id + 1-2 fields

Cross-source normalization

python
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

python
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

python
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

python
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:

TaskMetricBaselineWith LiquidDelta
Find 10 orders over $100tokens75,4821,519−98%
Revenue by status (aggregate)tokens75,482115−100%
Fetch customer (id+email only)tokens42412−97%
Recover from 401structured next_actionnoyes—
Find the shipping tickettokens14,588154−99%
Stripe↔PayPal consistencyfield overlap0.111.00+9×
Skip wasted call via estimatetokens14,9430−100%
max_tokens=2000 budget captokens14,9431,999−87%

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

Install

bash
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:

bash
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 transportpip install 'liquid-api[pg]'         # Postgres / pgvector (asyncpg)pip install 'liquid-api[mysql]'      # MySQL / MariaDB (aiomysql); SQLite needs no extrapip 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:

text
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]

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:

bash
pip install liquid-apiexport 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:

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

Claude Desktop / any MCP client:

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

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

python
from liquid import Liquid, InMemoryCache, RateLimiterfrom liquid._defaults import InMemoryVault, InMemoryAdapterRegistry, CollectorSinkfrom liquid_langchain import LiquidToolkitfrom langgraph.prebuilt import create_react_agentfrom 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.

InterfaceRuntimeWriteInstall
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 CRUDliquid-api[neo4j]
MongoDB (document)✅ collections, field filters, pagination✅liquid-api[mongodb]
Redis (key-value)✅ keyspace namespaces, typed values, SCAN paging✅ SET/HSET/DELliquid-api[redis]
MQTT (IoT pub/sub)✅ subscribe → batch + live sense✅ publishliquid-api[mqtt]
Modbus (industrial)✅ register/coil read + delta-poll sense✅ register/coil writeliquid-api[modbus]
OPC UA (industrial)✅ node read + native-subscription sense✅ node writeliquid-api[opcua]
BACnet (buildings)✅ object property read + delta-poll sense✅ property writeliquid-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:

python
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.)

DiscoveryWhere it looksCost
Databasescatalog introspection (postgres://, mysql://, mongodb://, redis://, neo4j://, …)Low
gRPC / WebSocket / SSEserver reflection / frame sampling / content-type sniffLow
MCP / A2A / Plugin/mcp, /.well-known/agent-card.json, /.well-known/ai-plugin.jsonLow
OpenAPI / GraphQL / SOAPspec, introspection, or WSDLLow
REST heuristiccommon paths + LLM interpretationMedium
BrowserPlaywright capturing networkHigh

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:

python
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

python
adapter.to_tools(format="anthropic")   # Claude tool useadapter.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

FeatureLiquidZapierLangChain toolDIY
Auto-discovers any interface (no curated connector)yesnonono
APIs + databases + agents in one layeryespartialnono
Read and write through one APIyesyespartialno
Server-side search / aggregateyesnonopartial
Cross-source output normalizationyespartialnono
Structured recovery with next_actionyesnonono
Self-healing on schema driftyesnonono
Pre-flight cost estimateyesnonono
MCP + A2A + LangChain + CrewAI nativeyesnopartialno
Open sourceyesnoyesn/a

Documentation

来源:README.md,提交 c904bd8

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

1
  1. v0.2.0最新Sep 16, 2026

评分

已满足适用检查项的 40%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分

    请在仓库或注册中心条目中声明许可证。