liquid-mcp
Liquid MCP is an agent-native API fabric that lets AI agents discover, connect to, and interact with any HTTP API with token-budget control, structured recovery, and pre-flight cost estimation — requiring no LLM calls at runtime after initial setup.
liquid_connect— Discover any API by URL and map it to a target data model. Handles auth-walled APIs via credentials. Returns anadapter_idfor subsequent operations. One-time setup; all future fetches are deterministic with zero LLM calls.liquid_list_adapters— List all API adapters already connected and persisted locally (~/.liquid), so agents skip redundant rediscovery.liquid_discover— Inspect an API's available endpoints and authentication requirements without creating a persistent adapter. Useful for exploration before committing to a connection.liquid_fetch— Fetch typed, structured records through a connected adapter. Fully deterministic. Returns a_metablock with service, endpoint, latency, and record count.liquid_query— Perform server-side search or aggregation to retrieve only the answer, not the full payload. Supports filtering (where), field selection,limit,group_by, and aggregation functions (e.g., sum, count). Reduces token usage by up to 98–100% vs. fetch-then-filter.liquid_estimate— Get a pre-flight estimate of expected items, bytes, tokens, credits, and latency before making any HTTP call, enabling informed budget decisions.
Enables agents to interact with Adyen's payment API through Liquid's intent layer, supporting operations like charging customers, refunding charges, customer management, and order listing with context optimization and error recovery.
Enables agents to interact with Braintree's payment API through Liquid's intent layer, supporting operations like charging customers, refunding charges, customer management, and order listing with context optimization and error recovery.
Enables agents to interact with PayPal's payment API through Liquid's intent layer, supporting operations like charging customers, refunding charges, customer management, and order listing with context optimization and error recovery.
Enables agents to interact with Shopify's API through Liquid's intent layer, supporting operations like listing orders, canceling orders, and customer management with context optimization and error recovery.
Enables agents to interact with Square's payment API through Liquid's intent layer, supporting operations like charging customers, refunding charges, customer management, and order listing with context optimization and error recovery.
Enables agents to interact with Stripe's API through Liquid's intent layer, supporting operations like charging customers, refunding charges, customer management, listing orders, and canceling orders with context optimization and error recovery.
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.
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,sensenew mail as it arrives, and sendIndustrial / 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, readshell, act withinput/amOther 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,sendreplies), Home Assistant (perceive a whole smart home's state changes, act viacall_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 storeYou 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.
Related MCP server: APIClaw
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 fieldsCross-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% |
| 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 mappingDo 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 : 0You 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:
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_serverZero-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-mcpClaude 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.
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) | — |
|
WebSocket | ✅ bounded batch reads + subscribe + live | — |
|
SSE / NDJSON (HTTP server-push) | ✅ bounded batch reads + live | — | — |
MCP (agent) | ✅ call tools / read resources + notification | ✅ tool calls | — |
A2A (agent) | ✅ JSON-RPC | — | — |
Postgres (+pgvector) | ✅ tables/views, filters, pagination, vector search | ✅ |
|
MySQL / MariaDB | ✅ tables/views, filters, pagination | ✅ |
|
SQLite | ✅ tables/views, filters, pagination | ✅ | — (stdlib) |
DuckDB | ✅ tables/views, filters, pagination | ✅ |
|
SQL Server | ✅ tables/views, OFFSET/FETCH pagination | ✅ |
|
Neo4j (graph) | ✅ labels/relationship types, property filters | ✅ node CRUD |
|
MongoDB (document) | ✅ collections, field filters, pagination | ✅ |
|
Redis (key-value) | ✅ keyspace namespaces, typed values, SCAN paging | ✅ SET/HSET/DEL |
|
MQTT (IoT pub/sub) | ✅ subscribe → batch + live | ✅ publish |
|
Modbus (industrial) | ✅ register/coil read + delta-poll | ✅ register/coil write |
|
OPC UA (industrial) | ✅ node read + native-subscription | ✅ node write |
|
BACnet (buildings) | ✅ object property read + delta-poll | ✅ property write |
|
ADB (Android) | ✅ shell read + logcat | ✅ shell actions (input/am) | — (system |
Email — IMAP/SMTP | ✅ read mailbox by UID + new-mail | ✅ send (MIME) | — (stdlib) |
Email — Gmail API | ✅ list/get + | ✅ | — (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 ( | Low |
gRPC / WebSocket / SSE | server reflection / frame sampling / content-type sniff | Low |
MCP / A2A / Plugin |
| 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)
↓
AdapterConfigAI 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
Quickstart — discover → map → fetch, plus the no-LLM runtime
OSS vs. Cloud — the honest boundary: free/self-hosted vs. hosted
Extending — implement your own Vault / LLM / Sink
Available Tools
6 toolsliquid_connectConnect to an API (one-time setup)AIdempotent
One-time setup for an API. Discovers the API at url, uses an LLM to map its responses onto your target_model, and saves a reusable adapter; returns an adapter_id you then pass to liquid_fetch / liquid_query / liquid_estimate. Side effects: makes outbound HTTP(S) requests to url, calls the configured LLM (requires an API key), and persists the adapter + any credentials under ~/.liquid. Idempotent — re-connecting the same url+target_model reuses the existing adapter instead of duplicating it. Use this once per API. For a quick look without saving anything, use liquid_discover instead; to read data from an already-connected API, use liquid_fetch.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Base URL or a specific endpoint of the API (e.g. https://api.example.com or https://api.example.com/v1/users). Also accepts a GraphQL endpoint, a WSDL URL, or grpc:// / wss:// targets. | |
| target_model | Yes | The record shape you want back: a flat map of field name -> type, e.g. {"name": "str", "price": "float", "in_stock": "bool"}. Liquid maps the API's raw response onto exactly these fields; everything else is dropped. | |
| credentials | No | Optional secrets for an auth-walled API, e.g. {"api_key": "..."}, {"token": "..."}, or {"username": "...", "password": "..."}. Stored encrypted under ~/.liquid and applied automatically on every later fetch. Omit for public APIs. |
Output Schema
| Name | Required | Description |
|---|---|---|
| status | No | "connected" or "review_needed". |
| adapter_id | No | Use this id with liquid_fetch/liquid_query. |
| service | No | |
| mapped_fields | No | |
| endpoints | No | |
| error | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses behavioral traits beyond the annotations: it mentions side effects ('makes outbound HTTP(S) requests to url, calls the configured LLM, and persists the adapter + any credentials under ~/.liquid') and confirms idempotency in a user-friendly way ('reuses the existing adapter instead of duplicating it'). This meets the high bar for transparency even with good annotations, as it adds context about persistence and auth requirements.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loaded with the main purpose. It contains several sentences, each adding value (e.g., side effects, idempotency, alternatives). While slightly longer than minimal, every sentence is justified and no redundancy is present. Could be slightly tighter, but overall effective.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (3 parameters, side effects, idempotency, output schema with adapter_id), the description covers all necessary context: usage, behavior, parameter details, return value, and alternatives. It also notes credentials storage. The description is complete enough for an AI agent to understand when and how to use the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Despite 100% schema coverage, the description adds significant meaning: for url, it explains it accepts GraphQL, WSDL, gRPC, or WebSocket targets (not in schema). For target_model, it clarifies it must be a flat map with examples. For credentials, it states they are stored encrypted under ~/.liquid and applied automatically. This enriches the parameter understanding beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'One-time setup for an API.' It specifies the actions: discovers the API at a URL, maps responses to a target model, and saves a reusable adapter, returning an adapter_id. It distinguishes from siblings by explicitly naming liquid_discover (quick look without saving) and liquid_fetch (reading from an already-connected API), making the purpose unique.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit usage guidance: 'Use this once per API.' It tells when to use an alternative: 'For a quick look without saving anything, use liquid_discover instead; to read data from an already-connected API, use liquid_fetch.' This clearly delineates when to use this tool versus its siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
liquid_discoverInspect an API without saving an adapterARead-onlyIdempotent
Inspect an API's shape — service name, discovery method, auth type and endpoint list — without creating or saving an adapter. Side effects: makes outbound HTTP(S) requests to url to probe it, and may call the configured LLM for APIs that publish no machine-readable spec (REST heuristic). Read-only: nothing is persisted. Use this to preview an unknown API; when you're ready to actually read data, call liquid_connect, which discovers and maps and saves a reusable adapter.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Base URL of the API to inspect (same forms as liquid_connect's url). | |
| credentials | No | Optional secrets for an auth-walled API, e.g. {"api_key": "..."}, {"token": "..."}, or {"username": "...", "password": "..."}. Stored encrypted under ~/.liquid and applied automatically on every later fetch. Omit for public APIs. |
Output Schema
| Name | Required | Description |
|---|---|---|
| service | No | |
| discovery_method | No | How it was found: openapi, graphql, soap, grpc, websocket, mcp, rest_heuristic, or browser. |
| auth_type | No | |
| endpoints | No | |
| error | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses side effects: makes outbound HTTP(S) requests and may call LLM for REST heuristic. States read-only and nothing persisted. No contradiction with annotations (readOnlyHint, destructiveHint, idempotentHint).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Description is about 5 well-structured sentences, no fluff, front-loaded with key action. Every sentence adds value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given output schema exists, description adequately covers the return shape (service name, discovery method, auth type, endpoint list). No gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, description adds meaning by explaining credentials are optional, stored encrypted, and implies automatic usage on later fetches. Adds value beyond schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it inspects an API's shape (service name, discovery method, auth type, endpoint list) without saving, and distinguishes from liquid_connect which also saves an adapter.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says to use for previewing unknown APIs, and to call liquid_connect when ready to read data. Provides clear when-to-use and when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
liquid_estimateEstimate a fetch (no call)ARead-onlyIdempotent
Pre-flight estimate for a fetch — predicted item count, bytes, tokens, credits and latency, each with a confidence and source — without making any HTTP call or LLM call. Read-only and free. Returns {estimate: {...}}. Check this before a potentially large liquid_fetch to decide whether to narrow the pull with liquid_query (filter/aggregate) first. Requires an adapter_id from liquid_connect.
| Name | Required | Description | Default |
|---|---|---|---|
| adapter_id | Yes | An adapter id returned by liquid_connect (or listed by liquid_list_adapters). | |
| endpoint | No | Optional endpoint path to act on (e.g. "/users"); defaults to the adapter's primary endpoint. Use a path shown by liquid_connect / liquid_list_adapters. |
Output Schema
| Name | Required | Description |
|---|---|---|
| estimate | No | Predicted items, bytes, tokens, credits, latency with confidence + source. |
| error | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false, idempotentHint=true. Description adds context: 'no HTTP call or LLM call', 'read-only and free', and describes return structure with confidence/source, which goes beyond annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise sentences, front-loaded with purpose and output, followed by usage guidance. No redundant information; every sentence adds value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simple pre-flight nature, output schema is referenced, schema covers parameters, annotations cover safety, and description provides complete usage context including prerequisites and alternative tools.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% so baseline is 3. Description mentions adapter_id is from liquid_connect and endpoint optional defaults to primary, which reinforces schema but doesn't add new semantic meaning beyond it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description explicitly states 'Pre-flight estimate for a fetch' with specific outputs (item count, bytes, tokens, credits, latency) and contrasts with siblings liquid_fetch and liquid_query, making the tool's role unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly instructs to check this before a large liquid_fetch and suggests using liquid_query to narrow the pull if needed. Also notes prerequisite of an adapter_id from liquid_connect, providing explicit when-to-use and alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
liquid_fetchFetch records through an adapterARead-onlyIdempotent
Fetch records through a connected adapter, mapped to the target_model you set at connect time — deterministic, no LLM call. Side effects: makes a read-only outbound HTTP(S) request to the connected API using the stored credentials; it is subject to that API's rate limits (Liquid throttles proactively and surfaces 429s with retry hints). Returns {records, data: [up to 100 mapped records], _meta}. Requires an adapter_id from liquid_connect. Use this to pull whole records; to filter/aggregate server-side and get a smaller answer use liquid_query instead; to size a pull before making it, call liquid_estimate first.
| Name | Required | Description | Default |
|---|---|---|---|
| adapter_id | Yes | An adapter id returned by liquid_connect (or listed by liquid_list_adapters). | |
| endpoint | No | Optional endpoint path to act on (e.g. "/users"); defaults to the adapter's primary endpoint. Use a path shown by liquid_connect / liquid_list_adapters. |
Output Schema
| Name | Required | Description |
|---|---|---|
| records | No | Number of records returned. |
| data | No | Mapped records (a list, capped at 100; or a single object). |
| _meta | No | Call metadata: adapter_id, service, endpoint, latency_ms (and records when applicable). |
| error | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false, idempotentHint=true. Description adds 'deterministic, no LLM call', 'read-only outbound HTTP(S) request', 'subject to rate limits with proactive throttling and 429 retry hints', and return structure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three well-organized sentences covering purpose, side effects, alternatives, and parameters. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 2 params, output schema, and rich annotations, the description provides all necessary context: purpose, prerequisites, behavior, return format, and sibling differentiation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers both parameters fully. Description adds context by linking adapter_id to liquid_connect and end point to liquid_connect/list_adapters, and notes optional default behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states verb 'Fetch records through a connected adapter', specifies determinism and no LLM call, and distinguishes from siblings by naming liquid_query and liquid_estimate as alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use this tool ('pull whole records') and when to use alternatives ('liquid_query for filtering/aggregation', 'liquid_estimate for sizing'). Also notes prerequisite 'adapter_id from liquid_connect'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
liquid_list_adaptersList connected adaptersARead-onlyIdempotent
List the adapters already connected on this machine (read from ~/.liquid) — read-only, no network call, no LLM. Each entry has its adapter_id, service name, source url and endpoint paths. Call this to find an adapter_id for liquid_fetch / liquid_query / liquid_estimate, or to check whether an API is already connected before calling liquid_connect.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| adapters | No | Connected adapters with adapter_id, service, url, endpoints. |
| error | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate readOnly, destructive, idempotent hints. Description adds 'read-only, no network call, no LLM' and specifies data source (~/.liquid), providing context beyond annotations. No contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, each earning its place. First states function, second provides usage context. No wasted words, well-structured.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given zero parameters, rich annotations, and presence of output schema, the description fully covers what the tool does, its data source, fields in output, and when to use it. Complete for the task.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
No parameters; schema coverage is 100%. Baseline is 4 per the rule. The description does not need to add param info, and it is clear that no parameters exist.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'List the adapters already connected' with specific verb and resource. It distinguishes from siblings by noting it's read-only, no network, no LLM, and explicitly names sibling tools for context.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit usage guidance: 'Call this to find an adapter_id for liquid_fetch / liquid_query / liquid_estimate, or to check whether an API is already connected before calling liquid_connect.' This directly tells when to use and implies when not to.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
liquid_querySearch or aggregate through an adapterARead-onlyIdempotent
Run a server-side search or aggregation through an adapter and get just the answer instead of the full payload — deterministic, no LLM call, read-only. Two modes: set group_by/agg to aggregate (counts, sums, …), or where/fields/limit to filter and project. Side effects: a read-only outbound HTTP(S) request to the connected API, rate-limited like liquid_fetch. Returns search results {records, data, _meta} or an aggregation {result, _meta}. Prefer this over liquid_fetch whenever you only need a filtered slice, a count, or a summary — it returns far fewer tokens.
| Name | Required | Description | Default |
|---|---|---|---|
| adapter_id | Yes | An adapter id returned by liquid_connect (or listed by liquid_list_adapters). | |
| endpoint | No | Optional endpoint path to act on (e.g. "/users"); defaults to the adapter's primary endpoint. Use a path shown by liquid_connect / liquid_list_adapters. | |
| where | No | Search-mode filter as field -> value (or field -> {op: value}), e.g. {"status": "active", "price": {"gt": 100}}. Keys are target_model fields. | |
| fields | No | Search-mode projection: target_model field names to return, e.g. ["name", "price"]. Omit for all fields. | |
| limit | No | Search-mode max records to return (default 100). | |
| group_by | No | Aggregate-mode: target_model field to group by, e.g. "category". | |
| agg | No | Aggregate-mode: aggregations per group as field -> op, e.g. {"price": "sum", "id": "count"}. Provide together with group_by. |
Output Schema
| Name | Required | Description |
|---|---|---|
| records | No | Search mode: number of records. |
| data | No | Search mode: matching records (capped at 100). |
| result | No | Aggregate mode: the grouped/aggregated result. |
| _meta | No | Call metadata: adapter_id, service, endpoint, latency_ms (and records when applicable). |
| error | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false, idempotentHint=true. The description adds that it is 'deterministic, no LLM call' and mentions a read-only outbound HTTP request with rate-limiting, providing helpful behavioral context beyond the structured annotations. No contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Description is fairly concise and well-structured, leading with key characteristics (deterministic, read-only) and following with mode details and comparison. While slightly long, every sentence contributes value; no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the presence of output schema (not shown but indicated), annotations, and 7 parameters with full schema coverage, the description sufficiently covers purpose, usage modes, side effects, return types, and sibling differentiation. It is complete for a query tool with this complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with descriptions for each parameter. The description adds context by explaining the two modes and which parameters belong to each (e.g., group_by/agg for aggregate, where/fields/limit for search), helping the agent understand parameter relationships beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states the tool performs server-side search or aggregation through an adapter, is deterministic and read-only. It distinguishes two modes (search with where/fields/limit, aggregate with group_by/agg) and explicitly contrasts with sibling tool liquid_fetch, making its purpose unambiguous and well-differentiated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit guidance on when to use this tool over liquid_fetch ('whenever you only need a filtered slice, a count, or a summary'), lists side effects (rate-limited like liquid_fetch), and describes the two operational modes. This equips the agent with clear decision-making criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v0.1.1- Changed
liquid_connect5 fields changed- added
Input schema / properties / credentials / descriptionAdded value: +"Optional secrets for an auth-walled API, e.g. {\"api_key\": \"...\"}, {\"token\": \"...\"}, or {\"username\": \"...\", \"password\": \"...\"}. Stored encrypted under ~/.liquid and applied automatically on every later fetch. Omit for public APIs." - added
Input schema / properties / target_model / additionalPropertiesAdded value: +{ + "type": "string" +} - changed
Input schema / properties / target_model / descriptionPrevious value: -"field name -> type (e.g. {\"name\":\"str\",\"price\":\"int\"})"New value: +"The record shape you want back: a flat map of field name -> type, e.g. {\"name\": \"str\", \"price\": \"float\", \"in_stock\": \"bool\"}. Liquid maps the API's raw response onto exactly these fields; everything else is dropped." - added
Input schema / properties / url / descriptionAdded value: +"Base URL or a specific endpoint of the API (e.g. https://api.example.com or https://api.example.com/v1/users). Also accepts a GraphQL endpoint, a WSDL URL, or grpc:// / wss:// targets." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "properties": { + "adapter_id": { + "description": "Use this id with liquid_fetch/liquid_query.", + "type": "string" + }, + "endpoints": { + "items": { + "type": "string" + }, + "type": "array" + }, + "error": { + "type": "string" + }, + "mapped_fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "service": { + "type": "string" + }, + "status": { + "description": "\"connected\" or \"review_needed\".", + "type": "string" + } + }, + "type": "object" +}
- Changed
liquid_discover3 fields changed- added
Input schema / properties / credentials / descriptionAdded value: +"Optional secrets for an auth-walled API, e.g. {\"api_key\": \"...\"}, {\"token\": \"...\"}, or {\"username\": \"...\", \"password\": \"...\"}. Stored encrypted under ~/.liquid and applied automatically on every later fetch. Omit for public APIs." - added
Input schema / properties / url / descriptionAdded value: +"Base URL of the API to inspect (same forms as liquid_connect's url)." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "properties": { + "auth_type": { + "type": "string" + }, + "discovery_method": { + "description": "How it was found: openapi, graphql, soap, grpc, websocket, mcp, rest_heuristic, or browser.", + "type": "string" + }, + "endpoints": { + "items": { + "type": "string" + }, + "type": "array" + }, + "error": { + "type": "string" + }, + "service": { + "type": "string" + } + }, + "type": "object" +}
- Changed
liquid_estimate3 fields changed- added
Input schema / properties / adapter_id / descriptionAdded value: +"An adapter id returned by liquid_connect (or listed by liquid_list_adapters)." - added
Input schema / properties / endpoint / descriptionAdded value: +"Optional endpoint path to act on (e.g. \"/users\"); defaults to the adapter's primary endpoint. Use a path shown by liquid_connect / liquid_list_adapters." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "properties": { + "error": { + "type": "string" + }, + "estimate": { + "additionalProperties": true, + "description": "Predicted items, bytes, tokens, credits, latency with confidence + source.", + "type": "object" + } + }, + "type": "object" +}
- Changed
liquid_fetch3 fields changed- added
Input schema / properties / adapter_id / descriptionAdded value: +"An adapter id returned by liquid_connect (or listed by liquid_list_adapters)." - added
Input schema / properties / endpoint / descriptionAdded value: +"Optional endpoint path to act on (e.g. \"/users\"); defaults to the adapter's primary endpoint. Use a path shown by liquid_connect / liquid_list_adapters." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "properties": { + "_meta": { + "additionalProperties": true, + "description": "Call metadata: adapter_id, service, endpoint, latency_ms (and records when applicable).", + "type": "object" + }, + "data": { + "description": "Mapped records (a list, capped at 100; or a single object)." + }, + "error": { + "type": "string" + }, + "records": { + "description": "Number of records returned.", + "type": "integer" + } + }, + "type": "object" +}
- Changed
liquid_list_adapters1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "properties": { + "adapters": { + "description": "Connected adapters with adapter_id, service, url, endpoints.", + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + "error": { + "type": "string" + } + }, + "type": "object" +}
- Changed
liquid_query8 fields changed- added
Input schema / properties / adapter_id / descriptionAdded value: +"An adapter id returned by liquid_connect (or listed by liquid_list_adapters)." - added
Input schema / properties / agg / descriptionAdded value: +"Aggregate-mode: aggregations per group as field -> op, e.g. {\"price\": \"sum\", \"id\": \"count\"}. Provide together with group_by." - added
Input schema / properties / endpoint / descriptionAdded value: +"Optional endpoint path to act on (e.g. \"/users\"); defaults to the adapter's primary endpoint. Use a path shown by liquid_connect / liquid_list_adapters." - added
Input schema / properties / fields / descriptionAdded value: +"Search-mode projection: target_model field names to return, e.g. [\"name\", \"price\"]. Omit for all fields." - added
Input schema / properties / group_by / descriptionAdded value: +"Aggregate-mode: target_model field to group by, e.g. \"category\"." - added
Input schema / properties / limit / descriptionAdded value: +"Search-mode max records to return (default 100)." - added
Input schema / properties / where / descriptionAdded value: +"Search-mode filter as field -> value (or field -> {op: value}), e.g. {\"status\": \"active\", \"price\": {\"gt\": 100}}. Keys are target_model fields." - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": true, + "properties": { + "_meta": { + "additionalProperties": true, + "description": "Call metadata: adapter_id, service, endpoint, latency_ms (and records when applicable).", + "type": "object" + }, + "data": { + "description": "Search mode: matching records (capped at 100)." + }, + "error": { + "type": "string" + }, + "records": { + "description": "Search mode: number of records.", + "type": "integer" + }, + "result": { + "description": "Aggregate mode: the grouped/aggregated result." + } + }, + "type": "object" +}
6 tool updates
v0.1.0- First observed
liquid_connect - First observed
liquid_discover - First observed
liquid_estimate - First observed
liquid_fetch - First observed
liquid_list_adapters - First observed
liquid_query
TDQS
Scored across 6 tools
Each tool targets a distinct lifecycle phase: discover previews an API, connect persists an adapter, list_adapters shows existing adapters, estimate preflights, fetch pulls records, and query filters/aggregates. The descriptions explicitly cross-reference when to use each tool, so there is minimal ambiguity between fetch/query and connect/discover.
All tools share a consistent 'liquid_' prefix, lowercase snake_case, and action-oriented verbs. Minor inconsistency: liquid_list_adapters uses the verb_noun form while the others are bare verbs like connect, fetch, and query, but the pattern is still predictable and easy to follow.
Six tools is a well-scoped size for this server. Each tool earns its place and together they cover the adapter lifecycle from discovery through connection, listing, estimation, and data access without redundant or filler tools.
The workflow discover → connect → list → fetch/query/estimate covers the core read-focused API integration domain thoroughly, with no dead ends. The only notable gap is adapter lifecycle management: there is no update or delete operation for modifying target_model or removing stored credentials.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
AI service marketplace — agents discover, call, and pay for API services automatically.
Give your agent live data from Twitter, Reddit, the web and GitHub. No API keys, no scraping stack.
SaaS intelligence for AI agents. 5 unified tools cover 1,000+ services with 91-96% token savings.
Verified, pay-per-use API tools for AI agents through one authenticated connection.
Related MCP Servers
AlicenseAqualityCmaintenanceEnable AI agents to work reliably - giving them secure access to structured data, tools to take action, and the context needed to make smart decisions.851,506424MIT- AlicenseAqualityBmaintenanceThe API layer for AI agents. World's biggest API index with 22,000+ APIs and growing. Agents discover and call APIs at runtime with semantic search, structured metadata, and 18 Direct Call APIs including AI providers.147738MIT

riocloud-readerofficial
AlicenseNot gradedqualityDmaintenanceEnables AI agents to fetch and digest content from 30+ platforms (Twitter, YouTube, Reddit, etc.) via a unified API. Supports multi-format output, transcription, and direct Obsidian sync.18BSD 2-Clause "Simplified"- AlicenseNot gradedqualityAmaintenanceEnables AI agents to discover, search, and call any REST API described by an OpenAPI or Swagger document. Supports multiple API endpoints with authentication and parameter handling.9MIT