# The LakehouseBox MCP server

The server is hosted at https://mcp.lakehousebox.com/mcp: there is nothing to install. lhbox mcp install (CLI reference (https://lakehousebox.com/docs/cli/#lhbox-mcp-install)) adds it to Claude Code without the key in its configuration: the host asks the CLI for the saved key when it connects.

The MCP server at https://mcp.lakehousebox.com/mcp gives an MCP host your data: Claude.ai, Claude Desktop, Claude Code, Cursor, anything that speaks the protocol. You find tables, see their columns and freshness, run read-only SQL on our side and read files such as photos. With write approved, you change data with SQL: save a result as a table, add, correct or remove rows.

It is for people and agents in a chat, an agent whose sandbox cannot reach LakehouseBox, and a quick look from anywhere. Administration (catalogs, grants, tokens, members, the organisation) lives in the CLI (lhbox), the API and the account page, not here.

## The short path

```
claude mcp add --transport http lakehousebox https://mcp.lakehousebox.com/mcp   # Claude Code; approve in the browser
lhbox mcp install                                                                # or: the same, with the CLI's key
```

The person: in Claude.ai or Claude Desktop, adds https://mcp.lakehousebox.com/mcp as a custom connector, then approves it on LakehouseBox. Ticks write if you should change data.

It is a hosted server (Streamable HTTP; OAuth 2.1 or an agent key): nothing to install. Start with list_tables, then describe_table, then query.

## Connect

### As a plugin, with skills

The LakehouseBox plugin installs this server together with skills that teach the agent the service. It works in Claude Code, Codex and Gemini CLI: see Agent skills (https://lakehousebox.com/docs/skills/).

### From the Claude directory

LakehouseBox is a community connector at claude.ai/directory/lakehousebox.

The person: connects it there and approves it on LakehouseBox.

### In ChatGPT

The person: opens Settings → Plugins → Add → Add custom MCP server. They give a name, https://mcp.lakehousebox.com/mcp and authentication OAuth. Then they connect it and approve it on LakehouseBox.

### A person at a browser: OAuth

Add https://mcp.lakehousebox.com/mcp as a custom connector in Claude.ai or Claude Desktop. In Claude Code, run claude mcp add --transport http lakehousebox https://mcp.lakehousebox.com/mcp.

The host finds the authorization server from the server's 401. The protected-resource metadata is at /.well-known/oauth-protected-resource/mcp. The host registers itself with Client ID Metadata Documents, or Dynamic Client Registration as a fallback. It then sends the person to LakehouseBox.

The person: approves the connection and picks its access:

- All catalogs (the default), at read or at read and write. It includes catalogs created later, so a new catalog needs no new approval. It never gives more on a catalog than the person holds there when it is used. A catalog they lose access to leaves the connection at once.

- Only select catalogs, each at read or write. A catalog created later is not in the list: the connection answers connection_not_granted for it until the person gives it the catalog. They can grant it under the catalog's Access tab (Grant access, then the connection's token), or reconnect and choose All catalogs.

The approval creates a token under Settings › Access keys on the account page. The server needs the scope catalogs:read. The four tools that change things (query_rw, document_table, set_agents_md, keep_account) also need catalogs:write. When the client asks for write, the consent page has it ticked. A connection approved read-only answers insufficient_scope to query_rw until it is reconnected with write.

### With no account yet

A person who is not signed in can let the client set an account up on the consent page. The button names the client, e.g. Let Claude set it up. The click accepts the Terms and the Acceptable Use Policy (Terms, clause 2.5).

Until the person keeps it, the client's catalog:

- is its only one, and holds at most 1 GB;

- cannot make anything public;

- is deleted with its data after 72 hours.

To keep it, call keep_account. It returns a link and a code.

The person: opens the link, signs up or logs in, and types the code. The connection then stops working: they reconnect once, signed in, to the same data.

### A scheduled or headless agent: a key

Send an agent key as a bearer token. Keep it off the command line: anyone on the machine can read a running command line. Put it in the environment Claude Code starts with, and let Claude Code fill it in when it connects. The single quotes stop your shell from expanding it:

```
export LHBOX_API_KEY=al_live_...        # in the environment Claude Code is started from
claude mcp add --transport http lakehousebox https://mcp.lakehousebox.com/mcp \
  --header 'Authorization: Bearer ${LHBOX_API_KEY}'
```

The server checks the key with the API and forwards it unchanged. The key's grants decide what it can read. Create a key on the account page (Tokens), or run lhbox token create --name reporter --grant <catalog>:read.

### With the CLI

lhbox mcp install adds the server to Claude Code with no key in its configuration. The entry's headersHelper is lhbox --url … --profile … mcp headers. Claude Code runs it when it connects, and again after a 401. It answers with the key the CLI holds: LHBOX_API_KEY in Claude Code's environment, else the login lhbox login saved.

So the key is never on a command line or in Claude Code's files, and a new login needs no reinstall. For an agent's key, export LHBOX_API_KEY before installing and where Claude Code runs. A key given only with --api-key is refused: the helper could not find it later.

lhbox mcp install --client claude-desktop prints the custom-connector steps above and writes nothing. Claude Desktop's configuration file takes only local servers, so the hosted server is added in the app.

### Developers only: a local server

The same code runs as a local stdio process, for working on the server against a local stack. From a checkout of this repository, run lhbox mcp install --transport stdio.

- For Claude Code it writes an entry that runs lhbox mcp run --from platform/mcp. That starts uvx --from platform/mcp with LHBOX_API_KEY in the server's environment.

