#
tokens: 8427/50000 16/16 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── claude_desktop_config.json
├── claude_document_mcp
│   ├── __init__.py
│   ├── __main__.py
│   └── server.py
├── CLAUDE_INTEGRATION.md
├── pyproject.toml
├── README.md
├── requirements.txt
├── run.py
├── run.sh
├── setup.sh
├── start_server.sh
├── test_client.py
├── test_server.sh
├── uv.lock
└── verify.py
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
.venv/
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# Environment variables
.env
.env.*

# Logs
logs/
*.log

# macOS
.DS_Store
.AppleDouble
.LSOverride
._*

# VS Code
.vscode/

# PyCharm
.idea/

# Jupiter
.ipynb_checkpoints

# Testing
.pytest_cache/
htmlcov/
.coverage
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Claude Document MCP Server

A Model Context Protocol (MCP) server that allows Claude Desktop to perform document operations on Microsoft Word, Excel, and PDF files.

## Features

### Microsoft Word Operations
- Create new Word documents from text
- Edit existing Word documents (add/edit/delete paragraphs and headings)
- Convert text files (.txt) to Word documents

### Excel Operations
- Create new Excel spreadsheets from JSON or CSV-like text
- Edit existing Excel files (update cells, ranges, add/delete rows, columns, sheets)
- Convert CSV files to Excel

### PDF Operations
- Create new PDF files from text
- Convert Word documents to PDF files

## Setup

This MCP server requires Python 3.10 or higher.

### Automatic Setup (Recommended)

Run the setup script to automatically install dependencies and configure for Claude Desktop:

```bash
git clone https://github.com/alejandroBallesterosC/document-edit-mcp
cd document-edit-mcp
./setup.sh
```

This will:
1. Create a virtual environment
2. Install required dependencies
3. Configure the server for Claude Desktop
4. Create necessary directories

### Manual Setup

If you prefer to set up manually:

1. Install dependencies:

```bash
cd claude-document-mcp
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
pip install -e .
```

2. Configure Claude Desktop:

Copy the `claude_desktop_config.json` file to:
- **Mac**: `~/Library/Application Support/Claude/`
- **Windows**: `%APPDATA%\Claude\`

3. Restart Claude Desktop

## Model Context Protocol Integration

This server follows the Model Context Protocol specification to provide document manipulation capabilities for Claude Desktop:

- **Tools**: Provides manipulations functions for Word, Excel, and PDF operations
- **Resources**: Provides information about capabilities
- **Prompts**: (none currently implemented)

## API Reference

### Microsoft Word

#### Create a Word Document
```
create_word_document(filepath: str, content: str) -> Dict
```

#### Edit a Word Document
```
edit_word_document(filepath: str, operations: List[Dict]) -> Dict
```

#### Convert TXT to Word
```
convert_txt_to_word(source_path: str, target_path: str) -> Dict
```

### Excel

#### Create an Excel File
```
create_excel_file(filepath: str, content: str) -> Dict
```

#### Edit an Excel File
```
edit_excel_file(filepath: str, operations: List[Dict]) -> Dict
```

#### Convert CSV to Excel
```
convert_csv_to_excel(source_path: str, target_path: str) -> Dict
```

### PDF

#### Create a PDF File
```
create_pdf_file(filepath: str, content: str) -> Dict
```

#### Convert Word to PDF
```
convert_word_to_pdf(source_path: str, target_path: str) -> Dict
```

## Logs

The server logs all operations to both the console and a `logs/document_mcp.log` file for troubleshooting.

## License

MIT

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

```

--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------

```
uvicorn
fastapi
python-docx
pandas
openpyxl
reportlab
pdf2docx
docx2pdf

```

--------------------------------------------------------------------------------
/claude_document_mcp/__init__.py:
--------------------------------------------------------------------------------

```python
"""
Claude Document MCP - A Model Context Protocol server for document operations
"""

__version__ = "0.1.0"

```

--------------------------------------------------------------------------------
/claude_document_mcp/__main__.py:
--------------------------------------------------------------------------------

```python
"""
Claude Document MCP - CLI entry point
"""

import sys
from claude_document_mcp.server import main

if __name__ == "__main__":
    sys.exit(main())

```

