Refactor to use MCP resources for read operations (v1.0.2)

- Convert all read/list operations from tools to resources following MCP best practices
- Add @mcp.resource decorators for domains, records, and analysis endpoints
- Update version to 1.0.2
- Add uvx support documentation for Claude Desktop integration
- Fix CLI asyncio usage for FastMCP synchronous run() method
- Add vultr-mcp-server console script entry point

This improves alignment with MCP patterns where resources represent
readable data and tools perform actions that modify state.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ryan Malloy 2025-07-16 14:28:21 -06:00
parent 66c4c3cb18
commit 691963a43b
7 changed files with 99 additions and 20 deletions

View File

@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.2] - 2025-01-16
### Changed
- Refactored read operations to use MCP resources instead of tools
- List domains endpoint: `@mcp.resource("dns://domains")`
- Get domain endpoint: `@mcp.resource("dns://domains/{domain}")`
- List records endpoint: `@mcp.resource("dns://domains/{domain}/records")`
- Get record endpoint: `@mcp.resource("dns://domains/{domain}/records/{record_id}")`
- Analyze domain endpoint: `@mcp.resource("dns://domains/{domain}/analysis")`
### Improved
- Better alignment with MCP best practices (resources for read, tools for write)
- Enhanced Claude Desktop integration documentation with uvx support
## [1.0.1] - 2024-12-20
### Fixed

View File

