Files
my-pal-mcp-server/tools/version.py
Beehive Innovations c960bcb720 Add DocGen tool with comprehensive documentation generation capabilities (#109)
* WIP: new workflow architecture

* WIP: further improvements and cleanup

* WIP: cleanup and docks, replace old tool with new

* WIP: cleanup and docks, replace old tool with new

* WIP: new planner implementation using workflow

* WIP: precommit tool working as a workflow instead of a basic tool
Support for passing False to use_assistant_model to skip external models completely and use Claude only

* WIP: precommit workflow version swapped with old

* WIP: codereview

* WIP: replaced codereview

* WIP: replaced codereview

* WIP: replaced refactor

* WIP: workflow for thinkdeep

* WIP: ensure files get embedded correctly

* WIP: thinkdeep replaced with workflow version

* WIP: improved messaging when an external model's response is received

* WIP: analyze tool swapped

* WIP: updated tests
* Extract only the content when building history
* Use "relevant_files" for workflow tools only

* WIP: updated tests
* Extract only the content when building history
* Use "relevant_files" for workflow tools only

* WIP: fixed get_completion_next_steps_message missing param

* Fixed tests
Request for files consistently

* Fixed tests
Request for files consistently

* Fixed tests

* New testgen workflow tool
Updated docs

* Swap testgen workflow

* Fix CI test failures by excluding API-dependent tests

- Update GitHub Actions workflow to exclude simulation tests that require API keys
- Fix collaboration tests to properly mock workflow tool expert analysis calls
- Update test assertions to handle new workflow tool response format
- Ensure unit tests run without external API dependencies in CI

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* WIP - Update tests to match new tools

* WIP - Update tests to match new tools

* WIP - Update tests to match new tools

* Should help with https://github.com/BeehiveInnovations/zen-mcp-server/issues/97
Clear python cache when running script: https://github.com/BeehiveInnovations/zen-mcp-server/issues/96
Improved retry error logging
Cleanup

* WIP - chat tool using new architecture and improved code sharing

* Removed todo

* Removed todo

* Cleanup old name

* Tweak wordings

* Tweak wordings
Migrate old tests

* Support for Flash 2.0 and Flash Lite 2.0

* Support for Flash 2.0 and Flash Lite 2.0

* Support for Flash 2.0 and Flash Lite 2.0
Fixed test

* Improved consensus to use the workflow base class

* Improved consensus to use the workflow base class

* Allow images

* Allow images

* Replaced old consensus tool

* Cleanup tests

* Tests for prompt size

* New tool: docgen
Tests for prompt size
Fixes: https://github.com/BeehiveInnovations/zen-mcp-server/issues/107
Use available token size limits: https://github.com/BeehiveInnovations/zen-mcp-server/issues/105

* Improved docgen prompt
Exclude TestGen from pytest inclusion

* Updated errors

* Lint

* DocGen instructed not to fix bugs, surface them and stick to d

* WIP

* Stop claude from being lazy and only documenting a small handful

* More style rules

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-06-22 10:21:19 +04:00

351 lines
13 KiB
Python

"""
Version Tool - Display Zen MCP Server version and system information
This tool provides version information about the Zen MCP Server including
version number, last update date, author, and basic system information.
It also checks for updates from the GitHub repository.
"""
import logging
import platform
import re
import sys
from pathlib import Path
from typing import Any, Optional
try:
from urllib.error import HTTPError, URLError
from urllib.request import urlopen
HAS_URLLIB = True
except ImportError:
HAS_URLLIB = False
from mcp.types import TextContent
from config import __author__, __updated__, __version__
from tools.models import ToolModelCategory, ToolOutput
from tools.shared.base_models import ToolRequest
from tools.shared.base_tool import BaseTool
logger = logging.getLogger(__name__)
def parse_version(version_str: str) -> tuple[int, int, int]:
"""
Parse version string to tuple of integers for comparison.
Args:
version_str: Version string like "5.5.5"
Returns:
Tuple of (major, minor, patch) as integers
"""
try:
parts = version_str.strip().split(".")
if len(parts) >= 3:
return (int(parts[0]), int(parts[1]), int(parts[2]))
elif len(parts) == 2:
return (int(parts[0]), int(parts[1]), 0)
elif len(parts) == 1:
return (int(parts[0]), 0, 0)
else:
return (0, 0, 0)
except (ValueError, IndexError):
return (0, 0, 0)
def compare_versions(current: str, remote: str) -> int:
"""
Compare two version strings.
Args:
current: Current version string
remote: Remote version string
Returns:
-1 if current < remote (update available)
0 if current == remote (up to date)
1 if current > remote (ahead of remote)
"""
current_tuple = parse_version(current)
remote_tuple = parse_version(remote)
if current_tuple < remote_tuple:
return -1
elif current_tuple > remote_tuple:
return 1
else:
return 0
def fetch_github_version() -> Optional[tuple[str, str]]:
"""
Fetch the latest version information from GitHub repository.
Returns:
Tuple of (version, last_updated) if successful, None if failed
"""
if not HAS_URLLIB:
logger.warning("urllib not available, cannot check for updates")
return None
github_url = "https://raw.githubusercontent.com/BeehiveInnovations/zen-mcp-server/main/config.py"
try:
# Set a 10-second timeout
with urlopen(github_url, timeout=10) as response:
if response.status != 200:
logger.warning(f"HTTP error while checking GitHub: {response.status}")
return None
content = response.read().decode("utf-8")
# Extract version using regex
version_match = re.search(r'__version__\s*=\s*["\']([^"\']+)["\']', content)
updated_match = re.search(r'__updated__\s*=\s*["\']([^"\']+)["\']', content)
if version_match:
remote_version = version_match.group(1)
remote_updated = updated_match.group(1) if updated_match else "Unknown"
return (remote_version, remote_updated)
else:
logger.warning("Could not parse version from GitHub config.py")
return None
except HTTPError as e:
logger.warning(f"HTTP error while checking GitHub: {e.code}")
return None
except URLError as e:
logger.warning(f"URL error while checking GitHub: {e.reason}")
return None
except Exception as e:
logger.warning(f"Error checking GitHub for updates: {e}")
return None
class VersionTool(BaseTool):
"""
Tool for displaying Zen MCP Server version and system information.
This tool provides:
- Current server version
- Last update date
- Author information
- Python version
- Platform information
"""
def get_name(self) -> str:
return "version"
def get_description(self) -> str:
return (
"VERSION & CONFIGURATION - Get server version, configuration details, and list of available tools. "
"Useful for debugging and understanding capabilities."
)
def get_input_schema(self) -> dict[str, Any]:
"""Return the JSON schema for the tool's input"""
return {"type": "object", "properties": {}, "required": []}
def get_system_prompt(self) -> str:
"""No AI model needed for this tool"""
return ""
def get_request_model(self):
"""Return the Pydantic model for request validation."""
return ToolRequest
async def prepare_prompt(self, request: ToolRequest) -> str:
"""Not used for this utility tool"""
return ""
def format_response(self, response: str, request: ToolRequest, model_info: dict = None) -> str:
"""Not used for this utility tool"""
return response
async def execute(self, arguments: dict[str, Any]) -> list[TextContent]:
"""
Display Zen MCP Server version and system information.
This overrides the base class execute to provide direct output without AI model calls.
Args:
arguments: Standard tool arguments (none required)
Returns:
Formatted version and system information
"""
output_lines = ["# Zen MCP Server Version\n"]
# Server version information
output_lines.append("## Server Information")
output_lines.append(f"**Current Version**: {__version__}")
output_lines.append(f"**Last Updated**: {__updated__}")
output_lines.append(f"**Author**: {__author__}")
# Get the current working directory (MCP server location)
current_path = Path.cwd()
output_lines.append(f"**Installation Path**: `{current_path}`")
output_lines.append("")
# Check for updates from GitHub
output_lines.append("## Update Status")
try:
github_info = fetch_github_version()
if github_info:
remote_version, remote_updated = github_info
comparison = compare_versions(__version__, remote_version)
output_lines.append(f"**Latest Version (GitHub)**: {remote_version}")
output_lines.append(f"**Latest Updated**: {remote_updated}")
if comparison < 0:
# Update available
output_lines.append("")
output_lines.append("🚀 **UPDATE AVAILABLE!**")
output_lines.append(
f"Your version `{__version__}` is older than the latest version `{remote_version}`"
)
output_lines.append("")
output_lines.append("**To update:**")
output_lines.append("```bash")
output_lines.append(f"cd {current_path}")
output_lines.append("git pull")
output_lines.append("```")
output_lines.append("")
output_lines.append("*Note: Restart your Claude session after updating to use the new version.*")
elif comparison == 0:
# Up to date
output_lines.append("")
output_lines.append("✅ **UP TO DATE**")
output_lines.append("You are running the latest version.")
else:
# Ahead of remote (development version)
output_lines.append("")
output_lines.append("🔬 **DEVELOPMENT VERSION**")
output_lines.append(
f"Your version `{__version__}` is ahead of the published version `{remote_version}`"
)
output_lines.append("You may be running a development or custom build.")
else:
output_lines.append("❌ **Could not check for updates**")
output_lines.append("Unable to connect to GitHub or parse version information.")
output_lines.append("Check your internet connection or try again later.")
except Exception as e:
logger.error(f"Error during version check: {e}")
output_lines.append("❌ **Error checking for updates**")
output_lines.append(f"Error: {str(e)}")
output_lines.append("")
# Python and system information
output_lines.append("## System Information")
output_lines.append(
f"**Python Version**: {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
)
output_lines.append(f"**Platform**: {platform.system()} {platform.release()}")
output_lines.append(f"**Architecture**: {platform.machine()}")
output_lines.append("")
# Available tools
try:
# Import here to avoid circular imports
from server import TOOLS
tool_names = sorted(TOOLS.keys())
output_lines.append("## Available Tools")
output_lines.append(f"**Total Tools**: {len(tool_names)}")
output_lines.append("\n**Tool List**:")
for tool_name in tool_names:
tool = TOOLS[tool_name]
# Get the first line of the tool's description for a brief summary
description = tool.get_description().split("\n")[0]
# Truncate if too long
if len(description) > 80:
description = description[:77] + "..."
output_lines.append(f"- `{tool_name}` - {description}")
output_lines.append("")
except Exception as e:
logger.warning(f"Error loading tools list: {e}")
output_lines.append("## Available Tools")
output_lines.append("**Error**: Could not load tools list")
output_lines.append("")
# Configuration information
output_lines.append("## Configuration")
# Check for configured providers
try:
from providers.base import ProviderType
from providers.registry import ModelProviderRegistry
provider_status = []
# Check each provider type
provider_types = [
ProviderType.GOOGLE,
ProviderType.OPENAI,
ProviderType.XAI,
ProviderType.OPENROUTER,
ProviderType.CUSTOM,
]
provider_names = ["Google Gemini", "OpenAI", "X.AI", "OpenRouter", "Custom/Local"]
for provider_type, provider_name in zip(provider_types, provider_names):
provider = ModelProviderRegistry.get_provider(provider_type)
status = "✅ Configured" if provider is not None else "❌ Not configured"
provider_status.append(f"- **{provider_name}**: {status}")
output_lines.append("**Providers**:")
output_lines.extend(provider_status)
# Get total available models
try:
available_models = ModelProviderRegistry.get_available_models(respect_restrictions=True)
output_lines.append(f"\n**Available Models**: {len(available_models)}")
except Exception:
output_lines.append("\n**Available Models**: Unknown")
except Exception as e:
logger.warning(f"Error checking provider configuration: {e}")
output_lines.append("**Providers**: Error checking configuration")
output_lines.append("")
# Usage information
output_lines.append("## Usage")
output_lines.append("- Use `listmodels` tool to see all available AI models")
output_lines.append("- Use `chat` for interactive conversations and brainstorming")
output_lines.append("- Use workflow tools (`debug`, `codereview`, `docgen`, etc.) for systematic analysis")
output_lines.append("- Set DEFAULT_MODEL=auto to let Claude choose the best model for each task")
# Format output
content = "\n".join(output_lines)
tool_output = ToolOutput(
status="success",
content=content,
content_type="text",
metadata={
"tool_name": self.name,
"server_version": __version__,
"last_updated": __updated__,
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
"platform": f"{platform.system()} {platform.release()}",
},
)
return [TextContent(type="text", text=tool_output.model_dump_json())]
def get_model_category(self) -> ToolModelCategory:
"""Return the model category for this tool."""
return ToolModelCategory.FAST_RESPONSE # Simple version info, no AI needed