--------------------------------------------------------------------------------
/run.py:
--------------------------------------------------------------------------------

```python
#!/usr/bin/env python3
"""
Run script for Claude Document MCP Server
"""

from claude_document_mcp.server import main

if __name__ == "__main__":
    main()

```

--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash
# Run the Claude Document MCP Server directly

# Get the project directory
PROJECT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)

# Run the server with UV (using --project parameter)
echo "Starting Document MCP Server..."
uv run --project "$PROJECT_DIR" -m claude_document_mcp.server
```

--------------------------------------------------------------------------------
/claude_desktop_config.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "document_operations": {
      "command": "python",
      "args": [
        "-m", 
        "claude_document_mcp.server"
      ],
      "cwd": "/Users/jandro/Documents/Coding-Projects/mcp-servers/claude-document-mcp",
      "env": {
        "PYTHONUNBUFFERED": "1"
      }
    }
  }
}

```

--------------------------------------------------------------------------------
/start_server.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash

# Start the Claude Document MCP Server

# Activate the virtual environment
if [ -d ".venv" ]; then
    source .venv/bin/activate
else
    echo "Creating virtual environment with UV..."
    uv venv
    source .venv/bin/activate
    
    echo "Installing dependencies with UV..."
    uv pip install -e .
fi

# Start the server
echo "Starting Document MCP Server..."
python -m claude_document_mcp

```

--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------

```toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "claude-document-mcp"
version = "0.1.0"
description = "Model Context Protocol server for document operations with Claude Desktop"
readme = "README.md"
requires-python = ">=3.10"
license = { text = "MIT" }
authors = [
    { name = "Your Name", email = "[email protected]" }
]

dependencies = [
    "mcp[cli]>=1.5.0",
    "python-docx>=0.8.11",
    "pandas>=2.0.0",
    "openpyxl>=3.1.0",
    "reportlab>=3.6.0",
    "pdf2docx>=0.5.6",
    "docx2pdf>=0.1.8"
]

[project.optional-dependencies]
dev = [
    "pytest>=7.0.0",
    "black>=22.0.0",
    "isort>=5.10.0"
]

[tool.hatch.build.targets.wheel]
packages = ["claude_document_mcp"]

```

--------------------------------------------------------------------------------
/setup.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash
# Setup script for Claude Document MCP Server

set -e  # Exit on error

echo "Setting up Claude Document MCP Server..."

# Check Python version
python_version=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
min_version="3.10"

if [ "$(printf '%s\n' "$min_version" "$python_version" | sort -V | head -n1)" != "$min_version" ]; then 
    echo "Error: Python $min_version or higher is required"
    echo "Current version: $python_version"
    exit 1
fi

# Create virtual environment first (using UV)
echo "Creating virtual environment with UV..."
uv sync

# Now install the project in development mode
echo "Installing project in development mode with UV..."
uv pip install -e .

# Create logs directory
mkdir -p logs

# Get the absolute path to the project directory
PROJECT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)

echo ""
echo "Setup complete! You can now use the Document MCP Server with Claude Desktop."
echo "Start Claude Desktop to use the document tools."
echo ""
```

--------------------------------------------------------------------------------
/test_server.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash

# This script tests the Document MCP Server by running it and confirming it works

# Create temporary directory for test files
TEST_DIR=$(mktemp -d)
echo "Created test directory: $TEST_DIR"

# Create a test text file
TEXT_FILE="$TEST_DIR/test.txt"
echo "This is a test text file." > "$TEXT_FILE"
echo "It has multiple lines." >> "$TEXT_FILE"
echo "We'll convert it to Word." >> "$TEXT_FILE"
echo "Created text file: $TEXT_FILE"

# The main test process
# This will start the server in the background and run tests against it
echo "Starting Document MCP Server validation..."

# Start the server with the MCP dev tool (this includes the inspector)
echo "Starting server..."
mcp dev claude_document_mcp/server.py:mcp &
SERVER_PID=$!

# Wait for server to start
sleep 3

echo "Server started with PID: $SERVER_PID"
echo "To view the server in the MCP Inspector, visit: http://localhost:9000"
echo ""
echo "Press Ctrl+C to stop the server when you're done testing"