@ -24,7 +24,9 @@ vultr-dns-mcp domains list
vultr-dns-mcp records list example.com
# As MCP server
uv run python -m vultr_dns_mcp
vultr-mcp-server
# or using Python module
python -m vultr_dns_mcp
```
## Development Setup
@ -135,6 +137,7 @@ vultr-dns-mcp/
- Built on FastMCP 2.0 framework for better Claude Desktop compatibility
- All tools use proper async/await patterns
- 12 comprehensive DNS management tools
- **Important**: FastMCP's `run()` method is synchronous, not async. Do not wrap with `asyncio.run()`
### MCP Tools (12 total)
- Domain management: list, create, delete, get details
@ -167,7 +170,8 @@ vultr-dns-mcp/
- **FEATURE**: Added HTTP request timeouts (30s total, 10s connect)
- **FEATURE**: Full uv package manager integration throughout project
- **FEATURE**: Created comprehensive Claude Desktop setup documentation
- **FIX**: Resolved event loop issues with proper async/await patterns
- **FIX**: Resolved event loop issues - FastMCP 2.0 uses synchronous `run()` method
- **FEATURE**: Added `vultr-mcp-server` console script entry point for easier Claude Desktop integration
- **IMPROVEMENT**: Enhanced README with professional structure and badges
- **IMPROVEMENT**: Added test suite for validating all improvements
@ -233,7 +237,7 @@ Use the GitHub Actions "Publish to PyPI" workflow with manual trigger.
### Common Issues
- **ImportError**: Run `uv sync` or `pip install -e .` from repository root
- **AsyncioError**: FastMCP handles async properly, ensure tools use `async def`
- **Event Loop Error**: Use `await` instead of `asyncio.run()` in FastMCP tools
- **Event Loop Error**: FastMCP 2.0's `run()` method is synchronous - do NOT use `asyncio.run()`
- **MCP Connection**: Ensure Claude Desktop config uses absolute Python path
- **API Errors**: Verify VULTR_API_KEY environment variable is set
@ -263,8 +267,8 @@ python -c "from vultr_dns_mcp.server import create_mcp_server"
{
"mcpServers": {
"vultr-dns": {
"command": "/path/to/python",
"args": ["-m", "vultr_dns_mcp"],
"command": "vultr-mcp-server",
"args": [],
"env": {
"VULTR_API_KEY": "your-api-key"
}

View File

@ -40,12 +40,28 @@ The configuration file location depends on your OS:
Add this to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"vultr-dns": {
"command": "vultr-mcp-server",
"args": [],
"env": {
"VULTR_API_KEY": "YOUR_VULTR_API_KEY_HERE"
}
}
}
}
```
**Note**: If `vultr-mcp-server` is not in your PATH, use the full Python module approach:
```json
{
"mcpServers": {
"vultr-dns": {
"command": "python",
"args": ["-m", "vultr_dns_mcp.server"],
"args": ["-m", "vultr_dns_mcp"],
"env": {
"VULTR_API_KEY": "YOUR_VULTR_API_KEY_HERE"
}
@ -61,7 +77,49 @@ Add this to your `claude_desktop_config.json`:
"mcpServers": {
"vultr-dns": {
"command": "uv",
"args": ["run", "python", "-m", "vultr_dns_mcp.server"],
"args": ["run", "vultr-mcp-server"],
"env": {
"VULTR_API_KEY": "YOUR_VULTR_API_KEY_HERE"
}
}
}
}
```
### 3a. Using uvx (Recommended for Easy Installation)
This approach uses `uvx` to automatically install and run the package without needing to manage Python environments:
```json
{
"mcpServers": {
"vultr-dns": {
"command": "uvx",
"args": [
"--from", "vultr-dns-mcp",
"vultr-mcp-server"
],
"env": {
"VULTR_API_KEY": "YOUR_VULTR_API_KEY_HERE"
}
}
}
}
```
For TestPyPI version:
```json
{
"mcpServers": {
"vultr-dns": {
"command": "uvx",
"args": [
"--index-url", "https://test.pypi.org/simple/",
"--extra-index-url", "https://pypi.org/simple/",
"--index-strategy", "unsafe-best-match",
"--from", "vultr-dns-mcp==1.0.1",
"vultr-mcp-server"
],
"env": {
"VULTR_API_KEY": "YOUR_VULTR_API_KEY_HERE"
}
@ -79,7 +137,7 @@ If you have issues, use the absolute path to your Python installation:
"mcpServers": {
"vultr-dns": {
"command": "/usr/bin/python3",
"args": ["-m", "vultr_dns_mcp.server"],
"args": ["-m", "vultr_dns_mcp"],
"env": {
"VULTR_API_KEY": "YOUR_VULTR_API_KEY_HERE"
}
@ -151,7 +209,9 @@ The server provides these tools that Claude Desktop can use:
2. **Test server manually**:
```bash
export VULTR_API_KEY="your-key"
python -m vultr_dns_mcp.server
vultr-mcp-server
# or
python -m vultr_dns_mcp
```
3. **Check API key**:
@ -185,7 +245,7 @@ If Claude Desktop can't find Python:
"mcpServers": {
"vultr-dns": {
"command": "/full/path/to/python3",
"args": ["-m", "vultr_dns_mcp.server"],
"args": ["-m", "vultr_dns_mcp"],
"env": {
"VULTR_API_KEY": "YOUR_KEY"
}
@ -209,8 +269,8 @@ If using a virtual environment:
{
"mcpServers": {
"vultr-dns": {
"command": "/path/to/your-venv/bin/python",
"args": ["-m", "vultr_dns_mcp.server"],
"command": "/path/to/your-venv/bin/vultr-mcp-server",
"args": [],
"env": {
"VULTR_API_KEY": "YOUR_KEY"
}

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "vultr-dns-mcp"
version = "1.0.1"
version = "1.0.2"
description = "A comprehensive Model Context Protocol (MCP) server for managing Vultr DNS records"
readme = "README.md"
license = {text = "MIT"}
@ -82,6 +82,7 @@ Changelog = "https://github.com/rsp2k/vultr-dns-mcp/blob/main/CHANGELOG.md"
[project.scripts]
vultr-dns-mcp = "vultr_dns_mcp.cli:main"
vultr-dns-server = "vultr_dns_mcp.cli:server_command"
vultr-mcp-server = "vultr_dns_mcp.fastmcp_server:run_server"
[tool.setuptools.packages.find]
where = ["src"]

View File

@ -1,4 +1,4 @@
"""Version information for vultr-dns-mcp package."""
__version__ = "1.0.1"
__version__ = "1.0.2"
__version_info__ = tuple(int(i) for i in __version__.split(".") if i.isdigit())

View File

@ -47,7 +47,7 @@ def server(ctx: click.Context):
click.echo(f"🔄 Press Ctrl+C to stop")
try:
asyncio.run(run_server(api_key))
run_server(api_key)
except KeyboardInterrupt:
click.echo("\n👋 Server stopped")
except Exception as e:

View File

@ -35,12 +35,12 @@ def create_vultr_mcp_server(api_key: Optional[str] = None) -> FastMCP:
# Initialize Vultr client
vultr_client = VultrDNSServer(api_key)
@mcp.tool
@mcp.resource("dns://domains")
async def list_dns_domains() -> List[Dict[str, Any]]:
"""List all DNS domains in your Vultr account."""
return await vultr_client.list_domains()
@mcp.tool
@mcp.resource("dns://domains/{domain}")
async def get_dns_domain(domain: str) -> Dict[str, Any]:
"""Get details for a specific DNS domain.
@ -70,7 +70,7 @@ def create_vultr_mcp_server(api_key: Optional[str] = None) -> FastMCP:
await vultr_client.delete_domain(domain)
return {"status": "success", "message": f"Domain {domain} deleted successfully"}
@mcp.tool
@mcp.resource("dns://domains/{domain}/records")
async def list_dns_records(domain: str) -> List[Dict[str, Any]]:
"""List all DNS records for a domain.
@ -79,7 +79,7 @@ def create_vultr_mcp_server(api_key: Optional[str] = None) -> FastMCP:
"""
return await vultr_client.list_records(domain)
@mcp.tool
@mcp.resource("dns://domains/{domain}/records/{record_id}")
async def get_dns_record(domain: str, record_id: str) -> Dict[str, Any]:
"""Get details for a specific DNS record.
@ -161,7 +161,7 @@ def create_vultr_mcp_server(api_key: Optional[str] = None) -> FastMCP:
"""
return await vultr_client.validate_record(record_type, name, data, ttl, priority)
@mcp.tool
@mcp.resource("dns://domains/{domain}/analysis")
async def analyze_dns_records(domain: str) -> Dict[str, Any]:
"""Analyze DNS records for a domain and provide recommendations.