In recent years, AI—especially large language models (LLMs)—has become a powerful assistant in our daily lives. In the realm of time series data management, LLMs are also showing promise in accelerating data querying and analysis. A key question emerges: how can we enable LLMs to interact more deeply with time series databases and lower the barrier to tapping into their capabilities?
This is where the Model Context Protocol (MCP) comes into play. With MCP, we're seeing a new wave of integration between AI and databases. Based on this protocol, we’ve developed the Apache IoTDB MCP Server, which allows LLMs to directly interact with IoTDB—eliminating the need to manually translate natural language into SQL and execute it separately. Now, LLMs can fetch data from IoTDB directly, bringing a streamlined experience to time series data access.
What Is MCP? A Gateway Between LLMs and the Outside World
Model Context Protocol (MCP) is a standardized interface proposed by Anthropic that empowers LLMs to interact with external systems and tools. You can think of it like a power adapter for AI—just as you need a plug adapter to use your charger in a different country, MCP serves as the "adapter" that connects LLMs with external functionalities like databases, APIs, and more.
Here’s how it works: developers define tool functions in a language like Python and provide natural language descriptions for those functions and their parameters. When an LLM receives a task, it can understand the context, call the right function with the appropriate parameters, and return the result to the user. In effect, it gives LLMs a set of "eyes and hands" to operate the external world.
Why MCP Matters
Previously, LLMs were limited to text generation and processing based on static training data. If you wanted to query a database, you had to write SQL yourself or use third-party tools.
With MCP, the game changes. Now, you can ask in plain text—something like “Show me the turbine performance for today”—and the LLM can automatically invoke a database function to perform the query and return real-time results. This drastically lowers the entry barrier, making advanced time series analysis accessible to non-technical users.
Key Benefits of MCP
Real-time Access: Overcomes the static nature of training data by enabling access to live databases.
Standardized Interface: Replaces fragmented Function Call mechanisms with a unified tool calling framework.
Security: Keeps credentials isolated from the LLM itself—only the MCP server handles sensitive access control.
Apache IoTDB MCP Server: Let LLMs Speak IoTDB
What Is It?
Apache IoTDB MCP Server is an open-source implementation of the MCP protocol that connects large language models with Apache IoTDB—a time series database purpose-built for IoT data. It allows LLMs to query and analyze massive time series datasets in a secure, structured, and intelligent way.
📌 GitHub Repository: https://github.com/apache/iotdb-mcp-server
Core Features
Query Tools
read_query(sql: str)
This is the primary interface for reading data from IoTDB. It accepts a standard SELECT
SQL statement as input and returns the query result as an array of JSON objects—each representing a row in the result set.
How it works in practice: Let’s say you want to check the temperature readings from a specific device over the past week. You don’t need to manually write a SQL statement. Instead, you can simply describe your need in natural language—for example:
“Get me the temperature data from device X for the past 7 days.”
The LLM will understand your request, generate the appropriate SQL (e.g., SELECT time, temperature FROM device_X WHERE time > now() - 7d
), and pass it to the read_query
function. The function executes the query and returns the results in a structured format.
This makes time series data querying far more intuitive and accessible—even for users without SQL knowledge.
Schema Tools
These tools help LLMs (and users) explore the database structure, which is critical for generating accurate queries.
list_tables()
This function returns a list of all table names currently available in the IoTDB instance. It does not require any parameters.
Example: Suppose a user doesn’t know what data is available in the system and asks:
“What tables are available in this database?”
The LLM can simply call list_tables()
and use the response to guide follow-up questions or recommendations—e.g., “You have a table called battery_data
that stores power system readings.”
describe_table(table_name: str)
This function returns the schema definition of a specified table. It requires the table name as input and returns metadata including column names, data types, and semantic tags like TIME, TAG, or FIELD.
Example: A user might ask:
“What kind of data is stored in the
battery_data
table?”
The LLM would invoke:
describe_table("battery_data")
The response might look like:
[
{"name": "time", "type": "TIMESTAMP", "role": "TIME"},
{"name": "station_id", "type": "STRING", "role": "TAG"},
{"name": "dc_voltage", "type": "FLOAT", "role": "FIELD"},
...
]
This schema information helps the LLM understand how to build correct SQL queries and provide intelligent suggestions for analysis.
How to Use the IoTDB MCP Server
Prerequisites
Python environment (with
uv
package manager:pip install uv
)A running Apache IoTDB instance
Download IoTDB 2.0.1-beta
Required dependencies for the MCP Server
Quick Start
Clone the Repository:
git clone https://github.com/apache/iotdb-mcp-server.git
cd iotdb_mcp_server
Create a Virtual Environment:
uv venv
source venv/bin/activate # (on Windows: venv\Scripts\activate)
Install Dependencies:
uv sync
4. Configure an MCP Client (Claude Desktop as an example)
Locate claude_desktop_config.json
:
macOS:
~/Library/Application Support/Claude/
Windows:
%APPDATA%/Claude/
Add the following block (replace YOUR_REPO_PATH
and update IoTDB credentials if needed):
{
"mcpServers": {
"iotdb": {
"command": "uv",
"args": [
"--directory",
"YOUR_REPO_PATH/src/iotdb_mcp_server",
"run",
"server.py"
],
"env": {
"IOTDB_HOST": "127.0.0.1",
"IOTDB_PORT": "6667",
"IOTDB_USER": "root",
"IOTDB_PASSWORD": "root",
"IOTDB_DATABASE": "test"
}
}
}
}
Launch Claude Desktop and Try It Out
Be sure to have some data ready. You can test the integration by inserting sample data using IoTDB’s CLI (with table mode):
create database test;
use test;
CREATE TABLE battery_data (
time TIMESTAMP TIME comment 'Time',
station_id STRING TAG comment 'Station ID',
dc_voltage FLOAT FIELD comment 'DC Voltage(V)',
load_current FLOAT FIELD comment 'Load Current(A)',
battery_current FLOAT FIELD comment 'Battery Group Current(A)',
float_voltage_set FLOAT FIELD comment 'Float Charging Voltage Set Value(V)',
equalize_voltage_set FLOAT FIELD comment 'Equalization Charging Voltage Set Value(V)',
rectifier_current FLOAT FIELD comment 'Rectifier Module Current(A)'
) comment 'Battery Data'
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742313600000,'b0001',50.88,69.36,0.0,50.9,51.1,82.48);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742313720000,'b0001',50.88,67.05,0.0,50.9,51.1,81.12);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742313840000,'b0001',50.88,67.37,0.0,50.9,51.1,81.24);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742313960000,'b0001',50.88,68.72,0.0,50.9,51.1,82.49);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742314080000,'b0001',50.88,67.27,0.0,50.9,51.1,80.24);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742314200000,'b0001',50.89,66.89,0.0,50.9,51.1,79.99);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742314380000,'b0001',50.88,69.53,0.0,50.9,51.1,83.23);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742314500000,'b0001',50.88,68.23,0.0,50.9,51.1,79.48);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742314620000,'b0001',50.88,69.76,0.0,50.9,51.1,83.36);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742314740000,'b0001',50.88,67.46,0.0,50.9,51.1,81.99);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742314860000,'b0001',50.88,67.39,0.0,50.9,51.1,80.98);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742314980000,'b0001',50.88,65.51,0.0,50.9,51.1,78.73);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742315100000,'b0001',50.88,66.11,0.0,50.9,51.1,80.49);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742315220000,'b0001',50.88,68.6,0.0,50.9,51.1,81.87);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742315340000,'b0001',50.88,66.42,0.0,50.9,51.1,78.23);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742315460000,'b0001',50.88,67.16,0.0,50.9,51.1,78.49);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742315580000,'b0001',50.88,66.6,0.0,50.9,51.1,78.36);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742315700000,'b0001',50.88,67.53,0.0,50.9,51.1,79.98);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742315820000,'b0001',50.88,66.69,0.0,50.9,51.1,79.73);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742315940000,'b0001',50.88,66.88,0.0,50.9,51.1,80.87);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742316060000,'b0001',50.88,67.0,0.0,50.9,51.1,79.73);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742316180000,'b0001',50.88,65.61,0.0,50.9,51.1,79.86);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742316300000,'b0001',50.89,61.51,0.0,50.9,51.1,73.98);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742316420000,'b0001',50.89,62.38,0.0,50.9,51.1,75.86);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742316540000,'b0001',50.89,60.35,0.0,50.9,51.1,73.23);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742316720000,'b0001',50.89,60.22,0.0,50.9,51.1,72.61);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742316840000,'b0001',50.89,60.88,0.0,50.9,51.1,73.98);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742316960000,'b0001',50.89,59.95,0.0,50.9,51.1,72.61);
insert into battery_data(time,station_id,dc_voltage,load_current,battery_current,float_voltage_set,equalize_voltage_set,rectifier_current) values(1742317080000,'b0001',50.89,61.09,0.0,50.9,51.1,74.11);
Example Use Cases
1. Natural Language Querying
Input: “Show me the average rectifier current for all stations on April 16, 2025”
LLM Output:
read_query("SELECT AVG(rectifier_current) as average_rectifier_current FROM battery_data WHERE time >= 2025-03-19T00:00:00 AND time < 2025-03-20T00:00:00")
Below is a snapshot of the LLM generating the corresponding SQL query and retrieving data via MCP:
2. Schema Exploration
Input: “What tables are in the database of IoTDB?”
LLM Output:
list_tables()
Here’s how the LLM lists all available tables through MCP interaction:
3. Table Structure Description
Input: “Describe the structure of the battery_data table”
LLM Output:
describe_table("battery_data")
This screenshot shows the LLM describing the schema of a specified table using MCP:
Final Thoughts
MCP introduces a new paradigm for making large language models truly interactive—able to work with live systems instead of static training data. The Apache IoTDB MCP Server brings this paradigm to the world of time series databases, providing a standardized way to connect LLMs with your IoT data.
As the ecosystem matures, we believe MCP will become a foundational bridge between AI and IoT, accelerating the intelligent management of time series data across industries.
We invite developers and enterprises alike to explore the Apache IoTDB MCP Server and unlock the full potential of natural language interfaces in time series analytics.