mirror of
https://github.com/aljazceru/mcp-python-sdk.git
synced 2025-12-18 22:44:20 +01:00
151 lines
4.8 KiB
Python
151 lines
4.8 KiB
Python
"""Claude app integration utilities."""
|
|
|
|
import json
|
|
import os
|
|
import shutil
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from mcp.server.fastmcp.utilities.logging import get_logger
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
MCP_PACKAGE = "mcp[cli]"
|
|
|
|
|
|
def get_claude_config_path() -> Path | None:
|
|
"""Get the Claude config directory based on platform."""
|
|
if sys.platform == "win32":
|
|
path = Path(Path.home(), "AppData", "Roaming", "Claude")
|
|
elif sys.platform == "darwin":
|
|
path = Path(Path.home(), "Library", "Application Support", "Claude")
|
|
elif sys.platform.startswith("linux"):
|
|
path = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"), "Claude")
|
|
else:
|
|
return None
|
|
|
|
if path.exists():
|
|
return path
|
|
return None
|
|
|
|
|
|
def get_uv_path() -> str:
|
|
"""Get the full path to the uv executable."""
|
|
uv_path = shutil.which("uv")
|
|
if not uv_path:
|
|
logger.error(
|
|
"uv executable not found in PATH, falling back to 'uv'. " "Please ensure uv is installed and in your PATH"
|
|
)
|
|
return "uv" # Fall back to just "uv" if not found
|
|
return uv_path
|
|
|
|
|
|
def update_claude_config(
|
|
file_spec: str,
|
|
server_name: str,
|
|
*,
|
|
with_editable: Path | None = None,
|
|
with_packages: list[str] | None = None,
|
|
env_vars: dict[str, str] | None = None,
|
|
) -> bool:
|
|
"""Add or update a FastMCP server in Claude's configuration.
|
|
|
|
Args:
|
|
file_spec: Path to the server file, optionally with :object suffix
|
|
server_name: Name for the server in Claude's config
|
|
with_editable: Optional directory to install in editable mode
|
|
with_packages: Optional list of additional packages to install
|
|
env_vars: Optional dictionary of environment variables. These are merged with
|
|
any existing variables, with new values taking precedence.
|
|
|
|
Raises:
|
|
RuntimeError: If Claude Desktop's config directory is not found, indicating
|
|
Claude Desktop may not be installed or properly set up.
|
|
"""
|
|
config_dir = get_claude_config_path()
|
|
uv_path = get_uv_path()
|
|
if not config_dir:
|
|
raise RuntimeError(
|
|
"Claude Desktop config directory not found. Please ensure Claude Desktop"
|
|
" is installed and has been run at least once to initialize its config."
|
|
)
|
|
|
|
config_file = config_dir / "claude_desktop_config.json"
|
|
if not config_file.exists():
|
|
try:
|
|
config_file.write_text("{}")
|
|
except Exception as e:
|
|
logger.error(
|
|
"Failed to create Claude config file",
|
|
extra={
|
|
"error": str(e),
|
|
"config_file": str(config_file),
|
|
},
|
|
)
|
|
return False
|
|
|
|
try:
|
|
config = json.loads(config_file.read_text())
|
|
if "mcpServers" not in config:
|
|
config["mcpServers"] = {}
|
|
|
|
# Always preserve existing env vars and merge with new ones
|
|
if server_name in config["mcpServers"] and "env" in config["mcpServers"][server_name]:
|
|
existing_env = config["mcpServers"][server_name]["env"]
|
|
if env_vars:
|
|
# New vars take precedence over existing ones
|
|
env_vars = {**existing_env, **env_vars}
|
|
else:
|
|
env_vars = existing_env
|
|
|
|
# Build uv run command
|
|
args = ["run"]
|
|
|
|
# Collect all packages in a set to deduplicate
|
|
packages = {MCP_PACKAGE}
|
|
if with_packages:
|
|
packages.update(pkg for pkg in with_packages if pkg)
|
|
|
|
# Add all packages with --with
|
|
for pkg in sorted(packages):
|
|
args.extend(["--with", pkg])
|
|
|
|
if with_editable:
|
|
args.extend(["--with-editable", str(with_editable)])
|
|
|
|
# Convert file path to absolute before adding to command
|
|
# Split off any :object suffix first
|
|
if ":" in file_spec:
|
|
file_path, server_object = file_spec.rsplit(":", 1)
|
|
file_spec = f"{Path(file_path).resolve()}:{server_object}"
|
|
else:
|
|
file_spec = str(Path(file_spec).resolve())
|
|
|
|
# Add fastmcp run command
|
|
args.extend(["mcp", "run", file_spec])
|
|
|
|
server_config: dict[str, Any] = {"command": uv_path, "args": args}
|
|
|
|
# Add environment variables if specified
|
|
if env_vars:
|
|
server_config["env"] = env_vars
|
|
|
|
config["mcpServers"][server_name] = server_config
|
|
|
|
config_file.write_text(json.dumps(config, indent=2))
|
|
logger.info(
|
|
f"Added server '{server_name}' to Claude config",
|
|
extra={"config_file": str(config_file)},
|
|
)
|
|
return True
|
|
except Exception as e:
|
|
logger.error(
|
|
"Failed to update Claude config",
|
|
extra={
|
|
"error": str(e),
|
|
"config_file": str(config_file),
|
|
},
|
|
)
|
|
return False
|