# Keep the script running so the server stays up
wait $SERVER_PID

# Clean up
echo "Cleaning up test files..."
rm -rf "$TEST_DIR"
echo "Done."

```

--------------------------------------------------------------------------------
/CLAUDE_INTEGRATION.md:
--------------------------------------------------------------------------------

```markdown
# Integrating with Claude Desktop

To integrate this Document MCP Server with Claude Desktop, follow these steps:

## 1. Install the Server

First, make sure you have installed the server with dependencies:

```bash
cd claude-document-mcp
uv venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
uv pip install -e .
```

## 2. Configure Claude Desktop

1. Find your Claude Desktop configuration file:
   - **Mac**: `~/Library/Application Support/Claude/claude_desktop_config.json`
   - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`

2. Create or edit this file to include the following configuration:

```json
{
  "mcpServers": {
    "document_operations": {
      "command": "uv",
      "args": [
        "--directory", "/ABSOLUTE/PATH/TO/claude-document-mcp", 
        "run", 
        "mcp", 
        "run", 
        "claude_document_mcp/server.py:mcp"
      ]
    }
  }
}
```

Replace `/ABSOLUTE/PATH/TO/claude-document-mcp` with the actual absolute path to your project directory.

3. Restart Claude Desktop

## 3. Test the Integration

1. Open Claude Desktop
2. You should see a hammer icon in the UI if the MCP server is detected
3. Try a test request like: "Can you create a new Word document with a simple hello world message and save it to my Desktop?"

## Troubleshooting

If Claude Desktop doesn't detect the server:

1. Check the logs directory for errors
2. Verify your Claude Desktop config file has the correct path
3. Make sure the MCP server runs correctly on its own (use `./test_server.sh`)
4. Restart Claude Desktop after any changes

```

--------------------------------------------------------------------------------
/test_client.py:
--------------------------------------------------------------------------------

```python
#!/usr/bin/env python3
"""
Simple test client for the Document MCP Server
"""

import asyncio
import json
import os
import sys
import tempfile
from pathlib import Path
from contextlib import asynccontextmanager

from mcp import ClientSession
from mcp.client.stdio import StdioServerParameters, stdio_client


async def test_word_operations(session):
    print("\n\nTesting Word operations...")
    
    # Create a temp directory for test files
    with tempfile.TemporaryDirectory() as temp_dir:
        # Create a text file
        txt_path = os.path.join(temp_dir, "test.txt")
        with open(txt_path, "w") as f:
            f.write("This is a test text file.\nIt has multiple lines.\nWe'll convert it to Word.")
        
        # Create a Word document
        word_path = os.path.join(temp_dir, "test_created.docx")
        
        print("Creating Word document...")
        result = await session.call_tool(
            "create_word_document",
            {"filepath": word_path, "content": "This is a test Word document created by the MCP server."}
        )
        print(f"Result: {json.dumps(result, indent=2)}")
        
        if result.get("success"):
            # Edit the Word document
            print("\nEditing Word document...")
            result = await session.call_tool(
                "edit_word_document",
                {
                    "filepath": word_path,
                    "operations": [
                        {"type": "add_paragraph", "text": "This is a new paragraph."},
                        {"type": "add_heading", "text": "Test Heading", "level": 1}
                    ]
                }
            )
            print(f"Result: {json.dumps(result, indent=2)}")
        
        # Convert text to Word
        word_converted_path = os.path.join(temp_dir, "test_converted.docx")
        print("\nConverting TXT to Word...")
        result = await session.call_tool(
            "convert_txt_to_word",
            {"source_path": txt_path, "target_path": word_converted_path}
        )
        print(f"Result: {json.dumps(result, indent=2)}")


async def test_capabilities(session):
    print("\n\nGetting server capabilities...")
    result = await session.get_resource("capabilities://")
    print(f"Capabilities: {json.dumps(json.loads(result), indent=2)}")


@asynccontextmanager
async def connect_to_server():
    """Helper to connect to the MCP server."""
    server_params = StdioServerParameters(
        command="python",
        args=["-m", "claude_document_mcp.server"],
        cwd=os.path.dirname(os.path.abspath(__file__))
    )
    
    client = stdio_client(server_params)
    session = await client.connect()
    
    try:
        yield session
    finally:
        await session.close()


async def main():
    print("Document MCP Server Test Client")
    print("===============================")
    
    try:
        # Connect to the MCP server using stdio
        print("Connecting to server...")
        
        async with connect_to_server() as session:
            print("Connected!")
            
            # Get server capabilities
            await test_capabilities(session)
            
            # Test Word operations
            await test_word_operations(session)
            
            print("\nAll tests completed!")
    except Exception as e:
        print(f"Error: {str(e)}")
        import traceback
        traceback.print_exc()


if __name__ == "__main__":
    asyncio.run(main())

```

