Content
# 🧠 100% Local MCP Client + SQLite Server (LlamaIndex + Ollama + Qwen2.5 / DeepSeek-R1)
## 🧩 Technology Stack and Principle Explanation
This project implements a **fully local MCP (Model Context Protocol) client and server system**.
---
### 🚀 Technology Stack
* **LlamaIndex**: Used to build agents based on the MCP protocol (FunctionAgent).
* **Ollama**: Provides a local large language model (recommended to use `Qwen2.5:7b-instruct`, DeepSeek-R1 is only for compatibility testing).
* **LightningAI**: Responsible for running and hosting workflows (optional, remote hosting is not enabled during local runs).
* **SQLite**: A lightweight local database used as a demonstration backend.
* **MCP Protocol**: Implements a standard communication mechanism between Host ↔ Client ↔ Server (local SSE).
---
### ⚙️ Workflow Principles
1. The user inputs a natural language query;
2. The agent (FunctionAgent) determines whether to call a tool based on the prompt and tool description;
3. The MCP client connects to the MCP server via SSE;
4. The server provides tools (such as `add_data` and `read_data`) and executes the corresponding SQL;
5. The execution result is sent back to the agent;
6. The model generates the final natural language response based on the context.
---
### 🧠 Overall Project Architecture

---
### 📘 Overview of Implementation Steps
| Step | Content | Description |
| ---- | --------------------- | ----------------------------------------------- |
| #1 | Build SQLite MCP Server | Provides two basic tools: add data / query data |
| #2 | Set up LLM | Use Ollama to call the local model (recommended Qwen2.5:7b) |
| #3 | Define System Prompt | Guides the agent on how to determine and use MCP tools |
| #4 | Define Agent | Wraps MCP tools as FunctionAgent using LlamaIndex |
| #5 | Define Agent Interaction | Manages user input, streaming events, and tool calls |
| #6 | Initialize MCP Client and Agent | Loads tools and establishes an SSE connection with the server |
| #7 | Run Agent | User interaction → Agent decision → Tool execution → Natural language output |
---
This project demonstrates a **fully local** minimal viable example:
* ✅ **MCP Server**: Exposes database read/write tools (based on SQLite)
* ✅ **MCP Client**: Wrapped as LlamaIndex FunctionAgent
* ✅ **LLM**: Calls the local model via Ollama (recommended `qwen2.5:7b-instruct`)
The entire process is completed on the local machine without the need for external APIs.
---
## 📁 Project Structure
```
local-mcp-demo/
├── README_zh.md # Running instructions (this file)
├── requirements.txt
├── server/
│ └── server.py # #1 SQLite MCP server (SSE / stdio either)
└── client/
├── ollama_client.py # #2~#7 MCP client + LlamaIndex agent
└── system_prompt.txt # #3 System prompt (defines tool usage strategy)
```
---
## ⚙️ Environment Preparation
### 1️⃣ Python Environment
* Python **3.10+**
* It is recommended to use a virtual environment
```bash
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
```
> If the network is slow in China, you can use the Tsinghua mirror:
>
> ```
> pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
> ```
---
### 2️⃣ Install Ollama (Local Model Runtime)
1. Open your browser and visit [https://ollama.ai/download](https://ollama.ai/download)
2. Download the installation package for your platform and install it (Windows/macOS/Linux)
3. After installation, open the terminal to verify:
```bash
ollama --version
```
If the version number appears, the installation is successful.
---
### 3️⃣ Pull Models Supporting Function Calls (Very Important)
> ⚠️ **DeepSeek-R1 official model does not support Function Calling by default**
> Even the `1.5b` / `7b` versions may not trigger MCP tool calls automatically.
> **It is recommended to use the `qwen2.5:7b-instruct` model** (supports tool calls).
```bash
# Recommended model (supports Function Calling)
ollama pull qwen2.5:7b-instruct
# Can be replaced with other models that support function calls:
# ollama pull llama3.1:8b-instruct
# ollama pull mistral:7b-instruct
```
You can verify if the download was successful:
```bash
ollama list
```
> **Note:**
>
> * Although DeepSeek-R1:1.5b claims to support tool calls, it is not stable in actual tests;
> * The 7B version of DeepSeek-R1 has higher support under the same conditions but also consumes more resources;
> * The Qwen2.5:7b-instruct model has the best support.
---
## 🚀 Running Steps
### 🧩 Step 1. Start SQLite MCP Server
`server/server.py` implements two tools:
* `add_data(query: str) -> bool`: Executes `INSERT/UPDATE/DELETE`
* `read_data(query: str = "SELECT * FROM people") -> list`: Executes `SELECT`
#### Start Command:
```bash
cd server
python server.py --db ../demo.db --transport sse
```
Seeing the following output indicates success:
```
✅ SQLite DB: D:\Projects\MCP\demo.db
🚀 MCP SQLite server running on SSE http://127.0.0.1:8000/sse
```
> ✅ After starting, an example table `people(name, age, profession)` will be automatically created.
---
### 🧠 Step 2. Set Up LLM (Ollama)
The default model in `client/ollama_client.py` is:
```python
MODEL_NAME = "qwen2.5:7b-instruct"
```
If you have downloaded other models (such as DeepSeek-R1), you can modify it accordingly.
---
### 📜 Step 3. Define System Prompt
The `client/system_prompt.txt` defines the model's **roles and tool usage rules**. For example:
```
- When the user mentions "add"/"insert", call add_data;
- When the user mentions "query"/"get", call read_data;
- After a successful call, please return a concise result without repeating the call;
```
> Deleting this file or clearing its content will cause the model to be unable to determine when to call tools (see "Principle Explanation" below for details).
---
### 🤖 Step 4. Define Agent (FunctionAgent)
In `client/ollama_client.py`, it uses:
* `llama_index.tools.mcp` to wrap MCP tools as native LlamaIndex tools;
* `FunctionAgent` to build a function-calling agent.
The agent is responsible for:
* Deciding whether to call a tool;
* Integrating results after the call;
* Generating natural language responses.
---
### 💬 Step 5. Define Agent Interaction
`handle_user_message(...)`:
* Passes user input to the agent;
* Prints tool call events (`[Event] ToolCall -> ...`);
* Returns natural language results.
---
### ⚙️ Step 6. Initialize MCP Client and Agent
```python
mcp_client = BasicMCPClient("http://127.0.0.1:8000/sse")
mcp_tool = McpToolSpec(client=mcp_client)
tools = await mcp_tool.to_tool_list_async()
agent = FunctionAgent(tools=tools, llm=llm, system_prompt=SYSTEM_PROMPT)
```
---
### 🧑💻 Step 7. Start Client and Model Agent
Open another terminal while keeping the server running:
```bash
cd client
source ../.venv/bin/activate # Windows use .venv\Scripts\activate
python ollama_client.py
```
Example input:
```
Add to database: INSERT INTO people(name, age, profession) VALUES('Rafael Nadal', 39, 'Tennis Player')
```
Expected output (partial example):
```
[Event] AgentInput
[Event] AgentStream
[Event] ToolCall -> add_data
[Event] AgentOutput
Agent: Successfully added Rafael Nadal to the database.
```
Then input:
```
Get data
```
or:
```
Query: SELECT * FROM people
```
Output:
```
[Event] ToolCall -> read_data
Agent: Found 1 record:
- Rafael Nadal (39 years old, Tennis Player)
```
---
## 🪞 Common Issues and Solutions
| Issue | Cause | Solution |
| --------------------- | ----------------------------- | ---------------------------------------------- |
| Model keeps calling tools | No limit on loop count | Set `max_steps=3` in `FunctionAgent` |
| Model misjudges (does not call tools) | system_prompt deleted or model does not support tool calls | Restore system_prompt or use `qwen2.5:7b-instruct` |
| No data found | Tool did not execute (only outputs JSON) | Switch to a model that supports Function Calling |
| Reports "near '*'" SQL error | Model output contains full-width symbols / code fences | Clean SQL on the server side (see `_clean_sql` in `server.py`) |
| LLM outputs garbled Chinese | Ollama console character set issue | Use a UTF-8 terminal or VSCode terminal |
| Insufficient GPU memory | Model too large | Switch to a smaller parameter model (e.g., qwen2.5:1.8b) |
---
## 💡 Brief Technical Principles
* **MCP Server**: Wraps SQLite tools (add / read), exposed as a standard MCP interface (SSE / stdio).
* **MCP Client**: Communicates with the server via `BasicMCPClient`.
* **LlamaIndex Agent**: Receives user input → Calls local LLM → LLM determines whether and how to call tools.
* **System Prompt**: Guides model decisions (the "manual" for tool calls).
* **LLM (Ollama)**: Executes inference, outputs function calls or natural language.
> If `system_prompt.txt` is deleted, the model will lose the instructions for tool usage, thus unable to "autonomously determine" function calls.
---
## 🧩 Our Improvements and Experience Summary
* ✅ Self-built MCP Server for database access;
* ✅ FunctionAgent can automatically select `add_data` / `read_data` based on natural language intent;
* ✅ Qwen2.5:7b-instruct is the best compatible model;
* ⚙️ DeepSeek-R1 1.5b/7b cannot stably support Function Call under the same conditions;
* 🔁 Added limits on call steps and prompt restrictions to prevent infinite loops;
* 🧱 Future expansion of more tools (file read/write, knowledge base retrieval, etc.).
---
## 🧾 License
MIT License (free to use, modify, and extend)
## Star History
[](https://www.star-history.com/?utm_source=chatgpt.com#StephenCurry885/100-local-MCP-Client&type=date&legend=top-left)
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
servers
Model Context Protocol Servers
servers
Model Context Protocol Servers
Agent-Reach
Give your AI agent eyes to see the entire internet. Read & search Twitter,...