Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions data/das-column-resources.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
SELECT v.name AS ViewName,
c.name AS ColumnName,
ep.value AS Description
FROM sys.views v
INNER JOIN sys.schemas s ON v.schema_id = s.schema_id
LEFT JOIN sys.columns c ON c.object_id = v.object_id
LEFT JOIN sys.extended_properties ep
ON ep.major_id = v.object_id
AND ep.name = c.name
AND ep.minor_id = 0
AND ep.class = 1
WHERE s.name = 'AI'
AND v.name = 'v_SL_Reviews'
ORDER BY c.column_id;
7 changes: 7 additions & 0 deletions data/das-table-resources.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
SELECT SCHEMA_NAME(v.schema_id) as SchemaName,
v.name as ViewName,
ep.value as Description
FROM sys.views v
LEFT JOIN sys.extended_properties ep ON ep.major_id = v.object_id
AND ep.minor_id = 0 AND ep.name = 'MS_Description'
WHERE SCHEMA_NAME(v.schema_id) = 'AI';
20 changes: 20 additions & 0 deletions mssql_mcp_server/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ class ServerConfig:
enable_dynamic_resources: bool = True


@dataclass
class ResourceConfig:
"""dynamically register resources"""
column_client: str
table_client: str


class Settings:
"""Application settings manager."""

Expand All @@ -101,6 +108,7 @@ def __init__(self):
self._async_database_config: Optional[AsyncDatabaseConfig] = None
self._cache_config: Optional[CacheConfig] = None
self._server_config: Optional[ServerConfig] = None
self._resource_config: Optional[ResourceConfig] = None

@property
def async_database(self) -> AsyncDatabaseConfig:
Expand All @@ -123,6 +131,12 @@ def server(self) -> ServerConfig:
self._server_config = self._load_server_config()
return self._server_config

@property
def resource(self) -> ResourceConfig:
if not self._resource_config:
self._resource_config = self._load_resource_config()
return self._resource_config

def _load_async_database_config(self) -> AsyncDatabaseConfig:
"""Load async database configuration from environment variables."""
required_vars = ["MSSQL_USER", "MSSQL_PASSWORD", "MSSQL_DATABASE"]
Expand Down Expand Up @@ -172,5 +186,11 @@ def _load_server_config(self) -> ServerConfig:
mcp_port=int(os.getenv("FASTMCP_PORT", "8000")),
)

def _load_resource_config(self) -> ResourceConfig:
return ResourceConfig(
column_client=os.getenv("RAG_RESOURCE_COLUMN_CLIENT"),
table_client=os.getenv("RAG_RESOURCE_TABLE_CLIENT")
)


settings = Settings()
16 changes: 16 additions & 0 deletions mssql_mcp_server/handlers/async_resources.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
import json
from pathlib import Path
from mssql_mcp_server.database.async_operations import AsyncDatabaseOperations
from mssql_mcp_server.utils.logger import Logger
from mssql_mcp_server.utils.exceptions import DatabaseOperationError

logger = Logger.get_logger(__name__)
current_dir = Path(__file__).parent.parent.parent
column_resources_path = current_dir / "data" / "das-column-resources.sql"
table_resources_path = current_dir / "data" / "das-table-resources.sql"


class AsyncResourceHandlers:
"""Async MCP resource handlers with dynamic resource generation."""

@staticmethod
async def get_ai_views_column_descriptions():
sql = column_resources_path.read_text()
logger.info(f"Getting AI views column descriptions: {sql}")
return await AsyncDatabaseOperations.execute_query(sql)

@staticmethod
async def get_ai_views_table_descriptions():
sql = table_resources_path.read_text()
logger.info(f"Getting AI views table descriptions: {sql}")
return await AsyncDatabaseOperations.execute_query(sql)

@staticmethod
async def read_object_data(object_name: str, object_type: str = "table", limit: int = 100) -> str:
"""Read data from a specific table or view."""
Expand Down
26 changes: 26 additions & 0 deletions mssql_mcp_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,31 @@
app = FastMCP(name="mssql_mcp_server")


def dynamically_register_resources():
if settings.resource.column_client:
logger.info(f"Registering column {settings.resource.column_client}")

@app.resource("mssql://database/ai_views/column_descriptions")
async def get_ai_views_column_descriptions() -> str:
"""Get column descriptions for AI schema views to help with SQL generation."""
try:
return await AsyncResourceHandlers.get_ai_views_column_descriptions()
except Exception as e:
logger.error(f"Error getting AI views column descriptions: {e}")
return f"Error: {str(e)}"
if settings.resource.table_client:
logger.info(f"Registering table {settings.resource.table_client}")

@app.resource("mssql://database/ai_views/table_descriptions")
async def get_ai_views_table_descriptions() -> str:
"""Get table level descriptions for AI schema views to help with SQL generation."""
try:
return await AsyncResourceHandlers.get_ai_views_table_descriptions()
except Exception as e:
logger.error(f"Error getting AI views table level descriptions: {e}")
return f"Error: {str(e)}"


# Static database-level resources
@app.resource("mssql://database/tables")
async def get_database_tables() -> str:
Expand Down Expand Up @@ -339,6 +364,7 @@ async def initialize_server() -> None:
# Dynamically register resources for each table and view
total_resources = await register_table_and_view_resources()
logger.info(f"Server will expose {total_resources} dynamic resources")
dynamically_register_resources()
logger.info("Server initialization completed successfully")

except Exception as e:
Expand Down
2 changes: 1 addition & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
asyncio_mode = auto
asyncio_default_fixture_loop_scope = function
testpaths = tests
python_files = test_*.py
python_files = test_*.py
6 changes: 5 additions & 1 deletion tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@
# except ValueError as e:
# if "Missing required database configuration" in str(e):
# pytest.skip("Database configuration not available")
# raise
# raise

def test_placeholder():
"""Placeholder test to prevent pytest from failing with no tests."""
assert True