--------------------------------------------------------------------------------
/verify.py:
--------------------------------------------------------------------------------

```python
#!/usr/bin/env python3
"""
Verification script for Claude Document MCP Server

This script verifies that the environment is correctly set up and that
all dependencies are installed properly.
"""

import sys
import importlib
import os
import subprocess
from pathlib import Path

def run_uv_command(args):
    """Run a UV command and return the output."""
    cmd = ["uv"] + args
    try:
        return subprocess.check_output(cmd, universal_newlines=True)
    except subprocess.CalledProcessError as e:
        print(f"Error running UV command: {e}")
        return None

def check_dependency(module_name):
    """Check if a Python module is installed."""
    try:
        importlib.import_module(module_name)
        return True
    except ImportError:
        return False

def main():
    """Main verification function."""
    print("Claude Document MCP Server Verification")
    print("======================================")
    
    # Check if UV is installed
    try:
        uv_version = subprocess.check_output(["uv", "--version"], universal_newlines=True).strip()
        print(f"UV installed: Yes (version {uv_version})")
    except (subprocess.CalledProcessError, FileNotFoundError):
        print("UV installed: No")
        print("ERROR: UV is not installed or not in PATH. Please install UV first.")
        return False
    
    # Check Python version
    print(f"Python version: {sys.version}")
    
    # Check if the project is installed
    print("\nChecking dependencies...")
    
    # Try to import the project
    can_import = check_dependency("claude_document_mcp")
    print(f"claude_document_mcp importable: {'Yes' if can_import else 'No'}")
    
    # Check MCP dependency
    if check_dependency("mcp"):
        import mcp
        print(f"MCP installed: Yes (version {getattr(mcp, '__version__', 'unknown')})")
    else:
        print("MCP installed: No")
    
    # Check other dependencies
    dependencies = [
        "docx", 
        "pandas", 
        "openpyxl", 
        "reportlab", 
        "pdf2docx", 
        "docx2pdf"
    ]
    
    missing_deps = []
    for dep in dependencies:
        installed = check_dependency(dep)
        print(f"{dep} installed: {'Yes' if installed else 'No'}")
        if not installed:
            missing_deps.append(dep)
    
    if missing_deps:
        print(f"\nMissing dependencies: {', '.join(missing_deps)}")
        print("Run 'uv sync' to install missing dependencies")
    
    # Check if logs directory exists
    logs_dir = Path(__file__).parent / "logs"
    if logs_dir.exists():
        print(f"\nLogs directory exists: Yes ({logs_dir})")
    else:
        print(f"\nLogs directory exists: No")
        print(f"Creating logs directory at {logs_dir}")
        logs_dir.mkdir(exist_ok=True)
    
    # Check if Claude Desktop config exists
    if sys.platform == "darwin":
        config_path = Path.home() / "Library/Application Support/Claude/claude_desktop_config.json"
    elif sys.platform == "win32":
        config_path = Path(os.environ.get("APPDATA", "")) / "Claude/claude_desktop_config.json"
    else:
        config_path = Path(__file__).parent / "claude_desktop_config.json"
    
    if config_path.exists():
        print(f"Claude Desktop config exists: Yes ({config_path})")
    else:
        print(f"Claude Desktop config exists: No")
        print(f"ERROR: Claude Desktop config does not exist at {config_path}")
        print("Run './setup.sh' to create the configuration")
        return False
    
    # Test running the server with UV
    print("\nTesting MCP server execution with UV...")
    try:
        # Just check if the command would run, don't actually run it
        cmd = ["uv", "run", "-m", "claude_document_mcp.server", "--help"]
        subprocess.check_call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        print("Server command is executable: Yes")
    except subprocess.CalledProcessError:
        print("Server command is executable: No")
        print("ERROR: Cannot execute the MCP server")
        return False
    
    print("\nVerification successful! Your environment is properly set up.")
    print("To run the server, use: ./run.sh")
    print("Or directly with UV: uv run -m claude_document_mcp.server")
    return True

if __name__ == "__main__":
    success = main()
    sys.exit(0 if success else 1)

```

