Skip to content

Commit 2e3b248

Browse files
committed
Add DataToolService for dataset access and management
Introduces DataToolService to provide access to specific CSV datasets via new tools: data_provider and show_tables. Updates the factory and service registration to include the new service and adds the DATA domain to the Domain enum. This enables controlled access to allowed data files for the MCP server.
1 parent 2d98eeb commit 2e3b248

File tree

4 files changed

+97
-2
lines changed

4 files changed

+97
-2
lines changed

src/backend/v3/mcp_server/core/factory.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class Domain(Enum):
1818
TECH_SUPPORT = "tech_support"
1919
RETAIL = "retail"
2020
GENERAL = "general"
21+
DATA = "data"
2122

2223

2324
class MCPToolBase(ABC):

src/backend/v3/mcp_server/mcp_server.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from typing import Optional
1212

1313
from core.factory import MCPToolFactory
14-
from services import HRService, TechSupportService, GeneralService
14+
from services import HRService, TechSupportService, GeneralService, DataToolService
1515
from config.settings import config
1616

1717
# Setup logging
@@ -26,6 +26,9 @@
2626
factory.register_service(TechSupportService())
2727
factory.register_service(GeneralService())
2828

29+
# Register DataToolService with the dataset path
30+
factory.register_service(DataToolService(dataset_path="data/datasets"))
31+
2932

3033
def create_fastmcp_server():
3134
"""Create and configure FastMCP server."""

src/backend/v3/mcp_server/services/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
from .hr_service import HRService
66
from .tech_support_service import TechSupportService
77
from .general_service import GeneralService
8+
from .data_tool_service import DataToolService
89

9-
__all__ = ["HRService", "TechSupportService", "GeneralService"]
10+
__all__ = ["HRService", "TechSupportService", "GeneralService", "DataToolService"]
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import os
2+
import logging
3+
from typing import List
4+
from ..core.factory import MCPToolBase, Domain
5+
6+
ALLOWED_FILES = [
7+
"competitor_Pricing_Analysis.csv",
8+
"customer_Churn_Analysis.csv",
9+
"customer_feedback_surveys.csv",
10+
"customer_profile.csv",
11+
"delivery_performance_metrics.csv",
12+
"email_Marketing_Engagement.csv",
13+
"loyalty_Program_Overview.csv",
14+
"product_return_rates.csv",
15+
"product_table.csv",
16+
"purchase_history.csv",
17+
"social_media_sentiment_analysis.csv",
18+
"store_visit_history.csv",
19+
"subscription_benefits_utilization.csv",
20+
"unauthorized_Access_Attempts.csv",
21+
"warehouse_Incident_Reports.csv",
22+
"website_activity_log.csv",
23+
]
24+
25+
26+
class DataToolService(MCPToolBase):
27+
def __init__(self, dataset_path: str):
28+
super().__init__(Domain.DATA)
29+
self.dataset_path = dataset_path
30+
self.allowed_files = set(ALLOWED_FILES)
31+
32+
def _find_file(self, filename: str) -> str:
33+
"""
34+
Searches recursively within the dataset_path for an exact filename match (case-sensitive).
35+
Returns the full path if found, else None.
36+
"""
37+
logger = logging.getLogger("find_file")
38+
for root, _, files in os.walk(self.dataset_path):
39+
if filename in files:
40+
full_path = os.path.join(root, filename)
41+
logger.info("Found file: %s", full_path)
42+
return full_path
43+
logger.warning(
44+
"File '%s' not found in '%s' directory.", filename, self.dataset_path
45+
)
46+
return None
47+
48+
def register_tools(self, mcp):
49+
@mcp.tool()
50+
def data_provider(tablename: str) -> str:
51+
"""A tool that provides data from database based on given table name as parameter."""
52+
logger = logging.getLogger("file_provider")
53+
logger.info("Table '%s' requested.", tablename)
54+
tablename = tablename.strip()
55+
filename = (
56+
f"{tablename}.csv"
57+
if not tablename.lower().endswith(".csv")
58+
else tablename
59+
)
60+
if filename not in self.allowed_files:
61+
logger.error("File '%s' is not allowed.", filename)
62+
return f"File '{filename}' is not allowed."
63+
file_path = self._find_file(filename)
64+
if not file_path:
65+
logger.error("File '%s' not found.", filename)
66+
return f"File '{filename}' not found."
67+
try:
68+
with open(file_path, "r", encoding="utf-8") as file:
69+
data = file.read()
70+
return data
71+
except IOError as e:
72+
logger.error("Error reading file '%s': %s", filename, e)
73+
return None
74+
75+
@mcp.tool()
76+
def show_tables() -> List[str]:
77+
"""Returns a list of allowed table names (without .csv extension) that exist in the dataset path."""
78+
logger = logging.getLogger("show_tables")
79+
found_tables = []
80+
for filename in self.allowed_files:
81+
file_path = self._find_file(filename)
82+
if file_path:
83+
table_name = filename[:-4] # Remove .csv
84+
found_tables.append(table_name)
85+
logger.info("Found table: %s", table_name)
86+
if not found_tables:
87+
logger.warning(
88+
"No allowed CSV tables found in '%s' directory.", self.dataset_path
89+
)
90+
return found_tables

0 commit comments

Comments
 (0)