Selcan Yukcu commited on
Commit
676f4cd
Β·
1 Parent(s): 6a5afc3

fix: conflicts with main solved

Browse files
Files changed (3) hide show
  1. README.md +13 -46
  2. langchain_mcp_client.py +5 -5
  3. postgre_mcp_server.py +94 -86
README.md CHANGED
@@ -14,17 +14,17 @@ A PostgreSQL MCP server and client system that accepts natural language queries
14
 
15
  ## Tech Stack
16
 
17
- | Layer | Technology / Library | Purpose |
18
- |-------------------|------------------------------------|--------------------------------------------------------------|
19
- | LLM Framework | [LangChain](https://www.langchain.com/) | Manages agent behavior, memory, and tool interaction |
20
- | LLM Provider | Google Gemini (via LangChain) | Converts natural language into SQL queries |
21
  | Agent Runtime | LangGraph’s `create_react_agent` | Runs the ReAct-style LLM agent |
22
- | Communication | Modular Command Protocol (MCP) | Handles communication between client and PostgreSQL backend|
23
- | Tooling | `langchain_mcp_adapters` | Loads and manages tools for the agent |
24
- | Database | PostgreSQL | Serves queried data |
25
- | UI (optional) | [Gradio](https://www.gradio.app/) | Provides a lightweight web interface for querying |
26
- | Memory | `ConversationBufferMemory` | Stores short-term in-session memory for conversation context |
27
- | Language | Python 3.10+ | Core programming language used for development |
28
 
29
 
30
  ## Installation
@@ -61,10 +61,6 @@ You can start by copying the example file provided:
61
 
62
  ### .env variables
63
  * API_KEY: Your Google Gemini API key
64
- * DSN: PostgreSQL connection string (used by MCP)
65
- * SCHEMA: Schema name to describe table structure
66
- ### config variables
67
- * TABLE_SUMMARY_PATH: Path to the table descriptions file
68
  * MCP_SERVER_PATH: Path to the MCP server script (e.g. ./postgres_mcp_server.py)
69
 
70
  ## Running the Project
@@ -85,11 +81,11 @@ Once you've installed the dependencies and configured the `.env` file, you're re
85
  .
86
  β”œβ”€β”€ gradio_app.py # Gradio UI to interact with any client (LangChain or SmolAgent)
87
  β”œβ”€β”€ langchain_mcp_client.py # Uses LangChain ReAct agent + ConversationBufferMemory (in-memory, also saves to memory.json)
88
- β”œβ”€β”€ postgre_mcp_client.py # Uses ReAct agent + custom memory class (in-session only), WIP: needs prompt fix
89
- β”œβ”€β”€ postgre_smolagent_client.py # Uses SmolAgent (lightweight), no memory support yet, WIP: output parsing + prompt building
90
  β”œβ”€β”€ postgre_mcp_server.py # MCP server that handles actual PostgreSQL communication
91
  β”œβ”€β”€ conversation_memory.py # Built-in memory class used by some clients
92
- β”œβ”€β”€ memory.json # Keeps session memory. Needs to be deleted after each session
93
  β”œβ”€β”€ table_summary.txt # Database table descriptions used to enrich tools
94
  β”œβ”€β”€ utils.py # Helper functions: intent classification, output parsing, etc.
95
  β”œβ”€β”€ .env.sample # Template for your environment config
@@ -97,34 +93,6 @@ Once you've installed the dependencies and configured the `.env` file, you're re
97
  └── README.md
98
  ```
99
 
100
- ### Switching Clients in Gradio
101
-
102
- The `gradio_app.py` file is designed to work with **all three clients**.
103
- To change the active one:
104
-
105
- 1. Open `gradio_app.py`
106
- 2. Replace the function call with one of the following:
107
-
108
- ```python
109
- from langchain_mcp_client import lc_mcp_exec
110
-
111
- async def run_agent(request):
112
- result = await lc_mcp_exec(request)
113
- return result
114
- # or
115
- from postgre_mcp_client import pg_mcp_exec
116
-
117
- async def run_agent(request):
118
- result = await pg_mcp_exec(request)
119
- return result
120
- # or
121
- from postgre_smolagent_client import pg_mcp_smolagent_exec
122
-
123
- async def run_agent(request):
124
- result = await pg_mcp_smolagent_exec(request)
125
- return result
126
- ```
127
-
128
  ### Work In Progress (WIP) Notes
129
  * postgre_mcp_client.py:
130
  - needs improvement in build_prompt() function
@@ -136,6 +104,5 @@ async def run_agent(request):
136
  - Uses a different output format β€” not yet compatible with parse_mcp_output()
137
 
138
  - Also needs build_prompt() logic finalized
139
- * config file for the variables TABLE_SUMMARY_PATH and MCP_SERVER_PATH
140
  * gradio_app.py:
141
  - Doesn't support conversation chat ui
 
14
 
15
  ## Tech Stack
16
 
17
+ | Layer | Technology / Library | Purpose |
18
+ |-------------|------------------------------------|--------------------------------------------------------------|
19
+ | LLM Framework | [LangChain](https://www.langchain.com/) | Manages agent behavior, memory, and tool interaction |
20
+ | LLM Provider | Google Gemini (via LangChain) | Converts natural language into SQL queries |
21
  | Agent Runtime | LangGraph’s `create_react_agent` | Runs the ReAct-style LLM agent |
22
+ | Communication | Modular Command Protocol (MCP) | Handles communication between client and PostgreSQL backend|
23
+ | Tooling | `langchain_mcp_adapters` | Loads and manages tools for the agent |
24
+ | Database | PostgreSQL | Serves queried data |
25
+ | UI (optional) | [Gradio](https://www.gradio.app/) | Provides a lightweight web interface for querying |
26
+ | Memory | `FileChatMessageHistory` | Stores short-term in-session memory for conversation context |
27
+ | Language | Python 3.10+ | Core programming language used for development |
28
 
29
 
30
  ## Installation
 
61
 
62
  ### .env variables
63
  * API_KEY: Your Google Gemini API key
 
 
 
 
64
  * MCP_SERVER_PATH: Path to the MCP server script (e.g. ./postgres_mcp_server.py)
65
 
66
  ## Running the Project
 
81
  .
82
  β”œβ”€β”€ gradio_app.py # Gradio UI to interact with any client (LangChain or SmolAgent)
83
  β”œβ”€β”€ langchain_mcp_client.py # Uses LangChain ReAct agent + ConversationBufferMemory (in-memory, also saves to memory.json)
84
+ β”œβ”€β”€ postgre_mcp_client.py # Uses ReAct agent + custom memory class (in-session only), **WIP**: needs prompt fix
85
+ β”œβ”€β”€ postgre_smolagent_client.py # Uses SmolAgent (lightweight), no memory support yet, **WIP**: output parsing + prompt building
86
  β”œβ”€β”€ postgre_mcp_server.py # MCP server that handles actual PostgreSQL communication
87
  β”œβ”€β”€ conversation_memory.py # Built-in memory class used by some clients
88
+ β”œβ”€β”€ chat_history.json # Keeps session memory. Needs to be deleted after each session
89
  β”œβ”€β”€ table_summary.txt # Database table descriptions used to enrich tools
90
  β”œβ”€β”€ utils.py # Helper functions: intent classification, output parsing, etc.
91
  β”œβ”€β”€ .env.sample # Template for your environment config
 
93
  └── README.md
94
  ```
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  ### Work In Progress (WIP) Notes
97
  * postgre_mcp_client.py:
98
  - needs improvement in build_prompt() function
 
104
  - Uses a different output format β€” not yet compatible with parse_mcp_output()
105
 
106
  - Also needs build_prompt() logic finalized
 
107
  * gradio_app.py:
108
  - Doesn't support conversation chat ui
langchain_mcp_client.py CHANGED
@@ -1,4 +1,5 @@
1
  import os.path
 
2
  from typing import Tuple, Any
3
 
4
  from mcp import ClientSession, StdioServerParameters
@@ -15,11 +16,10 @@ import logging
15
  from dotenv import load_dotenv
16
 
17
 
18
- load_dotenv()
19
- logger = logging.getLogger(__name__)
20
-
21
 
22
- async def lc_mcp_exec(request: str) -> tuple[Any, Any]:
 
 
23
  """
24
  Execute the full PostgreSQL MCP pipeline with persistent memory.
25
  """
@@ -121,6 +121,7 @@ def load_table_summary(path: str) -> str:
121
 
122
  def get_server_params() -> StdioServerParameters:
123
  # TODO: give server params from config
 
124
  return StdioServerParameters(
125
  command="python",
126
  args=[os.environ["MCP_SERVER_PATH"]],
@@ -134,7 +135,6 @@ async def build_prompt(session, intent, request, tools, summary, chat_history):
134
  superset_prompt = await session.read_resource("resource://last_prompt")
135
  conversation_prompt = await session.read_resource("resource://base_prompt")
136
 
137
-
138
  if intent == "superset_request":
139
  template = superset_prompt.contents[0].text
140
  return template.format(
 
1
  import os.path
2
+ import json
3
  from typing import Tuple, Any
4
 
5
  from mcp import ClientSession, StdioServerParameters
 
16
  from dotenv import load_dotenv
17
 
18
 
 
 
 
19
 
20
+ logger = logging.getLogger(__name__)
21
+ load_dotenv()
22
+ async def lc_mcp_exec(request: str, history) -> tuple[Any, Any]:
23
  """
24
  Execute the full PostgreSQL MCP pipeline with persistent memory.
25
  """
 
121
 
122
  def get_server_params() -> StdioServerParameters:
123
  # TODO: give server params from config
124
+ load_dotenv()
125
  return StdioServerParameters(
126
  command="python",
127
  args=[os.environ["MCP_SERVER_PATH"]],
 
135
  superset_prompt = await session.read_resource("resource://last_prompt")
136
  conversation_prompt = await session.read_resource("resource://base_prompt")
137
 
 
138
  if intent == "superset_request":
139
  template = superset_prompt.contents[0].text
140
  return template.format(
postgre_mcp_server.py CHANGED
@@ -59,119 +59,128 @@ async def base_prompt_query() -> str:
59
 
60
  base_prompt = """
61
 
62
- ==========================
63
- # Your Role
64
- ==========================
65
 
66
- You are an expert in generating and executing SQL queries and interacting with a PostgreSQL database using **FastMCP tools**. These tools allow you to:
67
 
68
- - List available tables
69
- - Retrieve schema details
70
- - Execute SQL queries
71
 
72
 
73
- Each tool may also return previews or summaries of table contents to help you better understand the data structure.
 
 
74
 
75
- You also have access to **short-term memory**, which stores relevant context from earlier queries. If memory contains the needed information, you **must use it** instead of repeating tool calls with the same input. Avoid redundant tool usage unless:
76
- - The memory is empty, or
77
- - A tool's output is outdated or missing
78
 
79
- ---
80
 
81
- ==========================
82
- # Your Objective
83
- ==========================
 
 
 
84
 
85
- When a user submits a request, follow these steps:
86
 
87
- 1. Analyze the request to determine the desired data or action.
88
- 2. Use tools to gather any necessary information (e.g., list tables, get schema).
89
- 3. Generate a valid SQL query (such as **SELECT**, **COUNT**, or other read-only operations) and clearly display the full query.
90
- 4. Execute the query and **return the execution result of the query**.
91
- 5. **Chain tools logically to build toward the answer.**
92
- 6. Explain your reasoning at every step for clarity and transparency.
93
- 7. Show the result of the **execute_query** in your final answer.
94
 
95
- ---
96
 
97
- ==========================
98
- # Critical Rules
99
- ==========================
 
 
 
 
100
 
101
- - Only use **read-only** SQL queries such as **SELECT**, **COUNT**, or queries with **GROUP BY**, **ORDER BY**, etc.
102
- - **Never** use destructive operations like **DELETE**, **UPDATE**, **INSERT**, or **DROP**.
103
- - Always show the SQL query you generate along with the execution result.
104
- - Validate SQL syntax before execution.
105
- - Never assume table or column names. Use tools to confirm structure.
106
- - Use memory efficiently. Don’t rerun a tool unless necessary.
107
- - If you generate a SQL query, immediately call the **execute_query** tool using that query. Do not delay or wait for user confirmation.
108
 
 
 
 
109
 
110
- ---
 
 
 
 
 
 
111
 
112
- ==========================
113
- # Short-Term Memory
114
- ==========================
 
 
 
 
 
 
115
 
116
- You have access to the following memory from this conversation. Use it if applicable for the current request.
 
 
117
 
118
- {chat_history}
119
 
120
- ---
121
 
122
- ==========================
123
- # Tools
124
- ==========================
125
 
126
- You can use the following FastMCP tools. These allow you to create **read-only** queries, such as `SELECT`, `COUNT`, or queries with `GROUP BY`, `ORDER BY`, and similar clauses. You may chain tools together to gather the necessary information before generating your SQL query.
127
 
128
- {tools}
 
129
 
130
- ---
 
 
 
 
 
 
131
 
 
 
 
132
 
133
- ### Invalid Example β€” DELETE Operation (Not Allowed):
134
- **User Request:** "Delete all customers from Germany."
135
 
136
- **Response Guidance:**
137
- - **Do not generate or execute** destructive queries such as `DELETE`.
138
- - Instead, respond with a message like:
139
- > Destructive operations such as `DELETE` are not permitted. I can help you retrieve the customers from Germany using a `SELECT` query instead:
140
- > ```sql
141
- > SELECT * FROM customers WHERE country = 'Germany';
142
- > ```
143
 
144
- ---
145
- ==========================
146
- # Output Format
147
- ==========================
148
-
149
- Present your final answer using the following structure **exactly**. When necessary, bold the important parts of your answer or use `` inline code blocks.:
150
-
151
- ```markdown
152
- # Result
153
- {{Take the result from the execute_query tool and format it nicely using Markdown. Use a Markdown table for tabular data (rows and columns) including headers. Use bullet points or items in markdown for answers that include lists of names or descriptions. Use plain text for single values or simple messages. Ensure data alignment and clarity.}}
154
-
155
- # Explanation
156
- {{Provide a concise explanation or interpretation of the results in 1-3 sentences. Explain what the data in the 'Result' section represents in the context of the user's request.}}
157
-
158
- # Query
159
- ```sql
160
- {{Display the exact SQL query you generated and executed here to answer the user's request.}}
161
-
162
- **Reminder:**
163
- **Every time you generate a SQL query, call **execute_query** right after and include the result in your final response.**
164
- **If you do not execute the generated SQL query, this will be the violation of the instructions**
165
- **Your final answer cannot be only a SQL query, you will have to call **execute_query** and give the result of the call with the SQL query.**
166
- ---
167
- =========================
168
- # New User Request
169
- =========================
170
 
171
- Please fulfill the following request based on the above context:
 
 
172
 
173
- {new_request}
174
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
  return base_prompt
177
 
@@ -442,7 +451,6 @@ async def test_connection(ctx: Context) -> str:
442
  return f"Connection failed: {str(e)}"
443
 
444
 
445
-
446
  @mcp.tool(description="Executes a read-only SQL SELECT query and returns formatted results or an error message.")
447
  async def execute_query(
448
  query: str = Field(description="SQL query to execute (SELECT only)"),
 
59
 
60
  base_prompt = """
61
 
62
+ ==========================
63
+ # Your Role
64
+ ==========================
65
 
66
+ You are an expert in generating and executing SQL queries and interacting with a PostgreSQL database using **FastMCP tools**. These tools allow you to:
67
 
68
+ - List available tables
69
+ - Retrieve schema details
70
+ - Execute SQL queries
71
 
72
 
73
+ Each tool may also return previews or summaries of table contents to help you better understand the data structure.
74
+
75
+ ---
76
 
77
+ ==========================
78
+ # Your Objective
79
+ ==========================
80
 
81
+ When a user submits a request, you must:
82
 
83
+ 1. **Analyze the request** to determine the required data or action.
84
+ 2. **Use FastMCP tools** to gather any necessary information (e.g., list tables or retrieve schema).
85
+ 3. **Generate a valid SQL SELECT query**, if needed, and clearly show the full query.
86
+ 4. **Execute the SQL query** and return the results.
87
+ 5. **Chain tools logically**, such as: List Tables β†’ Get Schema β†’ Write and Run Query.
88
+ 6. **Explain your reasoning and each step taken** to ensure clarity and transparency.
89
 
90
+ ---
91
 
92
+ ==========================
93
+ # Your Objective
94
+ ==========================
 
 
 
 
95
 
96
+ When a user submits a request, follow these steps:
97
 
98
+ 1. Analyze the request to determine the desired data or action.
99
+ 2. Use tools to gather any necessary information (e.g., list tables, get schema).
100
+ 3. Generate a valid SQL query (such as **SELECT**, **COUNT**, or other read-only operations) and clearly display the full query.
101
+ 4. Execute the query and **return the execution result of the query**.
102
+ 5. **Chain tools logically to build toward the answer.**
103
+ 6. Explain your reasoning at every step for clarity and transparency.
104
+ 7. Show the result of the **execute_query** in your final answer.
105
 
106
+ ---
 
 
 
 
 
 
107
 
108
+ ==========================
109
+ # Critical Rules
110
+ ==========================
111
 
112
+ - Only use **read-only** SQL queries such as **SELECT**, **COUNT**, or queries with **GROUP BY**, **ORDER BY**, etc.
113
+ - **Never** use destructive operations like **DELETE**, **UPDATE**, **INSERT**, or **DROP**.
114
+ - Always show the SQL query you generate along with the execution result.
115
+ - Validate SQL syntax before execution.
116
+ - Never assume table or column names. Use tools to confirm structure.
117
+ - Use memory efficiently. Don’t rerun a tool unless necessary.
118
+ - If you generate a SQL query, immediately call the **execute_query** tool using that query. Do not delay or wait for user confirmation.
119
 
120
+ ---
121
+
122
+ ==========================
123
+ # Database Description
124
+ ==========================
125
+
126
+ {descriptions}
127
+
128
+ ---
129
 
130
+ ==========================
131
+ # Tools
132
+ ==========================
133
 
134
+ You can use the following FastMCP tools. These allow you to create **read-only** queries, such as `SELECT`, `COUNT`, or queries with `GROUP BY`, `ORDER BY`, and similar clauses. You may chain tools together to gather the necessary information before generating your SQL query.
135
 
136
+ {tools}
137
 
138
+ ---
 
 
139
 
 
140
 
141
+ ### Invalid Example β€” DELETE Operation (Not Allowed):
142
+ **User Request:** "Delete all customers from Germany."
143
 
144
+ **Response Guidance:**
145
+ - **Do not generate or execute** destructive queries such as `DELETE`.
146
+ - Instead, respond with a message like:
147
+ > Destructive operations such as `DELETE` are not permitted. I can help you retrieve the customers from Germany using a `SELECT` query instead:
148
+ > ```sql
149
+ > SELECT * FROM customers WHERE country = 'Germany';
150
+ > ```
151
 
152
+ ==========================
153
+ # Output Format
154
+ ==========================
155
 
156
+ Present your final answer using the following structure **exactly**. When necessary, bold the important parts of your answer or use `` inline code blocks.:
 
157
 
158
+ ```markdown
159
+ # Result
160
+ {{Take the result from the execute_query tool and format it nicely using Markdown. Use a Markdown table for tabular data (rows and columns) including headers. Use bullet points or items in markdown for answers that include lists of names or descriptions. Use plain text for single values or simple messages. Ensure data alignment and clarity.}}
 
 
 
 
161
 
162
+ # Explanation
163
+ {{Provide a concise explanation or interpretation of the results in 1-3 sentences. Explain what the data in the 'Result' section represents in the context of the user's request.}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
+ # Query
166
+ ```sql
167
+ {{Display the exact SQL query you generated and executed here to answer the user's request.}}
168
 
169
+ **Reminder:**
170
+ **Every time you generate a SQL query, call **execute_query** right after and include the result in your final response.**
171
+ **If you do not execute the generated SQL query, this will be the violation of the instructions**
172
+ **Your final answer cannot be only a SQL query, you will have to call **execute_query** and give the result of the call with the SQL query.**
173
+ ---
174
+ {chat_history}
175
+ ---
176
+ =========================
177
+ # New User Request
178
+ =========================
179
+
180
+ Please fulfill the following request based on the above context:
181
+
182
+ {new_request}
183
+ """
184
 
185
  return base_prompt
186
 
 
451
  return f"Connection failed: {str(e)}"
452
 
453
 
 
454
  @mcp.tool(description="Executes a read-only SQL SELECT query and returns formatted results or an error message.")
455
  async def execute_query(
456
  query: str = Field(description="SQL query to execute (SELECT only)"),