feat(viz-platform): create MCP server foundation (#170)
Add viz-platform MCP server structure at mcp-servers/viz-platform/: - mcp_server/server.py: Main MCP server entry point with async initialization - mcp_server/config.py: Hybrid config loader with DMC version detection - mcp_server/dmc_tools.py: Placeholder for DMC validation tools - pyproject.toml and requirements.txt for dependencies - tests/ directory structure Server starts without errors with empty tool list. Config detects DMC installation status via importlib.metadata. Closes #170 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
129
mcp-servers/viz-platform/mcp_server/server.py
Normal file
129
mcp-servers/viz-platform/mcp_server/server.py
Normal file
@@ -0,0 +1,129 @@
|
||||
"""
|
||||
MCP Server entry point for viz-platform integration.
|
||||
|
||||
Provides Dash Mantine Components validation, charting, layout, theming, and page tools
|
||||
to Claude Code via JSON-RPC 2.0 over stdio.
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
import json
|
||||
from mcp.server import Server
|
||||
from mcp.server.stdio import stdio_server
|
||||
from mcp.types import Tool, TextContent
|
||||
|
||||
from .config import VizPlatformConfig
|
||||
|
||||
# Suppress noisy MCP validation warnings on stderr
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logging.getLogger("root").setLevel(logging.ERROR)
|
||||
logging.getLogger("mcp").setLevel(logging.ERROR)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VizPlatformMCPServer:
|
||||
"""MCP Server for visualization platform integration"""
|
||||
|
||||
def __init__(self):
|
||||
self.server = Server("viz-platform-mcp")
|
||||
self.config = None
|
||||
# Tool handlers will be added in subsequent issues
|
||||
# self.dmc_tools = None
|
||||
# self.chart_tools = None
|
||||
# self.layout_tools = None
|
||||
# self.theme_tools = None
|
||||
# self.page_tools = None
|
||||
|
||||
async def initialize(self):
|
||||
"""Initialize server and load configuration."""
|
||||
try:
|
||||
config_loader = VizPlatformConfig()
|
||||
self.config = config_loader.load()
|
||||
|
||||
# Log available capabilities
|
||||
caps = []
|
||||
if self.config.get('dmc_available'):
|
||||
caps.append(f"DMC {self.config.get('dmc_version')}")
|
||||
else:
|
||||
caps.append("DMC (not installed)")
|
||||
|
||||
logger.info(f"viz-platform MCP Server initialized with: {', '.join(caps)}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to initialize: {e}")
|
||||
raise
|
||||
|
||||
def setup_tools(self):
|
||||
"""Register all available tools with the MCP server"""
|
||||
|
||||
@self.server.list_tools()
|
||||
async def list_tools() -> list[Tool]:
|
||||
"""Return list of available tools"""
|
||||
tools = []
|
||||
|
||||
# DMC validation tools (Issue #172)
|
||||
# - list_components
|
||||
# - get_component_props
|
||||
# - validate_component
|
||||
|
||||
# Chart tools (Issue #173)
|
||||
# - chart_create
|
||||
# - chart_configure_interaction
|
||||
|
||||
# Layout tools (Issue #174)
|
||||
# - layout_create
|
||||
# - layout_add_filter
|
||||
# - layout_set_grid
|
||||
|
||||
# Theme tools (Issue #175)
|
||||
# - theme_create
|
||||
# - theme_extend
|
||||
# - theme_validate
|
||||
# - theme_export_css
|
||||
|
||||
# Page tools (Issue #176)
|
||||
# - page_create
|
||||
# - page_add_navbar
|
||||
# - page_set_auth
|
||||
|
||||
return tools
|
||||
|
||||
@self.server.call_tool()
|
||||
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
|
||||
"""Handle tool invocation."""
|
||||
try:
|
||||
# Tool routing will be added as tools are implemented
|
||||
# DMC tools
|
||||
# if name == "list_components":
|
||||
# result = await self.dmc_tools.list_components(**arguments)
|
||||
# ...
|
||||
|
||||
raise ValueError(f"Unknown tool: {name}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Tool {name} failed: {e}")
|
||||
return [TextContent(
|
||||
type="text",
|
||||
text=json.dumps({"error": str(e)}, indent=2)
|
||||
)]
|
||||
|
||||
async def run(self):
|
||||
"""Run the MCP server"""
|
||||
await self.initialize()
|
||||
self.setup_tools()
|
||||
|
||||
async with stdio_server() as (read_stream, write_stream):
|
||||
await self.server.run(
|
||||
read_stream,
|
||||
write_stream,
|
||||
self.server.create_initialization_options()
|
||||
)
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main entry point"""
|
||||
server = VizPlatformMCPServer()
|
||||
await server.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user