duckdb-iceberg-mcp
Allows querying Apache Iceberg tables stored on Amazon S3 via AWS Glue Data Catalog using DuckDB as the query engine.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@duckdb-iceberg-mcpWhat is the average latency per endpoint in the last hour?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
duckdb-iceberg-mcp
An MCP server that lets AI assistants query Apache Iceberg tables on S3 via AWS Glue Data Catalog. DuckDB is the embedded query engine — Apache Arrow columnar format, vectorized execution, direct S3 reads with no data movement.
Built on the official Python MCP SDK.
This MCP server is complementary to telemetry-iceberg-adaptor, which ingests telemetry data into Apache Iceberg. Use that project to write data and this project to query it through MCP-enabled AI clients.
Architecture
+----------------------+
| MCP Clients |
| - OpenAI Codex |
| - Claude Desktop |
| - OpenCode |
| - LibreChat |
+----------------------+
|
| MCP Protocol (tools/list · tools/call)
v
+--------------------------------------------------------------------------------------------+
| duckdb-iceberg-mcp |
| |
| +----------------------------------------------------------------------------------------+ |
| | MCP Protocol Layer | |
| | stdio · Streamable HTTP · SSE | |
| | JWT auth · write guard · row/char limits | |
| +----------------------------------------------------------------------------------------+ |
| <--> |
| +----------------------------------------------------------------------------------------+ |
| | ⚡ DuckDB | |
| | Apache Arrow columnar engine | |
| | Vectorized execution · Direct S3 reads | |
| | httpfs · iceberg · aws extensions | |
| +----------------------------------------------------------------------------------------+ |
+--------------------------------------------------------------------------------------------+
| |
| httpfs extension | boto3
| columnar Parquet reads | metadata · schema
| | Iceberg manifest resolution
v v
+--------------------------------------------------------------------------------------------+
| AWS |
| +-------------- Amazon S3 --------------+ +------------ AWS Glue Data Catalog ----------+ |
| | Apache Iceberg tables | | Databases · Tables · Schema | |
| | Parquet data files | | Iceberg metadata | |
| +---------------------------------------+ +---------------------------------------------+ |
+--------------------------------------------------------------------------------------------+
Related MCP server: DuckDB MCP Server
Features
Query Iceberg tables on S3 via AWS Glue Data Catalog
Three transports:
stdio, Streamable HTTP, SSEEasy mode — single-tenant, optional static API key, no IdP required
Full mode — JWT/JWKS token validation (Auth0, Cognito, Keycloak, Okta, …)
Writes disabled by default; only available in full mode with explicit opt-in
Configurable row and character limits to prevent runaway responses
Requirements
Python 3.12+
AWS credentials with access to Glue Data Catalog and S3
Installation
git clone <repo>
cd duckdb-iceberg-mcp
python -m venv .venv
source .venv/bin/activate
pip install -e .Quick Start
stdio (local AI client)
Copy the example config and fill in your AWS details:
cp config/easy.env.example .env# .env
MCP_MODE=easy
MCP_TRANSPORT=stdio
CATALOG_TYPE=glue
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...Run directly:
duckdb-iceberg-mcpOr configure in your MCP client (Claude Desktop, OpenAI Codex):
{
"mcpServers": {
"duckdb-iceberg-mcp": {
"command": "/path/to/.venv/bin/duckdb-iceberg-mcp",
"env": {
"MCP_MODE": "easy",
"CATALOG_TYPE": "glue",
"AWS_REGION": "us-east-1",
"AWS_ACCESS_KEY_ID": "AKIA...",
"AWS_SECRET_ACCESS_KEY": "..."
}
}
}
}Streamable HTTP (network clients, e.g. LibreChat in Docker)
MCP_MODE=easy
MCP_TRANSPORT=http
MCP_HOST=0.0.0.0
MCP_PORT=8766
MCP_ALLOWED_HOSTS=host.docker.internal:*,localhost:*
CATALOG_TYPE=glue
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...env $(grep -v '^#' .env | grep -v '^$' | xargs) .venv/bin/duckdb-iceberg-mcpMCP client URL: http://localhost:8766/mcp
LibreChat (librechat.yaml):
mcpServers:
duckdb-iceberg-mcp:
type: streamable-http
url: 'http://host.docker.internal:8766/mcp'
timeout: 60000
initTimeout: 20000OpenAI Codex: add via the Codex UI — Streamable HTTP, URL http://localhost:8766/mcp.
AWS Authentication
Configure credentials using one of these methods (key/secret takes priority if both are set):
Method | Env vars |
Explicit credentials |
|
Named profile |
|
Default chain | Set neither — falls back to env vars, |
MCP Tools
list_tables(database?)
Lists Glue catalog tables. Optionally filter by database name.
describe_table(table_name)
Returns column names, types, and partition keys. Use database.table format.
glue_table(table_name)
Registers a Glue Iceberg table as a queryable DuckDB view. Required for tables where data files live outside the Glue-registered table root (a common layout with shared S3 prefixes).
glue_table('mydb.mytable')
→ Registered view 'mydb__mytable'. Query with: SELECT * FROM mydb__mytablequery_lakehouse(sql)
Executes a SQL query against registered views or direct S3 paths (read_parquet(), iceberg_scan()).
Configuration Reference
Variable | Default | Description |
|
|
|
|
|
|
|
| Bind address for HTTP/SSE |
|
| Port for HTTP/SSE |
| (empty) | Comma-separated allowed |
| (empty) | Static bearer token for easy mode HTTP. Empty = no auth |
| (required in full mode) | JWKS endpoint for JWT validation |
|
| Expected |
|
| Only |
|
| AWS region |
| (empty) | Named AWS profile |
| (empty) | AWS access key |
| (empty) | AWS secret key |
|
|
|
|
| Maximum rows returned per query |
|
| Maximum characters in a query response |
Full Mode (JWT Auth)
Full mode validates a JWT bearer token on every request. All authenticated users share one DuckDB connection — per-user session isolation is deferred to a future release.
MCP_MODE=full
MCP_TRANSPORT=http
MCP_HOST=0.0.0.0
MCP_PORT=8766
JWKS_URL=https://your-idp.example.com/.well-known/jwks.json
JWT_AUDIENCE=duckdb-iceberg-mcp
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...The client passes a JWT as Authorization: Bearer <token>. The server validates it against the JWKS endpoint. Any IdP that issues standard JWTs works (Auth0, AWS Cognito, Keycloak, Okta).
See config/full.env.example for a full template.
Smoke Test
Run a quick end-to-end check against your real Glue catalog:
cp config/easy.env.example .env # fill in AWS credentials
.venv/bin/python scripts/smoke_test.pyRun Tests
pip install -e ".[dev]"
pytestAvailable Tools
4 toolsdescribe_tableB
Describe a table's schema. Use 'database.table' format for Glue.
| Name | Required | Description | Default |
|---|---|---|---|
| table_name | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, and the description only states the purpose. It does not disclose behavioral traits beyond being a read operation, such as error handling or required permissions.
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, no wasted words. The purpose and a key usage hint are front-loaded.
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?
The tool has only one parameter and an output schema, reducing the need for extensive description. However, without annotations, the description lacks safety and error context, making it minimally complete.
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?
The schema coverage is 0%, so the description must compensate. It adds a formatting hint for table_name, which adds meaning beyond the schema's title, but does not provide full semantics.
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 states it describes a table's schema, which is a specific verb+resource. It does not explicitly differentiate from sibling tools like glue_table, but the purpose is clear.
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 gives a formatting hint for the parameter ('Use 'database.table' format for Glue'), but does not provide guidance on when to use this tool versus siblings or when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
glue_tableA
Register a Glue Iceberg table as a queryable DuckDB view.
Resolves the actual data files from the Iceberg manifest (handles tables where data files live outside the Glue-registered table root).
Usage: glue_table('database.table_name') After calling this, query with: SELECT * FROM database__table_name
| Name | Required | Description | Default |
|---|---|---|---|
| table_name | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so description carries full burden. It discloses that the tool resolves actual data files from Iceberg manifest and handles non-standard data locations. It also indicates the side effect of creating a view. 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?
The description is six lines, each adding unique value: main action, behavioral details, usage syntax, and post-call query example. No redundant or filler content.
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 complexity of registering a view and the presence of an output schema (though not shown), the description covers the essential behavioral and usage aspects. It could mention what the tool returns, but output schema likely fills that gap.
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?
With 0% schema description coverage, the description compensates by specifying the parameter format 'database.table_name'. The input schema only has a title 'Table Name', so description adds significant meaning.
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 registers a Glue Iceberg table as a queryable DuckDB view, with specific verb 'Register' and resource 'Glue Iceberg table'. It also explains it resolves data files from Iceberg manifest, distinguishing it from siblings like describe_table or list_tables.
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 syntax 'glue_table('database.table_name')' and post-call query example 'SELECT * FROM database__table_name'. It implicitly guides when to use this tool, but does not explicitly state when not to use or mention alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_tablesA
List available tables. Optionally filter by database name.
| Name | Required | Description | Default |
|---|---|---|---|
| database | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It states the main action and filtering option but does not disclose any behavioral traits such as permissions, performance implications, or edge cases (e.g., empty result). It is minimally adequate for a simple listing tool.
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 extremely concise with a single sentence that front-loads the main action. Every word earns its place, with no superfluous text.
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 simplicity (1 optional param, output schema exists), the description is fairly complete. It covers the core functionality and filtering. However, it could clarify the scope of 'tables' (e.g., database tables versus other entities) to enhance completeness.
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?
The schema coverage is 0%, so the description must add meaning. It explains that the 'database' parameter is optional and filters results, which adds value beyond the schema. However, it does not specify the expected format or behavior when the parameter is empty.
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 action ('List available tables') and resource, and mentions optional filtering. It is distinct from sibling tools like describe_table and query_lakehouse.
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?
No guidance on when to use this tool versus alternatives like describe_table or query_lakehouse. Implicit usage context is minimal.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_lakehouseA
Execute a SQL query. Use read_parquet() or iceberg_scan() for S3 paths, or use the glue_table tool first to register a Glue table by name.
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It only states 'Execute a SQL query' without detailing important traits like whether it is read-only, destructive, authentication requirements, rate limits, or error handling.
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 concise, with two sentences that front-load the purpose and provide actionable tips. No unnecessary words.
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 has a single parameter and an output schema (so return format need not be described), the description is fairly complete. It covers the main action and usage hints, but could add minor details about potential errors or timeouts.
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?
The input schema has one parameter (sql) with no description, but the tool description adds meaning by specifying it should be a SQL query and suggests using certain functions for different data sources. This compensates for the lack of schema documentation.
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 executes a SQL query, which is the primary action. It differentiates from siblings by suggesting a workflow involving glue_table for Glue tables, but could be more explicit about what distinguishes this tool from similar ones.
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 usage guidance: use read_parquet() or iceberg_scan() for S3 paths, or use glue_table first for Glue tables. This gives context on how to use the tool effectively, though it does not explicitly state when not to use it or list alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
TDQS
Each tool has a distinct purpose: describe_table shows schema, glue_table registers a view, list_tables enumerates tables, and query_lakehouse executes SQL. No overlap in functionality.
Most names follow verb_noun (describe_table, list_tables, query_lakehouse), but 'glue_table' is a noun_noun that breaks the pattern, introducing inconsistency.
Four tools cover the essential operations for an Iceberg query server: listing, describing, registering, and querying tables. This is minimal but well-scoped for the stated domain.
The server supports read and schema-discovery operations but lacks write or lifecycle management tools (e.g., create, delete, update). This is acceptable for a query-focused tool but leaves gaps for full table management.
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
Query BigQuery, Snowflake, Redshift & Azure Synapse with natural language
Your Databricks Lakehouse in natural language: run SQL on your SQL warehouses, track long-running qu
Query 40 databases from Claude, ChatGPT, or Cursor — on any device. Read-only, encrypted, audited.
Query your warehouse or a CSV with Claude/ChatGPT over MCP, governed by table-level ACL + audit.
Related MCP Servers
- FlicenseBqualityDmaintenanceA Model Context Protocol server that provides a SQL interface for querying and managing Apache Iceberg tables through Claude desktop, allowing natural language interaction with Iceberg data lakes.18
- AlicenseNot gradedqualityDmaintenanceA Model Context Protocol server implementation that connects AI assistants to DuckDB, enabling them to query and analyze data from various sources including CSV, Parquet, JSON, and cloud storage through SQL.18MIT
- AlicenseBqualityDmaintenanceEnables AI assistants and IDEs to execute SQL queries on local DuckDB databases, in-memory databases, or cloud-stored databases with support for flexible connections and configurable result limits.11MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to query S3 data lakes using natural language, with support for CSV, JSON, Parquet and tools for data discovery, analysis, and metadata exploration.1MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ys1173/duckdb-iceberg-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server