--------------------------------------------------------------------------------
/claude_document_mcp/server.py:
--------------------------------------------------------------------------------

```python
"""
Claude Document MCP Server - Model Context Protocol server for Claude Desktop

Features:
- Microsoft Word file operations (create, edit, convert from txt)
- Excel file operations (create, edit, convert from csv)
- PDF file operations (create, convert from Word)

This is a headless server with no UI, designed to be used with Claude Desktop.
"""

import os
import sys
import json
import logging
from pathlib import Path
from typing import Dict, Any, List, Optional

from mcp.server.fastmcp import FastMCP

# Document processing libraries
try:
    import docx
    from docx import Document
    from docx.shared import Pt, Inches
except ImportError:
    raise ImportError("Please install python-docx with: uv pip install python-docx")

try:
    import pandas as pd
    import openpyxl
except ImportError:
    raise ImportError("Please install pandas and openpyxl with: uv pip install pandas openpyxl")

try:
    from reportlab.lib.pagesizes import letter
    from reportlab.pdfgen import canvas
except ImportError:
    raise ImportError("Please install reportlab with: uv pip install reportlab")

try:
    import docx2pdf
except ImportError:
    raise ImportError("Please install docx2pdf with: uv pip install docx2pdf")

# Set up logging
log_dir = Path(__file__).parent.parent / "logs"
log_dir.mkdir(exist_ok=True)
log_file = log_dir / "document_mcp.log"

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler(log_file)
    ]
)
logger = logging.getLogger(__name__)

# Initialize the FastMCP server
# Make sure to use a standard variable name that can be discovered automatically
server = FastMCP(
    "Document Operations", 
    description="MCP server for document operations (Word, Excel, PDF)",
    dependencies=[
        "python-docx", 
        "pandas", 
        "openpyxl", 
        "reportlab", 
        "docx2pdf",
    ]
)

# Also expose as mcp for current code compatibility
mcp = server

# ---- Microsoft Word Operations ----

@server.tool()
def create_word_document(filepath: str, content: str) -> Dict[str, Any]:
    """
    Create a new Microsoft Word document with the provided content.
    
    Args:
        filepath: Path where to save the document
        content: Text content for the document
        
    Returns:
        Operation result with success status, message, and filepath
    """
    try:
        # Create a new document
        doc = Document()
        
        # Add content
        doc.add_paragraph(content)
        
        # Ensure the directory exists
        os.makedirs(os.path.dirname(os.path.abspath(filepath)), exist_ok=True)
        
        # Save the document
        doc.save(filepath)
        
        logger.info(f"Created Word document: {filepath}")
        return {
            "success": True,
            "message": "Successfully created Word document",
            "filepath": filepath
        }
    except Exception as e:
        logger.error(f"Error creating Word document: {str(e)}")
        return {
            "success": False,
            "message": f"Error creating Word document: {str(e)}",
            "filepath": None
        }

@server.tool()
def edit_word_document(filepath: str, operations: List[Dict[str, Any]]) -> Dict[str, Any]:
    """
    Edit an existing Microsoft Word document using the specified operations.
    
    Args:
        filepath: Path to the Word document
        operations: List of operations to perform, where each operation is a dictionary with:
            - type: Operation type (add_paragraph, add_heading, edit_paragraph, delete_paragraph)
            - Additional parameters depending on the operation type
            
    Returns:
        Operation result with success status, message, and filepath
    """
    try:
        # Load the document
        if not os.path.exists(filepath):
            return {
                "success": False,
                "message": f"File not found: {filepath}",
                "filepath": None
            }
        
        doc = Document(filepath)
        
        # Apply operations
        for op in operations:
            op_type = op.get("type")
            
            if op_type == "add_paragraph":
                doc.add_paragraph(op.get("text", ""))
            
            elif op_type == "add_heading":
                doc.add_heading(op.get("text", ""), level=op.get("level", 1))
            
            elif op_type == "edit_paragraph":
                idx = op.get("index", 0)
                new_text = op.get("text", "")
                
                if 0 <= idx < len(doc.paragraphs):
                    doc.paragraphs[idx].text = new_text
                else:
                    logger.warning(f"Paragraph index out of range: {idx}")
            
            elif op_type == "delete_paragraph":
                idx = op.get("index", 0)
                
                if 0 <= idx < len(doc.paragraphs):
                    p = doc.paragraphs[idx]
                    p_elem = p._element
                    p_elem.getparent().remove(p_elem)
                else:
                    logger.warning(f"Paragraph index out of range: {idx}")
            
            else:
                logger.warning(f"Unknown operation type: {op_type}")
        
        # Save the document
        doc.save(filepath)
        
        logger.info(f"Edited Word document: {filepath}")
        return {
            "success": True,
            "message": "Successfully edited Word document",
            "filepath": filepath
        }
    except Exception as e:
        logger.error(f"Error editing Word document: {str(e)}")
        return {
            "success": False,
            "message": f"Error editing Word document: {str(e)}",
            "filepath": None
        }

@server.tool()
def convert_txt_to_word(source_path: str, target_path: str) -> Dict[str, Any]:
    """
    Convert a text file to a Microsoft Word document.
    
    Args:
        source_path: Path to the text file
        target_path: Path where to save the Word document
        
    Returns:
        Operation result with success status, message, and filepath
    """
    try:
        # Check if source file exists
        if not os.path.exists(source_path):
            return {
                "success": False,
                "message": f"Source file not found: {source_path}",
                "filepath": None
            }
        
        # Read the text file
        with open(source_path, 'r', encoding='utf-8') as file:
            text_content = file.read()
        
        # Create a new document
        doc = Document()
        
        # Add content as paragraphs (split by newlines)
        for paragraph in text_content.split('\n'):
            if paragraph.strip():  # Skip empty paragraphs
                doc.add_paragraph(paragraph)
        
        # Ensure the directory exists
        os.makedirs(os.path.dirname(os.path.abspath(target_path)), exist_ok=True)
        
        # Save the document
        doc.save(target_path)
        
        logger.info(f"Converted text to Word: {source_path} -> {target_path}")
        return {
            "success": True,
            "message": "Successfully converted text to Word document",
            "filepath": target_path
        }
    except Exception as e:
        logger.error(f"Error converting text to Word: {str(e)}")
        return {
            "success": False,
            "message": f"Error converting text to Word: {str(e)}",
            "filepath": None
        }

# ---- Excel Operations ----

@server.tool()
def create_excel_file(filepath: str, content: str) -> Dict[str, Any]:
    """
    Create a new Excel file with the provided content.
    
    Args:
        filepath: Path where to save the Excel file
        content: Data content, either JSON string or CSV-like string
        
    Returns:
        Operation result with success status, message, and filepath
    """
    try:
        # Parse the content as JSON data
        try:
            data = json.loads(content)
        except json.JSONDecodeError:
            # If not valid JSON, treat as CSV
            data = [line.split(',') for line in content.strip().split('\n')]
        
        # Convert to DataFrame
        df = pd.DataFrame(data)
        
        # Ensure the directory exists
        os.makedirs(os.path.dirname(os.path.abspath(filepath)), exist_ok=True)
        
        # Save to Excel
        df.to_excel(filepath, index=False)
        
        logger.info(f"Created Excel file: {filepath}")
        return {
            "success": True,
            "message": "Successfully created Excel file",
            "filepath": filepath
        }
    except Exception as e:
        logger.error(f"Error creating Excel file: {str(e)}")
        return {
            "success": False,
            "message": f"Error creating Excel file: {str(e)}",
            "filepath": None
        }

@server.tool()
def edit_excel_file(filepath: str, operations: List[Dict[str, Any]]) -> Dict[str, Any]:
    """
    Edit an existing Excel file using the specified operations.
    
    Args:
        filepath: Path to the Excel file
        operations: List of operations to perform, where each operation is a dictionary with:
            - type: Operation type (update_cell, update_range, delete_row, delete_column, add_sheet, delete_sheet)
            - Additional parameters depending on the operation type
            
    Returns:
        Operation result with success status, message, and filepath
    """
    try:
        # Check if file exists
        if not os.path.exists(filepath):
            return {
                "success": False,
                "message": f"File not found: {filepath}",
                "filepath": None
            }
        
        # Load the Excel file
        wb = openpyxl.load_workbook(filepath)
        
        # Apply operations
        for op in operations:
            op_type = op.get("type")
            sheet_name = op.get("sheet", wb.sheetnames[0])
            
            # Get the sheet, create if it doesn't exist
            if sheet_name not in wb.sheetnames:
                wb.create_sheet(sheet_name)
            
            sheet = wb[sheet_name]
            
            if op_type == "update_cell":
                row = op.get("row", 1)
                col = op.get("col", 1)
                value = op.get("value", "")
                
                sheet.cell(row=row, column=col, value=value)
            
            elif op_type == "update_range":
                start_row = op.get("start_row", 1)
                start_col = op.get("start_col", 1)
                values = op.get("values", [])
                
                for i, row_values in enumerate(values):
                    for j, value in enumerate(row_values):
                        sheet.cell(row=start_row + i, column=start_col + j, value=value)
            
            elif op_type == "delete_row":
                row = op.get("row", 1)
                sheet.delete_rows(row)
            
            elif op_type == "delete_column":
                col = op.get("col", 1)
                sheet.delete_cols(col)
            
            elif op_type == "add_sheet":
                new_sheet_name = op.get("name", "NewSheet")
                if new_sheet_name not in wb.sheetnames:
                    wb.create_sheet(new_sheet_name)
            
            elif op_type == "delete_sheet":
                if sheet_name in wb.sheetnames and len(wb.sheetnames) > 1:
                    del wb[sheet_name]
            
            else:
                logger.warning(f"Unknown operation type: {op_type}")
        
        # Save the workbook
        wb.save(filepath)
        
        logger.info(f"Edited Excel file: {filepath}")
        return {
            "success": True,
            "message": "Successfully edited Excel file",
            "filepath": filepath
        }
    except Exception as e:
        logger.error(f"Error editing Excel file: {str(e)}")
        return {
            "success": False,
            "message": f"Error editing Excel file: {str(e)}",
            "filepath": None
        }

@server.tool()
def convert_csv_to_excel(source_path: str, target_path: str) -> Dict[str, Any]:
    """
    Convert a CSV file to an Excel file.
    
    Args:
        source_path: Path to the CSV file
        target_path: Path where to save the Excel file
        
    Returns:
        Operation result with success status, message, and filepath
    """
    try:
        # Check if source file exists
        if not os.path.exists(source_path):
            return {
                "success": False,
                "message": f"Source file not found: {source_path}",
                "filepath": None
            }
        
        # Read the CSV file
        df = pd.read_csv(source_path)
        
        # Ensure the directory exists
        os.makedirs(os.path.dirname(os.path.abspath(target_path)), exist_ok=True)
        
        # Save to Excel
        df.to_excel(target_path, index=False)
        
        logger.info(f"Converted CSV to Excel: {source_path} -> {target_path}")
        return {
            "success": True,
            "message": "Successfully converted CSV to Excel",
            "filepath": target_path
        }
    except Exception as e:
        logger.error(f"Error converting CSV to Excel: {str(e)}")
        return {
            "success": False,
            "message": f"Error converting CSV to Excel: {str(e)}",
            "filepath": None
        }

# ---- PDF Operations ----

@server.tool()
def create_pdf_file(filepath: str, content: str) -> Dict[str, Any]:
    """
    Create a new PDF file with the provided text content.
    
    Args:
        filepath: Path where to save the PDF file
        content: Text content for the PDF
        
    Returns:
        Operation result with success status, message, and filepath
    """
    try:
        # Ensure the directory exists
        os.makedirs(os.path.dirname(os.path.abspath(filepath)), exist_ok=True)
        
        # Create a new PDF with ReportLab
        c = canvas.Canvas(filepath, pagesize=letter)
        width, height = letter
        
        # Process text content
        lines = content.split('\n')
        
        y_position = height - 40  # Start position from top
        for line in lines:
            if y_position < 40:  # If we're at the bottom of the page
                c.showPage()  # Create a new page
                y_position = height - 40  # Reset position
            
            c.drawString(40, y_position, line)
            y_position -= 15  # Move down for next line
        
        c.save()
        
        logger.info(f"Created PDF file: {filepath}")
        return {
            "success": True,
            "message": "Successfully created PDF file",
            "filepath": filepath
        }
    except Exception as e:
        logger.error(f"Error creating PDF file: {str(e)}")
        return {
            "success": False,
            "message": f"Error creating PDF file: {str(e)}",
            "filepath": None
        }

@server.tool()
def convert_word_to_pdf(source_path: str, target_path: str) -> Dict[str, Any]:
    """
    Convert a Microsoft Word document to a PDF file.
    
    Args:
        source_path: Path to the Word document
        target_path: Path where to save the PDF file
        
    Returns:
        Operation result with success status, message, and filepath
    """
    try:
        # Check if source file exists
        if not os.path.exists(source_path):
            return {
                "success": False,
                "message": f"Source file not found: {source_path}",
                "filepath": None
            }
        
        # Ensure the directory exists
        os.makedirs(os.path.dirname(os.path.abspath(target_path)), exist_ok=True)
        
        # Convert Word to PDF using docx2pdf
        docx2pdf.convert(source_path, target_path)
        
        logger.info(f"Converted Word to PDF: {source_path} -> {target_path}")
        return {
            "success": True,
            "message": "Successfully converted Word to PDF",
            "filepath": target_path
        }
    except Exception as e:
        logger.error(f"Error converting Word to PDF: {str(e)}")
        return {
            "success": False,
            "message": f"Error converting Word to PDF: {str(e)}",
            "filepath": None
        }

# ---- Resources ----

@server.resource("capabilities://")
def get_capabilities() -> Dict[str, Any]:
    """
    Provide information about this MCP server's capabilities.
    
    Returns:
        Dictionary containing capabilities information
    """
    return {
        "name": "Document Operations",
        "version": "0.1.0",
        "description": "Model Context Protocol server for document operations (Word, Excel, PDF)",
        "document_operations": {
            "word": {
                "create": True,
                "edit": True,
                "convert_from_txt": True
            },
            "excel": {
                "create": True,
                "edit": True,
                "convert_from_csv": True
            },
            "pdf": {
                "create": True,
                "convert_from_word": True
            }
        }
    }

def main():
    """Main entry point for the server."""
    try:
        # Setup logging directory
        log_dir = Path(__file__).parent.parent / "logs"
        log_dir.mkdir(exist_ok=True)
        
        # Log to file instead of stdout
        startup_logger = logging.getLogger("startup")
        startup_logger.setLevel(logging.INFO)
        
        # Make sure startup logger doesn't also log to console
        startup_logger.propagate = False
        
        # Add file handler for startup logs
        startup_log_file = log_dir / "startup.log"
        file_handler = logging.FileHandler(startup_log_file)
        file_handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
        startup_logger.addHandler(file_handler)
        
        # Log startup information to file only
        startup_logger.info("Starting Document Operations MCP Server...")
        startup_logger.info(f"Python version: {sys.version}")
        startup_logger.info(f"Python executable: {sys.executable}")
        startup_logger.info(f"Working directory: {os.getcwd()}")
        startup_logger.info(f"Logs directory: {log_dir}")
        
        # Verify environment
        if sys.prefix == sys.base_prefix:
            startup_logger.warning("Not running in a virtual environment")
            
        startup_logger.info("Server is ready to accept connections from Claude Desktop!")
        
        # Run the server
        server.run()
    except Exception as e:
        logger.error(f"Error starting server: {str(e)}")
        import traceback
        logger.error(traceback.format_exc())
        sys.exit(1)

if __name__ == "__main__":
    main()

```