Skip to content

Commit e5c8c3c

Browse files
Merge pull request #26 from insightbuilder/db-mcp-client
Db mcp client
2 parents e39a58b + fc41547 commit e5c8c3c

3 files changed

Lines changed: 220 additions & 4 deletions

File tree

docs/mcp_excel_server.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ manipulating and analysing the excel files.
6767
file
6868
- remove_file: Removes a file from the system
6969

70+
The mcp client in this proect is enabled with
71+
SQLite db connection. The same has been discussed
72+
in this [video](https://youtu.be/FAMg9kZpMQw)
73+
7074
When you need use mcp inspector to debug the code,
7175
use the command below. Ensure you have
7276
[npx](https://docs.npmjs.com/cli/v8/commands/npx)

mcp_excel_server/mcpclient.py

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,69 @@ async def connect_to_server(self, server_script_path: str):
5858
[tool.name for tool in tools],
5959
)
6060

61+
async def create_or_connect_db(self):
62+
import sqlite3
63+
64+
# Connect to SQLite (creates a file called chatbot.db)
65+
conn = sqlite3.connect("mcphistory.db")
66+
cursor = conn.cursor()
67+
68+
# Create a table to store query-response pairs
69+
cursor.execute("""
70+
CREATE TABLE IF NOT EXISTS conversations (
71+
id INTEGER PRIMARY KEY AUTOINCREMENT,
72+
query TEXT NOT NULL,
73+
response TEXT NOT NULL
74+
)
75+
""")
76+
77+
conn.commit()
78+
conn.close()
79+
80+
async def save_history(self, query: str, response: str):
81+
import sqlite3
82+
83+
# Connect to SQLite (creates a file called chatbot.db)
84+
conn = sqlite3.connect("mcphistory.db")
85+
cursor = conn.cursor()
86+
87+
# Insert the query-response pair into the table
88+
cursor.execute(
89+
"INSERT INTO conversations (query, response) VALUES (?, ?)",
90+
(query, response),
91+
)
92+
93+
conn.commit()
94+
conn.close()
95+
96+
async def read_history(self):
97+
import sqlite3
98+
99+
# Connect to SQLite (creates a file called chatbot.db)
100+
conn = sqlite3.connect("mcphistory.db")
101+
cursor = conn.cursor()
102+
103+
# Insert the query-response pair into the table
104+
cursor.execute("SELECT query, response FROM conversations")
105+
106+
history = ""
107+
108+
rows = cursor.fetchall()
109+
for row in rows:
110+
history += f"Query: {row[0]}\nResponse: {row[1]}\n{history}\n"
111+
112+
return history
113+
61114
async def process_query(self, query: str) -> str:
62115
"""Process a query using Claude and available tools"""
63116

64117
hal_system = """You are hal3025, an expert in working on filesystem and excel sheets.
65118
You have access to a local filesystem with read and write access.
66119
You are very good in analysing xlsx files and you can use the available
67120
tools with you and return the results to the user.
121+
When user asks you to refer to past conversation or history, then refer to
122+
the query and response available with you and respond. Don't say you
123+
do not have access to history.
68124
Just use the tools, and provide the updates the tools are giving.
69125
Do not use external python packages for the analysis.
70126
Use the tools that are available to you.
@@ -97,7 +153,7 @@ async def process_query(self, query: str) -> str:
97153
tool_results = []
98154
final_text = []
99155

100-
for content in response.content: # each tool call will handle seperately
156+
for content in response.content: # each tool call will handle seperately
101157
if content.type == "text":
102158
final_text.append(content.text)
103159
elif content.type == "tool_use":
@@ -129,20 +185,21 @@ async def chat_loop(self):
129185
"""Run an interactive chat loop"""
130186
print("\nMCP Client Started!")
131187
print("Type your queries or 'quit' to exit.")
132-
reply_history = "" # this variable stores memory
188+
history = await self.read_history()
133189
while True:
134190
try:
135191
query = input("\nInteract with Excel File here: ").strip()
136192
# below the history is assembled
193+
print(history)
137194
query_with_history = (
138-
f"Previous conversation:\n{reply_history}\n Query: {query}"
195+
f"Previous conversation:\n{history}\n Query: {query}"
139196
)
140197
if query.lower() == "quit":
141198
break
142199

143200
response = await self.process_query(query_with_history)
144201
# here is response is apended to history
145-
reply_history += f"Query: {query}\nResponse: {response}\n"
202+
await self.save_history(query, response)
146203
print("\n" + response)
147204

148205
except Exception as e:
@@ -161,6 +218,7 @@ async def main():
161218

162219
client = MCPClient()
163220
try:
221+
await client.create_or_connect_db()
164222
await client.connect_to_server(sys.argv[1])
165223
await client.chat_loop()
166224
finally:

mcp_excel_server/nodb_client.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import asyncio
2+
from typing import Optional
3+
from contextlib import AsyncExitStack
4+
5+
# pyright: reportMissingImports=false
6+
# pyright: reportOptionalSubscript=false
7+
8+
from mcp import ClientSession, StdioServerParameters
9+
from mcp.client.stdio import stdio_client
10+
11+
from anthropic import Anthropic
12+
from dotenv import load_dotenv
13+
14+
# uncomment this when running in your local environment
15+
# ensure you have updated the .env file with the Anthropic API Key
16+
# load_dotenv() # load environment variables from .env
17+
18+
19+
class MCPClient:
20+
def __init__(self):
21+
# Initialize session and client objects
22+
self.session: Optional[ClientSession] = None
23+
self.exit_stack = AsyncExitStack()
24+
self.anthropic = Anthropic()
25+
26+
# methods will go here
27+
async def connect_to_server(self, server_script_path: str):
28+
"""Connect to notion MCP server
29+
30+
Args:
31+
server_script_path: Path to the server script (.py or .js)
32+
"""
33+
is_python = server_script_path.endswith(".py")
34+
is_js = server_script_path.endswith(".js")
35+
if not (is_python or is_js):
36+
raise ValueError("Server script must be a .py or .js file")
37+
38+
command = "python" if is_python else "node"
39+
server_params = StdioServerParameters(
40+
command=command, args=[server_script_path], env=None
41+
)
42+
43+
stdio_transport = await self.exit_stack.enter_async_context(
44+
stdio_client(server_params)
45+
)
46+
self.stdio, self.write = stdio_transport
47+
self.session = await self.exit_stack.enter_async_context(
48+
ClientSession(self.stdio, self.write)
49+
)
50+
51+
await self.session.initialize()
52+
53+
# List available tools
54+
response = await self.session.list_tools()
55+
tools = response.tools
56+
print(
57+
"\nConnected to Notion MCP server with tools:",
58+
[tool.name for tool in tools],
59+
)
60+
61+
async def process_query(self, query: str) -> str:
62+
"""Process a query using Claude and available tools"""
63+
messages = [{"role": "user", "content": query}]
64+
65+
response = await self.session.list_tools()
66+
available_tools = [
67+
{
68+
"name": tool.name,
69+
"description": tool.description,
70+
"input_schema": tool.inputSchema,
71+
}
72+
for tool in response.tools
73+
]
74+
75+
# Initial Claude API call
76+
response = self.anthropic.messages.create(
77+
model="claude-3-5-haiku-20241022",
78+
max_tokens=1000,
79+
messages=messages,
80+
tools=available_tools,
81+
)
82+
83+
# Process response and handle tool calls
84+
tool_results = []
85+
final_text = []
86+
87+
for content in response.content:
88+
if content.type == "text":
89+
final_text.append(content.text)
90+
elif content.type == "tool_use":
91+
tool_name = content.name
92+
tool_args = content.input
93+
94+
# Execute tool call
95+
result = await self.session.call_tool(tool_name, tool_args)
96+
tool_results.append({"call": tool_name, "result": result})
97+
final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")
98+
99+
# Continue conversation with tool results
100+
if hasattr(content, "text") and content.text:
101+
messages.append({"role": "assistant", "content": content.text})
102+
messages.append({"role": "user", "content": result.content})
103+
104+
# Get next response from Claude
105+
response = self.anthropic.messages.create(
106+
model="claude-3-5-haiku-20241022",
107+
max_tokens=1000,
108+
messages=messages,
109+
)
110+
111+
final_text.append(response.content[0].text)
112+
113+
return "\n".join(final_text)
114+
115+
async def chat_loop(self):
116+
"""Run an interactive chat loop"""
117+
print("\nMCP Client Started!")
118+
print("Type your queries or 'quit' to exit.")
119+
120+
while True:
121+
try:
122+
query = input("\nBrain Dump Here: ").strip()
123+
124+
if query.lower() == "quit":
125+
break
126+
127+
response = await self.process_query(query)
128+
print("\n" + response)
129+
130+
except Exception as e:
131+
print(f"\nError: {str(e)}")
132+
133+
async def cleanup(self):
134+
"""Clean up resources"""
135+
await self.exit_stack.aclose()
136+
137+
138+
async def main():
139+
if len(sys.argv) < 2:
140+
print("Usage: python mcpclient.py mcpserver.py")
141+
sys.exit(1)
142+
143+
client = MCPClient()
144+
try:
145+
await client.connect_to_server(sys.argv[1])
146+
await client.chat_loop()
147+
finally:
148+
await client.cleanup()
149+
150+
151+
if __name__ == "__main__":
152+
import sys
153+
154+
asyncio.run(main())

0 commit comments

Comments
 (0)