Skip to main content
Glama

SingleStore MCP Server

init.py12.2 kB
import os import json import sys import subprocess from pathlib import Path from typing import Optional from ..logger import get_logger from .constants import ( CLIENT_CLAUDE_DESKTOP, CLIENT_CLAUDE_CODE, CLIENT_CURSOR, CLIENT_VSCODE, CLIENT_VSCODE_INSIDERS, CLIENT_WINDSURF, CLIENT_LM_STUDIO, CLIENT_GEMINI, CLIENT_CHOICES, ) # Get logger for this module logger = get_logger() # Client config file paths (platform-dependent) CLIENT_CONFIG_PATHS = { CLIENT_CLAUDE_DESKTOP: { "darwin": ("~/Library/Application Support/Claude/claude_desktop_config.json"), "win32": "%APPDATA%\\Claude\\claude_desktop_config.json", "linux": "~/.config/Claude/claude_desktop_config.json", }, CLIENT_CLAUDE_CODE: { "darwin": None, "win32": None, "linux": None, }, CLIENT_CURSOR: { "darwin": "~/.cursor/mcp.json", "win32": "~/.cursor/mcp.json", "linux": "~/.cursor/mcp.json", }, CLIENT_VSCODE: { "darwin": "~/Library/Application Support/Code/User/mcp.json", "win32": "%APPDATA%\\Code\\User\\mcp.json", "linux": "~/.config/Code/User/mcp.json", }, CLIENT_VSCODE_INSIDERS: { "darwin": "~/Library/Application Support/Code - Insiders/User/mcp.json", "win32": "%APPDATA%\\Code - Insiders\\User\\mcp.json", "linux": "~/.config/Code - Insiders/User/mcp.json", }, CLIENT_WINDSURF: { "darwin": "~/.windsurf/mcp.json", "win32": "~/.windsurf/mcp.json", "linux": "~/.windsurf/mcp.json", }, CLIENT_LM_STUDIO: { "darwin": "~/Library/Application Support/LM Studio/mcp.json", "win32": "%APPDATA%\\LM Studio\\mcp.json", "linux": "~/.config/lm-studio/mcp.json", }, CLIENT_GEMINI: { "darwin": "~/.config/gemini/settings.json", "win32": "%APPDATA%\\gemini\\settings.json", "linux": "~/.config/gemini/settings.json", }, } # Client-specific config templates CLIENT_CONFIG_TEMPLATES = { CLIENT_CLAUDE_DESKTOP: { "mcpServers": { "singlestore-mcp-server": { "command": "uvx", "args": [ "singlestore-mcp-server", "start", ], } } }, CLIENT_CLAUDE_CODE: {}, CLIENT_CURSOR: { "mcpServers": { "singlestore-mcp-server": { "command": "uvx", "args": ["singlestore-mcp-server", "start"], } } }, CLIENT_VSCODE: { "servers": { "singlestore-mcp-server": { "command": "uvx", "args": ["singlestore-mcp-server", "start"], } } }, CLIENT_VSCODE_INSIDERS: { "servers": { "singlestore-mcp-server": { "command": "uvx", "args": ["singlestore-mcp-server", "start"], } } }, CLIENT_WINDSURF: { "mcpServers": { "singlestore-mcp-server": { "command": "uvx", "args": ["singlestore-mcp-server", "start"], } } }, CLIENT_LM_STUDIO: { "mcpServers": { "singlestore-mcp-server": { "command": "uvx", "args": ["singlestore-mcp-server", "start"], } } }, CLIENT_GEMINI: { "mcpServers": { "singlestore-mcp-server": { "command": "uvx", "args": ["singlestore-mcp-server", "start"], } } }, } def get_config_path(client: str) -> Optional[Path]: """ Get the platform-specific config path for the client. Args: client: The LLM client name Returns: Path to the config file or None if unsupported platform or CLI-based client """ # Claude Code uses CLI configuration, no config file if client == CLIENT_CLAUDE_CODE: return None platform = sys.platform if platform not in CLIENT_CONFIG_PATHS[client]: logger.error(f"Unsupported platform: {platform} for client: {client}") return None # Get the raw path and expand environment variables and user directory raw_path = CLIENT_CONFIG_PATHS[client][platform] if raw_path is None: return None if platform == "win32": # Windows-specific environment variable expansion for env_var in os.environ: placeholder = f"%{env_var}%" if placeholder in raw_path: raw_path = raw_path.replace(placeholder, os.environ[env_var]) return Path(raw_path) else: # Unix-like systems return Path(os.path.expanduser(raw_path)) def create_config_directory(config_path: Path) -> bool: """ Create the directory for the config file if it doesn't exist. Args: config_path: Path to the config file Returns: True if successful, False otherwise """ try: config_path.parent.mkdir(parents=True, exist_ok=True) return True except Exception as e: logger.error(f"Error creating config directory: {e}") return False def update_client_config(client: str) -> tuple[bool, Optional[dict]]: """ Update the client configuration file to use the SingleStore MCP server with JWT authentication. Args: client: The LLM client name Returns: Tuple of (success: bool, config_data: Optional[dict]) """ # Handle Claude Code specially - it uses CLI configuration if client == CLIENT_CLAUDE_CODE: command = [ "claude", "mcp", "add", "singlestore-mcp-server", "uvx", "singlestore-mcp-server", "start", ] try: logger.info("Running Claude Code CLI command...") result = subprocess.run(command, capture_output=True, text=True, check=True) logger.info("Successfully added SingleStore MCP server to Claude Code") if result.stdout: logger.info(f"Output: {result.stdout.strip()}") logger.info( "The server will handle authentication automatically via browser OAuth." ) return True, { "cli_command": " ".join(command), "output": result.stdout.strip() if result.stdout else "", } except subprocess.CalledProcessError as e: logger.error(f"Failed to run Claude Code CLI command: {e}") if e.stderr: logger.error(f"Error output: {e.stderr.strip()}") logger.info("You can run this command manually:") logger.info(" ".join(command)) return False, None except FileNotFoundError: logger.error( "Claude CLI not found. Please make sure Claude Code is installed and the 'claude' command is available in your PATH." ) logger.info( "You can run this command manually once Claude Code is installed:" ) logger.info(" ".join(command)) return False, None config_path = get_config_path(client) if not config_path: return False, None # Create directory if it doesn't exist if not create_config_directory(config_path): return False, None # Prepare the config data template = CLIENT_CONFIG_TEMPLATES[client] config_data = template try: # Read existing config if available if config_path.exists(): with open(config_path, "r") as f: try: existing_config = json.load(f) # Merge the configs based on client type if client in [CLIENT_VSCODE, CLIENT_VSCODE_INSIDERS]: # VS Code uses a different structure in settings.json if "servers" not in existing_config: existing_config["servers"] = {} existing_config["servers"]["singlestore-mcp-server"] = ( config_data["servers"]["singlestore-mcp-server"] ) elif client in [ CLIENT_CLAUDE_DESKTOP, CLIENT_CURSOR, CLIENT_WINDSURF, CLIENT_LM_STUDIO, CLIENT_GEMINI, ]: # Standard mcpServers structure if "mcpServers" not in existing_config: existing_config["mcpServers"] = {} existing_config["mcpServers"]["singlestore-mcp-server"] = ( config_data["mcpServers"]["singlestore-mcp-server"] ) config_data = existing_config except json.JSONDecodeError: # If the file exists but is invalid JSON, use our template logger.warning( f"Existing config file at {config_path} is not valid JSON. Creating a new file." ) # Write the updated config with open(config_path, "w") as f: json.dump(config_data, indent=2, fp=f) logger.info( f"Successfully configured {client.capitalize()} to use SingleStore MCP server." ) logger.info(f"Config updated at: {config_path}") logger.info( "The server will handle authentication automatically via browser OAuth." ) return True, config_data except Exception as e: logger.error(f"Error updating client config: {e}") return False, None def init_command(client: str) -> int: """ Initialize the SingleStore MCP server for a specific client with JWT authentication. Args: client: Name of the LLM client (claude-desktop, claude-code, cursor, vscode, windsurf, lm-studio, gemini) Returns: Exit code (0 for success, 1 for failure) """ client = client.lower() valid_clients = CLIENT_CHOICES if client not in valid_clients: logger.error(f"Unsupported client '{client}'") logger.error(f"Supported clients: {', '.join(valid_clients)}") return 1 logger.info(f"Initializing SingleStore MCP server for {client.capitalize()}...") # Update the client configuration success, config_data = update_client_config(client) if success and config_data: logger.info( "\nSetup complete! You can now use the MCP server with your LLM client." ) logger.info( "The server will handle authentication automatically via browser OAuth." ) # Show the generated config logger.info("\nGenerated configuration:") if client == CLIENT_CLAUDE_CODE: if "output" in config_data and config_data["output"]: logger.info(f"Claude Code output: {config_data['output']}") else: logger.info(f"CLI command executed: {config_data['cli_command']}") elif client in [CLIENT_VSCODE, CLIENT_VSCODE_INSIDERS]: mcp_server_config = config_data.get("servers", {}).get( "singlestore-mcp-server", {} ) config_display = { "servers": { "...": "...", "singlestore-mcp-server": mcp_server_config, } } logger.info(json.dumps(config_display, indent=4)) else: mcp_server_config = config_data.get("mcpServers", {}).get( "singlestore-mcp-server", {} ) config_display = { "mcpServers": { "...": "...", "singlestore-mcp-server": mcp_server_config, } } logger.info(json.dumps(config_display, indent=4)) logger.info("Restart your LLM client to apply the changes.") return 0 else: logger.error("\nSetup failed. Please check the error messages above.") return 1

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/singlestore-labs/mcp-server-singlestore'

If you have feedback or need assistance with the MCP directory API, please join our Discord server