Skip to content
Open
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
144 changes: 54 additions & 90 deletions README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions diagnostics/mcp_server_diagnostic.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ def check_mcp_environment():
print(" Create a config file with MCP servers to analyze real connections")
print()
print(" 📝 mcp-cli uses 'server_config.json' by default. Create one of:")
print(" 💡 Tip: cp src/mcp_cli/server_config.example.json server_config.json")
print(" • server_config.json (in current directory)")
print(" • ~/.config/mcp/config.json (standard MCP location)")
print(" • mcp_config.json (alternative name)")
Expand Down
2 changes: 1 addition & 1 deletion manifest.in
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Include documentation
include README.md
include server_config.json
include src/mcp_cli/server_config.example.json

# Include package files
recursive-include src/mcp_cli *.py
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ where = ["src"]
include = ["mcp_cli*"]

[tool.setuptools.package-data]
mcp_cli = ["server_config.json", "dashboard/static/**/*"]
mcp_cli = ["server_config.example.json", "dashboard/static/**/*"]
[dependency-groups]
dev = [
"colorama>=0.4.6",
Expand Down
21 changes: 15 additions & 6 deletions src/mcp_cli/chat/chat_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,17 @@ def create(
# ── Properties ────────────────────────────────────────────────────────
@property
def client(self) -> Any:
"""Get current LLM client (cached automatically by ModelManager)."""
return self.model_manager.get_client()
"""Get current LLM client (cached automatically by ModelManager).

Returns None if the client cannot be created (e.g. missing API key)
so the UI can start without crashing. The error is logged and the user
will see a proper message when they try to send a message.
"""
try:
return self.model_manager.get_client()
except Exception as e:
logger.error(f"Failed to create client for {self.provider}: {e}")
return None

@property
def provider(self) -> str:
Expand Down Expand Up @@ -460,11 +469,11 @@ async def initialize(
await self._initialize_session()

# Quick provider validation (non-blocking)
try:
_client = self.client # noqa: F841 — fails fast if no API key
_client = self.client # None if client could not be created
if _client is not None:
logger.info(f"Provider {self.provider} client created successfully")
except Exception as e:
logger.warning(f"Provider validation warning: {e}")
else:
logger.warning("Provider validation warning: client could not be created.")
logger.warning("Chat may fail when making API calls.")

if not self.tools:
Expand Down
5 changes: 3 additions & 2 deletions src/mcp_cli/chat/chat_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from mcp_cli.tools.manager import ToolManager
from mcp_cli.context import initialize_context
from mcp_cli.config import initialize_config
from mcp_cli.config.defaults import DEFAULT_PROVIDER, DEFAULT_MODEL
from mcp_cli.config.defaults import DEFAULT_PROVIDER, DEFAULT_MODEL, DEFAULT_CONFIG_FILENAME

# Set up logger
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -94,7 +94,8 @@ async def handle_chat_mode(
# Initialize configuration manager
from pathlib import Path

initialize_config(Path("server_config.json"))
# Use the project-wide constant instead of a hardcoded filename
initialize_config(Path(DEFAULT_CONFIG_FILENAME))

# Initialize global context manager for commands to work
app_context = initialize_context(
Expand Down
4 changes: 2 additions & 2 deletions src/mcp_cli/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ def load_runtime_config(
"""
from pathlib import Path

path = Path(config_path or "server_config.json")
path = Path(config_path or DEFAULT_CONFIG_FILENAME)
file_config = MCPConfig.load_sync(path)
return RuntimeConfig(file_config, cli_overrides)

Expand All @@ -290,7 +290,7 @@ async def load_runtime_config_async(
"""
from pathlib import Path

path = Path(config_path or "server_config.json")
path = Path(config_path or DEFAULT_CONFIG_FILENAME)
file_config = await MCPConfig.load_async(path)
return RuntimeConfig(file_config, cli_overrides)

Expand Down
26 changes: 4 additions & 22 deletions src/mcp_cli/config/cli_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from mcp_cli.config import (
MCPConfig,
DEFAULT_CONFIG_FILENAME,
setup_chuk_llm_environment,
trigger_discovery_after_setup,
detect_server_types,
Expand All @@ -29,43 +30,24 @@


def load_config(config_file: str) -> MCPConfig | None:
"""Load MCP server config file with fallback to bundled package config."""
"""Load MCP server config file."""
try:
config_path = Path(config_file)

# Try explicit path or current directory first
if config_path.is_file():
config = MCPConfig.load_from_file(config_path)
# If config loaded but has no servers and file exists, it might be invalid JSON
# Check if the file has content but failed to parse
if not config.servers and config_path.stat().st_size > 0:
try:
# Try to parse as JSON to verify it's valid
import json

content = config_path.read_text()
json.loads(content)
except json.JSONDecodeError:
# Invalid JSON - return None
return None
return config

# If not found and using default name, try package bundle
if config_file == "server_config.json":
try:
import importlib.resources as resources

# Try Python 3.9+ API
if hasattr(resources, "files"):
package_files = resources.files("mcp_cli")
bundled_config = package_files / "server_config.json"
if bundled_config.is_file():
logger.info("Loading bundled server configuration")
# Create a temporary Path object from the resource
return MCPConfig.load_from_file(Path(str(bundled_config)))
except (ImportError, FileNotFoundError, AttributeError, TypeError) as e:
logger.debug(f"Could not load bundled config: {e}")

except Exception as exc:
logger.error("Error loading config file '%s': %s", config_file, exc)
return None
Expand Down Expand Up @@ -106,7 +88,7 @@ def process_options(
disable_filesystem: bool,
provider: str,
model: str | None,
config_file: str = "server_config.json",
config_file: str = DEFAULT_CONFIG_FILENAME,
quiet: bool = False,
) -> tuple[list[str], list[str], dict[int, str]]:
"""
Expand Down Expand Up @@ -140,7 +122,7 @@ def process_options(
cfg = load_config(config_file)

if not cfg:
logger.warning(f"Could not load config file: {config_file}")
logger.info(f"Could not load config file: {config_file}")
# Return empty configuration
return [], user_specified, {}

Expand Down
25 changes: 3 additions & 22 deletions src/mcp_cli/config/config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from pydantic import BaseModel, Field

from mcp_cli.auth import OAuthConfig
from mcp_cli.config.defaults import DEFAULT_PROVIDER, DEFAULT_MODEL
from mcp_cli.config.defaults import DEFAULT_PROVIDER, DEFAULT_MODEL, DEFAULT_CONFIG_FILENAME
from mcp_cli.tools.models import ServerInfo, TransportType

# Import clean models from new config system
Expand Down Expand Up @@ -352,32 +352,13 @@ def initialize(self, config_path: Path | None = None) -> MCPConfig:

Priority order:
1. Explicit config_path if provided
2. server_config.json in current directory (overrides package default)
3. server_config.json bundled in package (fallback)
2. server_config.json in current directory
"""
if self._config is None:
if config_path:
# Explicit path provided
self._config_path = Path(config_path)
else:
# Check current directory first
cwd_config = Path("server_config.json")
if cwd_config.exists():
self._config_path = cwd_config
else:
# Fall back to package bundled config
import importlib.resources as resources

try:
package_files = resources.files("mcp_cli")
config_file = package_files / "server_config.json"
if config_file.is_file():
self._config_path = Path(str(config_file))
else:
self._config_path = cwd_config
except (ImportError, FileNotFoundError, AttributeError, TypeError):
# If package config doesn't exist or can't be accessed, use cwd
self._config_path = cwd_config
self._config_path = Path(DEFAULT_CONFIG_FILENAME)

self._config = MCPConfig.load_from_file(self._config_path)
return self._config
Expand Down
4 changes: 2 additions & 2 deletions src/mcp_cli/config/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,10 @@
# Provider/Model Defaults
# ================================================================

DEFAULT_PROVIDER = "openai"
DEFAULT_PROVIDER = "ollama"
"""Default LLM provider."""

DEFAULT_MODEL = "gpt-4o-mini"
DEFAULT_MODEL = "gpt-oss"
"""Default LLM model."""


Expand Down
6 changes: 3 additions & 3 deletions src/mcp_cli/context/context_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from pydantic import BaseModel, Field, ConfigDict, PrivateAttr, SkipValidation

from mcp_cli.config.defaults import DEFAULT_PROVIDER, DEFAULT_MODEL
from mcp_cli.config.defaults import DEFAULT_PROVIDER, DEFAULT_MODEL, DEFAULT_CONFIG_FILENAME
from mcp_cli.tools.manager import ToolManager
from mcp_cli.model_management import ModelManager
from mcp_cli.tools.models import ServerInfo, ToolInfo, ConversationMessage
Expand All @@ -35,7 +35,7 @@ class ApplicationContext(BaseModel):
model_manager: Annotated[ModelManager | None, SkipValidation()] = None

# Configuration
config_path: Path = Field(default_factory=lambda: Path("server_config.json"))
config_path: Path = Field(default_factory=lambda: Path(DEFAULT_CONFIG_FILENAME))
provider: str = DEFAULT_PROVIDER
model: str = DEFAULT_MODEL
api_base: str | None = None
Expand Down Expand Up @@ -90,7 +90,7 @@ def create(
"""
context = cls(
tool_manager=tool_manager,
config_path=config_path or Path("server_config.json"),
config_path=config_path or Path(DEFAULT_CONFIG_FILENAME),
provider=provider or DEFAULT_PROVIDER,
model=model or DEFAULT_MODEL,
**kwargs,
Expand Down
Loading
Loading