Skip to content

Big architecture refactor across the project #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 7, 2025
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ git clone https://github.com/jonigl/mcp-client-for-ollama.git
cd mcp-client-for-ollama
uv venv && source .venv/bin/activate
uv pip install .
uv run mcp_client_for_ollama/client.py
uv run -m mcp_client_for_ollama.client
```

## Usage
Expand Down
4 changes: 2 additions & 2 deletions cli-package/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "ollmcp"
version = "0.2.5"
version = "0.3.0"
description = "CLI for MCP Client for Ollama - An easy-to-use command for interacting with Ollama through MCP"
readme = "README.md"
requires-python = ">=3.10"
Expand All @@ -9,7 +9,7 @@ authors = [
{name = "Jonathan LΓΆwenstern"}
]
dependencies = [
"mcp-client-for-ollama==0.2.5"
"mcp-client-for-ollama==0.3.0"
]

[project.scripts]
Expand Down
2 changes: 1 addition & 1 deletion mcp_client_for_ollama/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""MCP Client for Ollama package."""

__version__ = "0.2.5"
__version__ = "0.3.0"
922 changes: 126 additions & 796 deletions mcp_client_for_ollama/client.py

Large diffs are not rendered by default.

Empty file.
42 changes: 42 additions & 0 deletions mcp_client_for_ollama/config/defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Default configuration settings for MCP Client for Ollama.

This module provides default settings and paths used throughout the application.
"""

import os
from ..utils.constants import DEFAULT_MODEL, DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_DIR

def default_config() -> dict:
"""Get default configuration settings.

Returns:
dict: Default configuration dictionary
"""

return {
"model": DEFAULT_MODEL,
"enabledTools": {}, # Will be populated with available tools
"contextSettings": {
"retainContext": True
}
}

def get_config_path(config_name: str = "default") -> str:
"""Get the path to a specific configuration file.

Args:
config_name: Name of the configuration (default: "default")

Returns:
str: Path to the configuration file
"""
# Ensure the directory exists
os.makedirs(DEFAULT_CONFIG_DIR, exist_ok=True)

# Sanitize the config name
config_name = ''.join(c for c in config_name if c.isalnum() or c in ['-', '_']).lower() or "default"

if config_name == "default":
return os.path.join(DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILE)
else:
return os.path.join(DEFAULT_CONFIG_DIR, f"{config_name}.json")
190 changes: 190 additions & 0 deletions mcp_client_for_ollama/config/manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
"""Configuration management for MCP Client for Ollama.

This module handles loading, saving, and validating configuration settings for
the MCP Client for Ollama, including tool settings and model preferences.
"""

import json
import os
from typing import Dict, Any, Optional
from rich.console import Console
from rich.panel import Panel
from ..utils.constants import DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILE
from .defaults import default_config

class ConfigManager:
"""Manages configuration for the MCP Client for Ollama.

This class handles loading, saving, and validating configuration settings,
including enabled tools, selected model, and context retention preferences.
"""

def __init__(self, console: Optional[Console] = None):
"""Initialize the ConfigManager.

Args:
console: Rich console for output (optional)
"""
self.console = console or Console()

def load_configuration(self, config_name: Optional[str] = None) -> Dict[str, Any]:
"""Load tool configuration and model settings from a file.

Args:
config_name: Optional name of the config to load (defaults to 'default')

Returns:
Dict containing the configuration settings
"""
# Default to 'default' if no config name provided
if not config_name:
config_name = "default"

# Sanitize filename
config_name = self._sanitize_config_name(config_name)

# Create config file path
config_path = self._get_config_path(config_name)

# Check if config file exists
if not os.path.exists(config_path):
self.console.print(Panel(
f"[yellow]Configuration file not found:[/yellow]\n"
f"[blue]{config_path}[/blue]",
title="Config Not Found", border_style="yellow", expand=False
))
return default_config()

# Read config file
try:
with open(config_path, 'r') as f:
config_data = json.load(f)

# Validate loaded configuration and provide defaults for missing fields
validated_config = self._validate_config(config_data)

self.console.print(Panel(
f"[green]Configuration loaded successfully from:[/green]\n"
f"[blue]{config_path}[/blue]",
title="Config Loaded", border_style="green", expand=False
))
return validated_config

except Exception as e:
self.console.print(Panel(
f"[red]Error loading configuration:[/red]\n"
f"{str(e)}",
title="Error", border_style="red", expand=False
))
return default_config()

def save_configuration(self, config_data: Dict[str, Any], config_name: Optional[str] = None) -> bool:
"""Save tool configuration and model settings to a file.

Args:
config_data: Dictionary containing the configuration to save
config_name: Optional name for the config (defaults to 'default')

Returns:
bool: True if saved successfully, False otherwise
"""
# Create config directory if it doesn't exist
os.makedirs(DEFAULT_CONFIG_DIR, exist_ok=True)

# Default to 'default' if no config name provided
if not config_name:
config_name = "default"

# Sanitize filename
config_name = self._sanitize_config_name(config_name)

# Create config file path
config_path = self._get_config_path(config_name)

# Write to file
try:
with open(config_path, 'w') as f:
json.dump(config_data, f, indent=2)

self.console.print(Panel(
f"[green]Configuration saved successfully to:[/green]\n"
f"[blue]{config_path}[/blue]",
title="Config Saved", border_style="green", expand=False
))
return True

except Exception as e:
self.console.print(Panel(
f"[red]Error saving configuration:[/red]\n"
f"{str(e)}",
title="Error", border_style="red", expand=False
))
return False

def reset_configuration(self) -> Dict[str, Any]:
"""Reset tool configuration to default (all tools enabled).

Returns:
Dict containing the default configuration
"""
config = default_config()

self.console.print(Panel(
"[green]Configuration reset to defaults![/green]\n"
"β€’ All tools enabled\n"
"β€’ Context retention enabled",
title="Config Reset", border_style="green", expand=False
))

return config

def _sanitize_config_name(self, config_name: str) -> str:
"""Sanitize configuration name for use in filenames.

Args:
config_name: Name to sanitize

Returns:
str: Sanitized name safe for use in filenames
"""
sanitized = ''.join(c for c in config_name if c.isalnum() or c in ['-', '_']).lower()
return sanitized or "default"

def _get_config_path(self, config_name: str) -> str:
"""Get the full path to a configuration file.

Args:
config_name: Name of the configuration

Returns:
str: Full path to the configuration file
"""
if config_name == "default":
return os.path.join(DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILE)
else:
return os.path.join(DEFAULT_CONFIG_DIR, f"{config_name}.json")

def _validate_config(self, config_data: Dict[str, Any]) -> Dict[str, Any]:
"""Validate configuration data and provide defaults for missing fields.

Args:
config_data: Configuration data to validate

Returns:
Dict: Validated configuration with defaults applied where needed
"""
# Start with default configuration
validated = default_config()

# Apply values from the loaded configuration if they exist
if "model" in config_data:
validated["model"] = config_data["model"]

if "enabledTools" in config_data and isinstance(config_data["enabledTools"], dict):
validated["enabledTools"] = config_data["enabledTools"]

if "contextSettings" in config_data and isinstance(config_data["contextSettings"], dict):
if "retainContext" in config_data["contextSettings"]:
validated["contextSettings"]["retainContext"] = bool(config_data["contextSettings"]["retainContext"])

return validated
Empty file.
Loading
Loading