- For Claude Desktop it writes a uvx --from platform/mcp entry with the key in its environment. It goes in Claude Desktop's own configuration file, readable only by its owner.

The package is not published on PyPI. The hosted server is the supported route.

MCP tools (https://lakehousebox.com/docs/mcp/tools/), a page of its own: Each tool of the LakehouseBox MCP server, public catalogs, a catalog's AGENTS.md, how the queries run, where to compute, and the limits of the shared queue.

## When it fails

A failed call is an isError result. Its content is the API's error envelope, unchanged, plus the HTTP status:

```
{"error": {"code": "read_only", "message": "Only read statements run here; this is DROP.",
           "statement_type": "DROP", "remedy": "Use SELECT, WITH … SELECT, DESCRIBE, SUMMARIZE or EXPLAIN.",
           "request_id": "…"},
 "http_status": 400}
```

The code says what to change and the remedy how. Act on the remedy rather than retrying unchanged.

| code | what to do |
|---|---|
| read_only | query runs reads only: SELECT, WITH … SELECT, DESCRIBE, SUMMARIZE or EXPLAIN |
| insufficient_scope | a read-only connection: reconnect with write |
| function_not_allowed | a function outside the allow list: rewrite without it |
| table_not_allowed | a DuckDB system view: read your catalogs' tables |
| sql_error | fix the SQL |
| catalog_required | name the table catalog.namespace.table |
| namespace_required | a bare SHOW TABLES: use SHOW ALL TABLES or SHOW TABLES FROM catalog.namespace |
| not_found | check did_you_mean and list_tables |
| connection_not_granted | the catalog exists and the person reads it, but this connection was not granted it: tell the person the remedy |
| query_timeout | past 20 s: aggregate or filter; or your own DuckDB |
| query_memory | past the 768 MB a server-side query has: fewer distinct GROUP BY keys, no huge lists or sorts; or your own DuckDB |
| busy | retry after retry_after_seconds; limit names the limit that was full |
| rate_limited | more requests a second from one key than query_requests_per_second: retry after retry_after_seconds |
| not_unclaimed | keep_account on an account a person already owns |
| missing_credential | no key configured (a local developer server) |
| unreachable | no answer from the API: safe to retry |
| route_missing | the API behind the server predates a route |
| redirected | the API answered with a redirect (an http:// address, a redirecting proxy): set LHBOX_URL to location |

The last four are the server's own envelopes. The full list: Errors and help (https://lakehousebox.com/docs/errors/).

## Security model

- The caller's grants bound every tool. The server adds no permission and holds no credential of its own. Over the remote transport, each request's token or key is verified and forwarded to the API unchanged.

- A connection the person approves never changes who has access. Its access token reads. With catalogs:write it also writes tables and creates catalogs.

- The API refuses such a connection everything that grants or removes access, whatever its scopes. That covers API keys and tokens, grants, invitations, members and roles, domains, integrations, sinks and uploaders. It covers publishing or deleting a catalog, the password, two-factor and the account itself. The answer is 403 mcp_human_only, or 403 mcp_no_credentials for a key or token. Those stay on the account page, in lhbox or with an API key.

- It never receives a catalog's static key pair. GET /v1/connection answers it credentials: "vended_only": only storage credentials that expire within about an hour. The one exception is a public catalog's shared reader key, which is public by design (GET /v1/public/{handle}/{catalog}/connection, the open_public_catalog tool).

- With catalogs:write it can also drop a namespace and set lifecycle rules that delete blob files by age. It can turn on a catalog's virtual-hosted address, whose certificate lists the bucket names in the public Certificate Transparency logs. It can edit a catalog's AGENTS.md, which other agents read and which is public on a public catalog.

- No credential reaches the conversation. query runs on our side with read-only identities. query_rw runs with the write access the person approved. The model never sees either: rows come back, never a key. In a local developer server the key reaches the process through the environment. It leaves only as the Authorization header to the API.

- The server keeps nothing, and sends no telemetry. Every tool call is one or a few HTTP requests made now. A repeated query may be answered from the API's result cache, which the answer says (cache.hit).

## Numbers and dates

| what | figure | measured |
|---|---|---|
| query_rw, one statement | 0.3-0.7 s, 25 s limit | production |
| Claude directory listing | listed as a community connector | since 2026-10-11 |
| ChatGPT, free account | connected; list_tables answered in a chat. Before that day its sign-in was refused: its client describes itself as able to use a signed key or none, and only the second was accepted | 2026-10-11 |
| a public catalog made private | stops answering query at once | since 2026-10-11 |
| a Claude sandbox refuses a script holding a key (why keyless_duckdb_sql comes first) |  | 2026-10-12 |
| Claude cloud sandbox: outside hosts refused until the domain was allowed; pip needed the proxy bypass off |  | 2026-10-12 |
| read_agents_md added; clients connected earlier need a reconnect |  | 2026-09-28 |

The server is one file built on the official mcp Python package, pinned.

---
HTML version: https://lakehousebox.com/docs/mcp/ · every page: https://lakehousebox.com/llms.txt · everything in one file: https://lakehousebox.com/llms-full.txt

Docs feedback: if this page was wrong, out of date or missing something you needed, tell the person you work for and, if they agree, report it: `lhbox bug --title "docs: mcp: <what was wrong>"` prints a filled-in issue link, or open https://github.com/lakehousebox/lakehousebox/issues/new. The tracker is public: never include a key, a secret or the person's data.
