Rename package to mcwaddams
Some checks are pending
Test Dashboard / test-and-dashboard (push) Waiting to run
Some checks are pending
Test Dashboard / test-and-dashboard (push) Waiting to run
Named for Milton Waddams, who was relocated to the basement with boxes of legacy documents. He handles the .doc and .xls files from 1997 that nobody else wants to touch. - Rename package from mcp-office-tools to mcwaddams - Update author to Ryan Malloy - Update all imports and references - Add Office Space themed README narrative - All 53 tests passing
This commit is contained in:
parent
6fb76d8760
commit
31948d6ffc
20
CLAUDE.md
20
CLAUDE.md
@ -1,10 +1,10 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with the MCP Office Tools codebase.
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with the mcwaddams codebase.
|
||||
|
||||
## Project Overview
|
||||
|
||||
MCP Office Tools is a FastMCP server that provides comprehensive Microsoft Office document processing capabilities including text extraction, image extraction, metadata extraction, and format detection. The server supports Word (.docx, .doc), Excel (.xlsx, .xls), PowerPoint (.pptx, .ppt), and CSV files with intelligent method selection and automatic fallbacks.
|
||||
mcwaddams is a FastMCP server that provides comprehensive Microsoft Office document processing capabilities including text extraction, image extraction, metadata extraction, and format detection. The server supports Word (.docx, .doc), Excel (.xlsx, .xls), PowerPoint (.pptx, .ppt), and CSV files with intelligent method selection and automatic fallbacks.
|
||||
|
||||
## Development Commands
|
||||
|
||||
@ -23,7 +23,7 @@ uv sync --dev
|
||||
uv run pytest
|
||||
|
||||
# Run with coverage
|
||||
uv run pytest --cov=mcp_office_tools
|
||||
uv run pytest --cov=mcwaddams
|
||||
|
||||
# Run specific test file
|
||||
uv run pytest tests/test_server.py
|
||||
@ -47,10 +47,10 @@ uv run mypy src/
|
||||
### Running the Server
|
||||
```bash
|
||||
# Run MCP server directly
|
||||
uv run mcp-office-tools
|
||||
uv run mcwaddams
|
||||
|
||||
# Run with Python module
|
||||
uv run python -m mcp_office_tools.server
|
||||
uv run python -m mcwaddams.server
|
||||
|
||||
# Test with sample documents
|
||||
uv run python examples/test_office_tools.py /path/to/test.docx
|
||||
@ -69,8 +69,8 @@ uv publish
|
||||
|
||||
### Core Components
|
||||
|
||||
- **`src/mcp_office_tools/server.py`**: Main server implementation with all Office processing tools
|
||||
- **`src/mcp_office_tools/utils/`**: Utility modules for validation, caching, and file detection
|
||||
- **`src/mcwaddams/server.py`**: Main server implementation with all Office processing tools
|
||||
- **`src/mcwaddams/utils/`**: Utility modules for validation, caching, and file detection
|
||||
- **FastMCP Framework**: Uses FastMCP for MCP protocol implementation
|
||||
- **Multi-library approach**: Integrates python-docx, openpyxl, python-pptx, pandas, and legacy format handlers
|
||||
|
||||
@ -165,8 +165,8 @@ Tools are registered using FastMCP decorators and follow MCP protocol standards
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
mcp-office-tools/
|
||||
├── src/mcp_office_tools/
|
||||
mcwaddams/
|
||||
├── src/mcwaddams/
|
||||
│ ├── __init__.py # Package initialization
|
||||
│ ├── server.py # Main FastMCP server with tools
|
||||
│ ├── utils/ # Utility modules
|
||||
@ -218,7 +218,7 @@ The project uses pytest with:
|
||||
|
||||
## Relationship to MCP PDF Tools
|
||||
|
||||
MCP Office Tools is designed as a companion to MCP PDF Tools:
|
||||
mcwaddams is designed as a companion to MCP PDF Tools:
|
||||
- Consistent API design patterns
|
||||
- Similar caching and URL handling
|
||||
- Parallel tool organization
|
||||
|
||||
@ -151,10 +151,10 @@ except OfficeFileError as e:
|
||||
|
||||
### **Server Status: OPERATIONAL ✅**
|
||||
```bash
|
||||
$ uv run mcp-office-tools --version
|
||||
$ uv run mcwaddams --version
|
||||
MCP Office Tools v0.1.0
|
||||
|
||||
$ uv run mcp-office-tools
|
||||
$ uv run mcwaddams
|
||||
[Server starts successfully with FastMCP banner]
|
||||
```
|
||||
|
||||
@ -178,8 +178,8 @@ $ uv run mcp-office-tools
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"mcp-office-tools": {
|
||||
"command": "mcp-office-tools"
|
||||
"mcwaddams": {
|
||||
"command": "mcwaddams"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
6
Makefile
6
Makefile
@ -97,13 +97,13 @@ quick-test:
|
||||
# Coverage report
|
||||
coverage:
|
||||
@echo "📊 Generating coverage report..."
|
||||
@uv run pytest --cov=mcp_office_tools --cov-report=html --cov-report=term
|
||||
@uv run pytest --cov=mcwaddams --cov-report=html --cov-report=term
|
||||
@echo "✅ Coverage report generated at htmlcov/index.html"
|
||||
|
||||
# Run server in development mode
|
||||
dev:
|
||||
@echo "🚀 Starting MCP Office Tools server..."
|
||||
@uv run mcp-office-tools
|
||||
@uv run mcwaddams
|
||||
|
||||
# Build distribution packages
|
||||
build:
|
||||
@ -116,7 +116,7 @@ info:
|
||||
@echo "MCP Office Tools - Project Information"
|
||||
@echo "======================================="
|
||||
@echo ""
|
||||
@echo "Project: mcp-office-tools"
|
||||
@echo "Project: mcwaddams"
|
||||
@echo "Version: $(shell grep '^version' pyproject.toml | cut -d'"' -f2)"
|
||||
@echo "Python: $(shell python --version)"
|
||||
@echo "UV: $(shell uv --version 2>/dev/null || echo 'not installed')"
|
||||
|
||||
213
README.md
213
README.md
@ -1,15 +1,15 @@
|
||||
<div align="center">
|
||||
|
||||
# 📊 MCP Office Tools
|
||||
# 📎 mcwaddams
|
||||
|
||||
**MCP server for extracting text, tables, images, and data from Microsoft Office files**
|
||||
**MCP server for Microsoft Office document processing**
|
||||
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://gofastmcp.com)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://modelcontextprotocol.io)
|
||||
|
||||
*Word, Excel, PowerPoint, CSV — all the formats your AI agent needs to read but can't*
|
||||
*"I was told there would be document extraction."*
|
||||
|
||||
[Installation](#-installation) • [Tools](#-available-tools) • [Examples](#-usage-examples) • [Testing](#-testing)
|
||||
|
||||
@ -17,14 +17,22 @@
|
||||
|
||||
---
|
||||
|
||||
## The Backstory
|
||||
|
||||
Milton Waddams was relocated to the basement. They took his stapler. But down there, surrounded by boxes of `.doc` files from 1997 and `.xls` spreadsheets that predate Unicode, he became something else entirely: a document processing expert.
|
||||
|
||||
This MCP server channels that energy. It handles the legacy formats nobody else wants to touch. It extracts text from files that should have been migrated to Google Docs a decade ago. It reads the TPS reports.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- **Universal extraction** — Pull text, images, and metadata from any Office format
|
||||
- **Format-specific tools** — Deep analysis for Word (tables, structure), Excel (formulas, charts), PowerPoint
|
||||
- **Automatic pagination** — Large documents get chunked so they don't blow up your context window
|
||||
- **Fallback processing** — When one library chokes on a weird file, we try another. No silent failures.
|
||||
- **Fallback processing** — When one library chokes on a weird file, we try another
|
||||
- **URL support** — Pass a URL instead of a file path; we'll download and cache it
|
||||
- **Legacy formats** — Yes, even those .doc and .xls files from 2003 still work
|
||||
- **Legacy formats** — Yes, even those `.doc` and `.xls` files from the basement
|
||||
|
||||
---
|
||||
|
||||
@ -32,11 +40,11 @@
|
||||
|
||||
```bash
|
||||
# Quick install with uvx (recommended)
|
||||
uvx mcp-office-tools
|
||||
uvx mcwaddams
|
||||
|
||||
# Or install with uv/pip
|
||||
uv add mcp-office-tools
|
||||
pip install mcp-office-tools
|
||||
uv add mcwaddams
|
||||
pip install mcwaddams
|
||||
```
|
||||
|
||||
### Claude Desktop Configuration
|
||||
@ -46,9 +54,9 @@ Add to your `claude_desktop_config.json`:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"office-tools": {
|
||||
"mcwaddams": {
|
||||
"command": "uvx",
|
||||
"args": ["mcp-office-tools"]
|
||||
"args": ["mcwaddams"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -57,7 +65,7 @@ Add to your `claude_desktop_config.json`:
|
||||
### Claude Code Configuration
|
||||
|
||||
```bash
|
||||
claude mcp add office-tools "uvx mcp-office-tools"
|
||||
claude mcp add mcwaddams "uvx mcwaddams"
|
||||
```
|
||||
|
||||
---
|
||||
@ -183,7 +191,7 @@ Use `text_patterns_only=True` to skip heading style detection for documents with
|
||||
|
||||
## 🎯 MCP Prompts
|
||||
|
||||
Pre-built workflows that chain multiple tools together. Use these as starting points:
|
||||
Pre-built workflows that chain multiple tools together:
|
||||
|
||||
| Prompt | Level | Description |
|
||||
|--------|-------|-------------|
|
||||
@ -245,99 +253,7 @@ result = await analyze_excel_data(
|
||||
check_data_quality=True
|
||||
)
|
||||
|
||||
# Returns per-column analysis
|
||||
# {
|
||||
# "analysis": {
|
||||
# "Sheet1": {
|
||||
# "dimensions": {"rows": 1000, "columns": 12},
|
||||
# "column_info": {
|
||||
# "Revenue": {
|
||||
# "data_type": "float64",
|
||||
# "null_percentage": 2.3,
|
||||
# "statistics": {"mean": 45000, "median": 42000, ...},
|
||||
# "quality_issues": ["5 potential outliers"]
|
||||
# }
|
||||
# },
|
||||
# "data_quality": {
|
||||
# "completeness_percentage": 97.8,
|
||||
# "duplicate_rows": 12
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
```
|
||||
|
||||
### Extract Excel Formulas
|
||||
|
||||
```python
|
||||
result = await extract_excel_formulas(
|
||||
file_path="financial-model.xlsx",
|
||||
analyze_dependencies=True
|
||||
)
|
||||
|
||||
# Returns formula details with dependency mapping
|
||||
# {
|
||||
# "formulas": {
|
||||
# "Sheet1": [
|
||||
# {
|
||||
# "cell": "D2",
|
||||
# "formula": "=B2*C2",
|
||||
# "value": 1500.00,
|
||||
# "dependencies": ["B2", "C2"]
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
# }
|
||||
```
|
||||
|
||||
### Generate Chart Data
|
||||
|
||||
```python
|
||||
result = await create_excel_chart_data(
|
||||
file_path="quarterly-revenue.xlsx",
|
||||
chart_type="line",
|
||||
output_format="chartjs"
|
||||
)
|
||||
|
||||
# Returns ready-to-use Chart.js configuration
|
||||
# {
|
||||
# "chartjs": {
|
||||
# "type": "line",
|
||||
# "data": {
|
||||
# "labels": ["Q1", "Q2", "Q3", "Q4"],
|
||||
# "datasets": [{"label": "Revenue", "data": [100, 120, 115, 140]}]
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
```
|
||||
|
||||
### Extract Word Tables
|
||||
|
||||
```python
|
||||
result = await extract_word_tables(
|
||||
file_path="contract.docx",
|
||||
output_format="markdown"
|
||||
)
|
||||
|
||||
# Returns tables with optional format conversion
|
||||
# {
|
||||
# "tables": [
|
||||
# {
|
||||
# "table_index": 0,
|
||||
# "dimensions": {"rows": 5, "columns": 3},
|
||||
# "converted_output": "| Name | Role | Department |\n|---|---|---|\n..."
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
```
|
||||
|
||||
### Process Documents from URLs
|
||||
|
||||
```python
|
||||
# Documents are downloaded and cached automatically
|
||||
result = await extract_text("https://example.com/report.docx")
|
||||
|
||||
# Cache expires after 1 hour by default
|
||||
# Returns per-column analysis with quality issues
|
||||
```
|
||||
|
||||
### Index Document for On-Demand Resource Fetching
|
||||
@ -351,8 +267,7 @@ result = await index_document("novel.docx")
|
||||
# "doc_id": "56036b0f171a",
|
||||
# "resources": {
|
||||
# "chapter": [
|
||||
# {"id": "1", "title": "Chapter 1: The Beginning", "uri": "chapter://56036b0f171a/1"},
|
||||
# {"id": "2", "title": "Chapter 2: Rising Action", "uri": "chapter://56036b0f171a/2"},
|
||||
# {"id": "1", "title": "Chapter 1", "uri": "chapter://56036b0f171a/1"},
|
||||
# ...
|
||||
# ],
|
||||
# "image": [
|
||||
@ -362,63 +277,47 @@ result = await index_document("novel.docx")
|
||||
# }
|
||||
# }
|
||||
|
||||
# Now fetch specific content via MCP resources:
|
||||
# Fetch specific content via MCP resources:
|
||||
# - chapter://56036b0f171a/1 → Chapter 1 as markdown
|
||||
# - chapter://56036b0f171a/1.txt → Chapter 1 as plain text
|
||||
# - chapters://56036b0f171a/1-3 → Chapters 1-3 combined
|
||||
# - image://56036b0f171a/0 → First embedded image
|
||||
|
||||
# Works with Excel and PowerPoint too:
|
||||
await index_document("data.xlsx")
|
||||
# → sheet://abc123/Revenue, sheet://abc123/Expenses, ...
|
||||
|
||||
await index_document("presentation.pptx")
|
||||
# → slide://def456/1, slide://def456/2, ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
We built a visual test dashboard because staring at pytest output gets old. Run `make test` and you get an HTML report with pass/fail stats, detailed I/O for each test, and expandable tracebacks when things break.
|
||||
|
||||
```bash
|
||||
# Run tests and generate the dashboard
|
||||
make test
|
||||
|
||||
# Just pytest, no dashboard
|
||||
# Just pytest
|
||||
make test-pytest
|
||||
|
||||
# Open existing dashboard
|
||||
# Open dashboard
|
||||
make view-dashboard
|
||||
```
|
||||
|
||||
The dashboard has an MS Office-inspired theme (Word blue, Excel green, PowerPoint orange) and groups tests by category so you can see what's working at a glance.
|
||||
|
||||
---
|
||||
|
||||
## 🏗 Architecture
|
||||
|
||||
The mixin pattern keeps things modular — universal tools work on everything, format-specific tools go deeper. When the primary library can't handle something (corrupted files, weird formatting), we fall back to alternatives.
|
||||
The mixin pattern keeps things modular — universal tools work on everything, format-specific tools go deeper.
|
||||
|
||||
```
|
||||
mcp-office-tools/
|
||||
├── src/mcp_office_tools/
|
||||
mcwaddams/
|
||||
├── src/mcwaddams/
|
||||
│ ├── server.py # FastMCP server + resource templates
|
||||
│ ├── resources.py # Resource store for on-demand content
|
||||
│ ├── mixins/
|
||||
│ │ ├── universal.py # Format-agnostic tools (incl. index_document)
|
||||
│ │ ├── universal.py # Format-agnostic tools
|
||||
│ │ ├── word.py # Word-specific tools
|
||||
│ │ ├── excel.py # Excel-specific tools
|
||||
│ │ └── powerpoint.py # PowerPoint tools (WIP)
|
||||
│ ├── utils/
|
||||
│ │ ├── validation.py # File validation
|
||||
│ │ ├── file_detection.py # Format detection
|
||||
│ │ ├── caching.py # URL caching
|
||||
│ │ └── decorators.py # Error handling, defaults
|
||||
│ │ └── powerpoint.py # PowerPoint tools
|
||||
│ ├── utils/ # Validation, caching, detection
|
||||
│ └── pagination.py # Large document pagination
|
||||
├── tests/ # pytest test suite
|
||||
└── reports/ # Test dashboard output
|
||||
├── tests/
|
||||
└── reports/
|
||||
```
|
||||
|
||||
### Processing Libraries
|
||||
@ -436,57 +335,17 @@ mcp-office-tools/
|
||||
## 🔧 Development
|
||||
|
||||
```bash
|
||||
# Clone and install
|
||||
git clone https://github.com/yourusername/mcp-office-tools.git
|
||||
cd mcp-office-tools
|
||||
git clone https://github.com/ryanmalloy/mcwaddams.git
|
||||
cd mcwaddams
|
||||
uv sync --dev
|
||||
|
||||
# Run tests
|
||||
uv run pytest
|
||||
|
||||
# Format and lint
|
||||
uv run black src/ tests/
|
||||
uv run ruff check src/ tests/
|
||||
|
||||
# Type check
|
||||
uv run mypy src/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 Dependencies
|
||||
|
||||
**Core:**
|
||||
- `fastmcp` - MCP server framework
|
||||
- `python-docx` - Word document processing
|
||||
- `openpyxl` - Excel spreadsheet processing
|
||||
- `python-pptx` - PowerPoint processing
|
||||
- `pandas` - Data analysis and CSV handling
|
||||
- `mammoth` - Word to HTML/Markdown conversion
|
||||
- `olefile` - Legacy OLE format support
|
||||
- `xlrd` - Legacy Excel support
|
||||
- `pillow` - Image processing
|
||||
- `aiohttp` / `aiofiles` - Async HTTP and file I/O
|
||||
|
||||
**Optional:**
|
||||
- `python-magic` - Enhanced MIME type detection
|
||||
- `msoffcrypto-tool` - Encrypted file detection
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Related Projects
|
||||
|
||||
- **[MCP PDF Tools](https://github.com/yourusername/mcp-pdf-tools)** - Companion server for PDF processing
|
||||
- **[FastMCP](https://gofastmcp.com)** - The framework powering this server
|
||||
|
||||
## 📝 Behind the Scenes
|
||||
|
||||
This README was rewritten during a human-AI collaboration session. The process raised questions about discernment, voice, and what makes documentation actually land:
|
||||
|
||||
- **[AI Isn't New. Your Discernment Is What Matters.](https://ryanmalloy.com/blog/ai-discernment)** — Ryan's take on 40 years of writing code and why discernment matters more than the tools
|
||||
|
||||
---
|
||||
|
||||
## 📜 License
|
||||
|
||||
MIT License - see [LICENSE](LICENSE) for details.
|
||||
@ -495,6 +354,8 @@ MIT License - see [LICENSE](LICENSE) for details.
|
||||
|
||||
<div align="center">
|
||||
|
||||
*Named for Milton Waddams, who was relocated to the basement with the legacy documents.*
|
||||
|
||||
**Built with [FastMCP](https://gofastmcp.com) and the [Model Context Protocol](https://modelcontextprotocol.io)**
|
||||
|
||||
</div>
|
||||
|
||||
@ -59,7 +59,7 @@ async def test_tool_functionality():
|
||||
mixin = UniversalMixin(app)
|
||||
|
||||
# Mock dependencies
|
||||
with patch('mcp_office_tools.utils.validation.validate_office_file'):
|
||||
with patch('mcwaddams.utils.validation.validate_office_file'):
|
||||
# Test tool directly through mixin
|
||||
result = await mixin.extract_text("/test.csv")
|
||||
assert "text" in result
|
||||
@ -185,13 +185,13 @@ uv run pytest
|
||||
uv run pytest tests/test_universal_mixin.py -v
|
||||
|
||||
# With coverage
|
||||
uv run pytest --cov=mcp_office_tools
|
||||
uv run pytest --cov=mcwaddams
|
||||
```
|
||||
|
||||
### Continuous Integration
|
||||
```bash
|
||||
# All tests with coverage reporting
|
||||
uv run pytest --cov=mcp_office_tools --cov-report=xml --cov-report=html
|
||||
uv run pytest --cov=mcwaddams --cov-report=xml --cov-report=html
|
||||
```
|
||||
|
||||
## Key Testing Fixtures
|
||||
|
||||
@ -17,7 +17,7 @@ This document captures critical bugs discovered and fixed while processing compl
|
||||
|
||||
## 1. FastMCP Banner Corruption
|
||||
|
||||
**File:** `src/mcp_office_tools/server.py`
|
||||
**File:** `src/mcwaddams/server.py`
|
||||
|
||||
**Symptom:** MCP connection fails with `Invalid JSON: EOF while parsing`
|
||||
|
||||
@ -35,7 +35,7 @@ def main():
|
||||
|
||||
## 2. Page Range Cap Bug
|
||||
|
||||
**File:** `src/mcp_office_tools/utils/word_processing.py`
|
||||
**File:** `src/mcwaddams/utils/word_processing.py`
|
||||
|
||||
**Symptom:** Requesting pages 1-5 returns truncated content, but pages 195-200 returns everything.
|
||||
|
||||
@ -57,7 +57,7 @@ max_chars = num_pages_requested * 50000
|
||||
|
||||
## 3. Heading Scan Limit Bug
|
||||
|
||||
**File:** `src/mcp_office_tools/utils/word_processing.py`
|
||||
**File:** `src/mcwaddams/utils/word_processing.py`
|
||||
|
||||
**Symptom:** `_get_available_headings()` returns empty list for documents with chapters beyond the first few pages.
|
||||
|
||||
@ -81,7 +81,7 @@ for element in doc.element.body: # Scan ALL elements
|
||||
|
||||
## 4. Short-Text Fallback Logic Bug
|
||||
|
||||
**File:** `src/mcp_office_tools/utils/word_processing.py`
|
||||
**File:** `src/mcwaddams/utils/word_processing.py`
|
||||
|
||||
**Symptom:** Chapter search fails even when chapter text exists and is under 100 characters.
|
||||
|
||||
@ -115,7 +115,7 @@ if is_heading_style or len(text_content.strip()) < 100:
|
||||
|
||||
## 5. Critical xpath API Mismatch (ROOT CAUSE)
|
||||
|
||||
**File:** `src/mcp_office_tools/utils/word_processing.py`
|
||||
**File:** `src/mcwaddams/utils/word_processing.py`
|
||||
|
||||
**Symptom:** Chapter search always returns "not found" even for chapters that clearly exist.
|
||||
|
||||
@ -152,7 +152,7 @@ if pPr is not None:
|
||||
|
||||
## 6. Image Mode Default
|
||||
|
||||
**File:** `src/mcp_office_tools/mixins/word.py`
|
||||
**File:** `src/mcwaddams/mixins/word.py`
|
||||
|
||||
**Symptom:** Responses exceed token limits when documents contain images.
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ from pathlib import Path
|
||||
# Add the package to Python path for local testing
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
from mcp_office_tools.server import (
|
||||
from mcwaddams.server import (
|
||||
extract_text,
|
||||
extract_images,
|
||||
extract_metadata,
|
||||
|
||||
@ -29,15 +29,15 @@ def test_import():
|
||||
print("🔍 Testing package import...")
|
||||
|
||||
try:
|
||||
import mcp_office_tools
|
||||
print(f"✅ Package imported successfully - Version: {mcp_office_tools.__version__}")
|
||||
import mcwaddams
|
||||
print(f"✅ Package imported successfully - Version: {mcwaddams.__version__}")
|
||||
|
||||
# Test server import
|
||||
from mcp_office_tools.server import app
|
||||
from mcwaddams.server import app
|
||||
print("✅ Server module imported successfully")
|
||||
|
||||
# Test utils import
|
||||
from mcp_office_tools.utils import OfficeFileError, get_supported_extensions
|
||||
from mcwaddams.utils import OfficeFileError, get_supported_extensions
|
||||
print("✅ Utils module imported successfully")
|
||||
|
||||
# Test supported extensions
|
||||
@ -58,7 +58,7 @@ async def test_utils():
|
||||
print("\n🔧 Testing utility functions...")
|
||||
|
||||
try:
|
||||
from mcp_office_tools.utils import (
|
||||
from mcwaddams.utils import (
|
||||
detect_file_format,
|
||||
validate_office_path,
|
||||
OfficeFileError
|
||||
@ -103,7 +103,7 @@ def test_server_structure():
|
||||
print("\n🖥️ Testing server structure...")
|
||||
|
||||
try:
|
||||
from mcp_office_tools.server import app
|
||||
from mcwaddams.server import app
|
||||
|
||||
# Check that app has tools
|
||||
if hasattr(app, '_tools'):
|
||||
@ -134,7 +134,7 @@ async def test_caching():
|
||||
print("\n📦 Testing caching functionality...")
|
||||
|
||||
try:
|
||||
from mcp_office_tools.utils.caching import OfficeFileCache, get_cache
|
||||
from mcwaddams.utils.caching import OfficeFileCache, get_cache
|
||||
|
||||
# Test cache creation
|
||||
cache = get_cache()
|
||||
@ -145,7 +145,7 @@ async def test_caching():
|
||||
print(f"✅ Cache stats: {stats['total_files']} files, {stats['total_size_mb']} MB")
|
||||
|
||||
# Test URL validation
|
||||
from mcp_office_tools.utils.validation import is_url
|
||||
from mcwaddams.utils.validation import is_url
|
||||
|
||||
assert is_url("https://example.com/file.docx")
|
||||
assert not is_url("/local/path/file.docx")
|
||||
@ -243,7 +243,7 @@ async def main():
|
||||
print("🎉 Installation verified successfully!")
|
||||
print("✅ MCP Office Tools is ready to use.")
|
||||
print("\n🚀 Next steps:")
|
||||
print(" 1. Run the MCP server: uv run mcp-office-tools")
|
||||
print(" 1. Run the MCP server: uv run mcwaddams")
|
||||
print(" 2. Add to Claude Desktop config")
|
||||
print(" 3. Test with Office documents")
|
||||
return 0
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
[project]
|
||||
name = "mcp-office-tools"
|
||||
name = "mcwaddams"
|
||||
version = "0.1.0"
|
||||
description = "MCP server for comprehensive Microsoft Office document processing"
|
||||
authors = [{name = "MCP Office Tools", email = "contact@mcpofficetools.dev"}]
|
||||
description = "MCP server for Microsoft Office document processing. Named for Milton Waddams, who was relocated to the basement with boxes of legacy documents."
|
||||
authors = [{name = "Ryan Malloy", email = "ryan@supported.systems"}]
|
||||
readme = "README.md"
|
||||
license = {text = "MIT"}
|
||||
requires-python = ">=3.11"
|
||||
keywords = ["mcp", "office", "docx", "xlsx", "pptx", "word", "excel", "powerpoint", "document", "processing"]
|
||||
keywords = ["mcp", "office", "docx", "xlsx", "pptx", "word", "excel", "powerpoint", "document", "processing", "milton", "legacy"]
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
@ -64,20 +64,19 @@ enhanced = [
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/mcp-office-tools/mcp-office-tools"
|
||||
Documentation = "https://mcp-office-tools.readthedocs.io"
|
||||
Repository = "https://github.com/mcp-office-tools/mcp-office-tools"
|
||||
Issues = "https://github.com/mcp-office-tools/mcp-office-tools/issues"
|
||||
Homepage = "https://github.com/ryanmalloy/mcwaddams"
|
||||
Repository = "https://github.com/ryanmalloy/mcwaddams"
|
||||
Issues = "https://github.com/ryanmalloy/mcwaddams/issues"
|
||||
|
||||
[project.scripts]
|
||||
mcp-office-tools = "mcp_office_tools.server:main"
|
||||
mcwaddams = "mcwaddams.server:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/mcp_office_tools"]
|
||||
packages = ["src/mcwaddams"]
|
||||
|
||||
[tool.hatch.build.targets.sdist]
|
||||
include = [
|
||||
@ -145,7 +144,7 @@ minversion = "7.0"
|
||||
addopts = [
|
||||
"--strict-markers",
|
||||
"--strict-config",
|
||||
"--cov=mcp_office_tools",
|
||||
"--cov=mcwaddams",
|
||||
"--cov-report=term-missing",
|
||||
"--cov-report=html",
|
||||
"--cov-report=xml",
|
||||
@ -161,7 +160,7 @@ markers = [
|
||||
]
|
||||
|
||||
[tool.coverage.run]
|
||||
source = ["src/mcp_office_tools"]
|
||||
source = ["src/mcwaddams"]
|
||||
omit = [
|
||||
"*/tests/*",
|
||||
"*/test_*",
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
{
|
||||
"metadata": {
|
||||
"start_time": "2026-01-11T09:40:29.164041",
|
||||
"start_time": "2026-01-11T11:35:12.792077",
|
||||
"pytest_version": "9.0.2",
|
||||
"end_time": "2026-01-11T09:40:30.048909",
|
||||
"duration": 0.8848621845245361,
|
||||
"end_time": "2026-01-11T11:35:14.191660",
|
||||
"duration": 1.399580955505371,
|
||||
"exit_status": 0
|
||||
},
|
||||
"summary": {
|
||||
|
||||
@ -39,7 +39,7 @@ async def test_mcp_server():
|
||||
|
||||
try:
|
||||
# Import the server components
|
||||
from mcp_office_tools.mixins import UniversalMixin
|
||||
from mcwaddams.mixins import UniversalMixin
|
||||
|
||||
# Test the Universal Mixin directly
|
||||
universal = UniversalMixin()
|
||||
|
||||
@ -12,9 +12,9 @@ def test_pagination():
|
||||
|
||||
try:
|
||||
# Import the server components
|
||||
from mcp_office_tools.server import app
|
||||
from mcp_office_tools.mixins.word import WordMixin
|
||||
from mcp_office_tools.pagination import DocumentPaginationManager, paginate_document_conversion
|
||||
from mcwaddams.server import app
|
||||
from mcwaddams.mixins.word import WordMixin
|
||||
from mcwaddams.pagination import DocumentPaginationManager, paginate_document_conversion
|
||||
|
||||
print("✅ Successfully imported all pagination components:")
|
||||
print(" • DocumentPaginationManager")
|
||||
|
||||
@ -14,7 +14,7 @@ from typing import Dict, Any
|
||||
from fastmcp import FastMCP
|
||||
# FastMCP testing utilities are created manually
|
||||
|
||||
from mcp_office_tools.mixins import UniversalMixin, WordMixin, ExcelMixin, PowerPointMixin
|
||||
from mcwaddams.mixins import UniversalMixin, WordMixin, ExcelMixin, PowerPointMixin
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -211,23 +211,23 @@ class MockValidationContext:
|
||||
self.patches = []
|
||||
|
||||
def __enter__(self):
|
||||
import mcp_office_tools.utils.validation
|
||||
import mcp_office_tools.utils.file_detection
|
||||
import mcwaddams.utils.validation
|
||||
import mcwaddams.utils.file_detection
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
if self.resolve_path:
|
||||
p1 = patch('mcp_office_tools.utils.validation.resolve_office_file_path',
|
||||
p1 = patch('mcwaddams.utils.validation.resolve_office_file_path',
|
||||
return_value=self.resolve_path)
|
||||
self.patches.append(p1)
|
||||
p1.start()
|
||||
|
||||
p2 = patch('mcp_office_tools.utils.validation.validate_office_file',
|
||||
p2 = patch('mcwaddams.utils.validation.validate_office_file',
|
||||
return_value=self.validation_result)
|
||||
self.patches.append(p2)
|
||||
p2.start()
|
||||
|
||||
p3 = patch('mcp_office_tools.utils.file_detection.detect_format',
|
||||
p3 = patch('mcwaddams.utils.file_detection.detect_format',
|
||||
return_value=self.format_detection)
|
||||
self.patches.append(p3)
|
||||
p3.start()
|
||||
|
||||
@ -20,8 +20,8 @@ from typing import Dict, Any
|
||||
from fastmcp import FastMCP
|
||||
# FastMCP testing - using direct tool access
|
||||
|
||||
from mcp_office_tools.mixins import UniversalMixin, WordMixin, ExcelMixin, PowerPointMixin
|
||||
from mcp_office_tools.utils import OfficeFileError
|
||||
from mcwaddams.mixins import UniversalMixin, WordMixin, ExcelMixin, PowerPointMixin
|
||||
from mcwaddams.utils import OfficeFileError
|
||||
|
||||
|
||||
class TestMixinArchitecture:
|
||||
@ -131,9 +131,9 @@ class TestUniversalMixinUnit:
|
||||
await universal_mixin.extract_text("/nonexistent/file.docx")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mcp_office_tools.mixins.universal.validate_office_file')
|
||||
@patch('mcp_office_tools.mixins.universal.detect_format')
|
||||
@patch('mcp_office_tools.mixins.universal.resolve_office_file_path')
|
||||
@patch('mcwaddams.mixins.universal.validate_office_file')
|
||||
@patch('mcwaddams.mixins.universal.detect_format')
|
||||
@patch('mcwaddams.mixins.universal.resolve_office_file_path')
|
||||
async def test_extract_text_csv_success(self, mock_resolve, mock_detect, mock_validate, universal_mixin, mock_csv_file):
|
||||
"""Test successful CSV text extraction with proper mocking."""
|
||||
# Setup mocks
|
||||
@ -200,9 +200,9 @@ class TestWordMixinUnit:
|
||||
await word_mixin.convert_to_markdown("/nonexistent/file.docx")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mcp_office_tools.mixins.word.validate_office_file')
|
||||
@patch('mcp_office_tools.mixins.word.detect_format')
|
||||
@patch('mcp_office_tools.mixins.word.resolve_office_file_path')
|
||||
@patch('mcwaddams.mixins.word.validate_office_file')
|
||||
@patch('mcwaddams.mixins.word.detect_format')
|
||||
@patch('mcwaddams.mixins.word.resolve_office_file_path')
|
||||
async def test_convert_to_markdown_non_word_document(self, mock_resolve, mock_detect, mock_validate, word_mixin):
|
||||
"""Test that non-Word documents are rejected for markdown conversion."""
|
||||
# Setup mocks for a non-Word document
|
||||
@ -287,9 +287,9 @@ class TestMockingStrategies:
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mcp_office_tools.mixins.universal.resolve_office_file_path')
|
||||
@patch('mcp_office_tools.mixins.universal.validate_office_file')
|
||||
@patch('mcp_office_tools.mixins.universal.detect_format')
|
||||
@patch('mcwaddams.mixins.universal.resolve_office_file_path')
|
||||
@patch('mcwaddams.mixins.universal.validate_office_file')
|
||||
@patch('mcwaddams.mixins.universal.detect_format')
|
||||
async def test_comprehensive_mocking_pattern(self, mock_detect, mock_validate, mock_resolve, mock_office_file):
|
||||
"""Demonstrate comprehensive mocking pattern for tool testing."""
|
||||
app = FastMCP("Test App")
|
||||
@ -347,8 +347,8 @@ class TestFileOperationMocking:
|
||||
universal.register_all(app)
|
||||
|
||||
# Mock only the validation/detection layers
|
||||
with patch('mcp_office_tools.utils.validation.validate_office_file') as mock_validate:
|
||||
with patch('mcp_office_tools.utils.file_detection.detect_format') as mock_detect:
|
||||
with patch('mcwaddams.utils.validation.validate_office_file') as mock_validate:
|
||||
with patch('mcwaddams.utils.file_detection.detect_format') as mock_detect:
|
||||
mock_validate.return_value = {"is_valid": True, "errors": []}
|
||||
mock_detect.return_value = {
|
||||
"category": "data",
|
||||
@ -375,9 +375,9 @@ class TestAsyncPatterns:
|
||||
universal.register_all(app)
|
||||
|
||||
# Mock all async dependencies
|
||||
with patch('mcp_office_tools.mixins.universal.resolve_office_file_path') as mock_resolve:
|
||||
with patch('mcp_office_tools.mixins.universal.validate_office_file') as mock_validate:
|
||||
with patch('mcp_office_tools.mixins.universal.detect_format') as mock_detect:
|
||||
with patch('mcwaddams.mixins.universal.resolve_office_file_path') as mock_resolve:
|
||||
with patch('mcwaddams.mixins.universal.validate_office_file') as mock_validate:
|
||||
with patch('mcwaddams.mixins.universal.detect_format') as mock_detect:
|
||||
# Make mocks properly async
|
||||
mock_resolve.return_value = "/test.csv"
|
||||
mock_validate.return_value = {"is_valid": True, "errors": []}
|
||||
|
||||
@ -8,8 +8,8 @@ from unittest.mock import patch, MagicMock
|
||||
|
||||
# FastMCP testing - using direct tool access
|
||||
|
||||
from mcp_office_tools.server import app
|
||||
from mcp_office_tools.utils import OfficeFileError
|
||||
from mcwaddams.server import app
|
||||
from mcwaddams.utils import OfficeFileError
|
||||
|
||||
|
||||
class TestServerInitialization:
|
||||
@ -54,7 +54,7 @@ class TestServerInitialization:
|
||||
def test_mixin_composition_works(self):
|
||||
"""Test that mixin composition created the expected server structure."""
|
||||
# Import the server module to ensure all mixins are initialized
|
||||
import mcp_office_tools.server as server_module
|
||||
import mcwaddams.server as server_module
|
||||
|
||||
# Verify the mixins were created
|
||||
assert hasattr(server_module, 'universal_mixin')
|
||||
@ -63,7 +63,7 @@ class TestServerInitialization:
|
||||
assert hasattr(server_module, 'powerpoint_mixin')
|
||||
|
||||
# Verify mixin instances are correct types
|
||||
from mcp_office_tools.mixins import UniversalMixin, WordMixin, ExcelMixin, PowerPointMixin
|
||||
from mcwaddams.mixins import UniversalMixin, WordMixin, ExcelMixin, PowerPointMixin
|
||||
assert isinstance(server_module.universal_mixin, UniversalMixin)
|
||||
assert isinstance(server_module.word_mixin, WordMixin)
|
||||
assert isinstance(server_module.excel_mixin, ExcelMixin)
|
||||
|
||||
@ -16,8 +16,8 @@ from pathlib import Path
|
||||
from fastmcp import FastMCP
|
||||
# FastMCP testing - using direct tool access
|
||||
|
||||
from mcp_office_tools.mixins.universal import UniversalMixin
|
||||
from mcp_office_tools.utils import OfficeFileError
|
||||
from mcwaddams.mixins.universal import UniversalMixin
|
||||
from mcwaddams.utils import OfficeFileError
|
||||
|
||||
|
||||
class TestUniversalMixinRegistration:
|
||||
@ -69,9 +69,9 @@ class TestExtractText:
|
||||
await mixin.extract_text("/nonexistent/file.docx")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mcp_office_tools.mixins.universal.resolve_office_file_path')
|
||||
@patch('mcp_office_tools.mixins.universal.validate_office_file')
|
||||
@patch('mcp_office_tools.mixins.universal.detect_format')
|
||||
@patch('mcwaddams.mixins.universal.resolve_office_file_path')
|
||||
@patch('mcwaddams.mixins.universal.validate_office_file')
|
||||
@patch('mcwaddams.mixins.universal.detect_format')
|
||||
async def test_extract_text_validation_failure(self, mock_detect, mock_validate, mock_resolve, mixin):
|
||||
"""Test extract_text with validation failure."""
|
||||
mock_resolve.return_value = "/test.docx"
|
||||
@ -84,9 +84,9 @@ class TestExtractText:
|
||||
await mixin.extract_text("/test.docx")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mcp_office_tools.mixins.universal.resolve_office_file_path')
|
||||
@patch('mcp_office_tools.mixins.universal.validate_office_file')
|
||||
@patch('mcp_office_tools.mixins.universal.detect_format')
|
||||
@patch('mcwaddams.mixins.universal.resolve_office_file_path')
|
||||
@patch('mcwaddams.mixins.universal.validate_office_file')
|
||||
@patch('mcwaddams.mixins.universal.detect_format')
|
||||
async def test_extract_text_csv_success(self, mock_detect, mock_validate, mock_resolve, mixin):
|
||||
"""Test successful CSV text extraction."""
|
||||
# Setup mocks
|
||||
@ -126,9 +126,9 @@ class TestExtractText:
|
||||
async def test_extract_text_parameter_handling(self, mixin):
|
||||
"""Test extract_text parameter validation and handling."""
|
||||
# Mock all dependencies for parameter testing
|
||||
with patch('mcp_office_tools.mixins.universal.resolve_office_file_path') as mock_resolve:
|
||||
with patch('mcp_office_tools.mixins.universal.validate_office_file') as mock_validate:
|
||||
with patch('mcp_office_tools.mixins.universal.detect_format') as mock_detect:
|
||||
with patch('mcwaddams.mixins.universal.resolve_office_file_path') as mock_resolve:
|
||||
with patch('mcwaddams.mixins.universal.validate_office_file') as mock_validate:
|
||||
with patch('mcwaddams.mixins.universal.detect_format') as mock_detect:
|
||||
mock_resolve.return_value = "/test.docx"
|
||||
mock_validate.return_value = {"is_valid": True, "errors": []}
|
||||
mock_detect.return_value = {"category": "word", "extension": ".docx", "format_name": "Word"}
|
||||
@ -174,9 +174,9 @@ class TestExtractImages:
|
||||
await mixin.extract_images("/nonexistent/file.docx")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mcp_office_tools.mixins.universal.resolve_office_file_path')
|
||||
@patch('mcp_office_tools.mixins.universal.validate_office_file')
|
||||
@patch('mcp_office_tools.mixins.universal.detect_format')
|
||||
@patch('mcwaddams.mixins.universal.resolve_office_file_path')
|
||||
@patch('mcwaddams.mixins.universal.validate_office_file')
|
||||
@patch('mcwaddams.mixins.universal.detect_format')
|
||||
async def test_extract_images_unsupported_format(self, mock_detect, mock_validate, mock_resolve, mixin):
|
||||
"""Test extract_images with unsupported format (CSV) returns empty list."""
|
||||
mock_resolve.return_value = "/test.csv"
|
||||
@ -263,9 +263,9 @@ class TestDocumentHealth:
|
||||
return mixin
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mcp_office_tools.mixins.universal.resolve_office_file_path')
|
||||
@patch('mcp_office_tools.mixins.universal.validate_office_file')
|
||||
@patch('mcp_office_tools.mixins.universal.detect_format')
|
||||
@patch('mcwaddams.mixins.universal.resolve_office_file_path')
|
||||
@patch('mcwaddams.mixins.universal.validate_office_file')
|
||||
@patch('mcwaddams.mixins.universal.detect_format')
|
||||
async def test_analyze_document_health_success(self, mock_detect, mock_validate, mock_resolve, mixin):
|
||||
"""Test successful document health analysis."""
|
||||
mock_resolve.return_value = "/test.docx"
|
||||
@ -343,9 +343,9 @@ class TestMockingPatterns:
|
||||
async def test_comprehensive_mocking_pattern(self, mixin):
|
||||
"""Demonstrate comprehensive mocking for complex tool testing."""
|
||||
# Mock all external dependencies
|
||||
with patch('mcp_office_tools.mixins.universal.resolve_office_file_path') as mock_resolve:
|
||||
with patch('mcp_office_tools.mixins.universal.validate_office_file') as mock_validate:
|
||||
with patch('mcp_office_tools.mixins.universal.detect_format') as mock_detect:
|
||||
with patch('mcwaddams.mixins.universal.resolve_office_file_path') as mock_resolve:
|
||||
with patch('mcwaddams.mixins.universal.validate_office_file') as mock_validate:
|
||||
with patch('mcwaddams.mixins.universal.detect_format') as mock_detect:
|
||||
|
||||
# Setup realistic mock responses
|
||||
mock_resolve.return_value = "/realistic/path/document.docx"
|
||||
|
||||
@ -14,8 +14,8 @@ from pathlib import Path
|
||||
from fastmcp import FastMCP
|
||||
# FastMCP testing - using direct tool access
|
||||
|
||||
from mcp_office_tools.mixins.word import WordMixin
|
||||
from mcp_office_tools.utils import OfficeFileError
|
||||
from mcwaddams.mixins.word import WordMixin
|
||||
from mcwaddams.utils import OfficeFileError
|
||||
|
||||
|
||||
class TestWordMixinRegistration:
|
||||
@ -58,9 +58,9 @@ class TestConvertToMarkdown:
|
||||
await mixin.convert_to_markdown("/nonexistent/file.docx")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mcp_office_tools.mixins.word.resolve_office_file_path')
|
||||
@patch('mcp_office_tools.mixins.word.validate_office_file')
|
||||
@patch('mcp_office_tools.mixins.word.detect_format')
|
||||
@patch('mcwaddams.mixins.word.resolve_office_file_path')
|
||||
@patch('mcwaddams.mixins.word.validate_office_file')
|
||||
@patch('mcwaddams.mixins.word.detect_format')
|
||||
async def test_convert_to_markdown_validation_failure(self, mock_detect, mock_validate, mock_resolve, mixin):
|
||||
"""Test convert_to_markdown with validation failure."""
|
||||
mock_resolve.return_value = "/test.docx"
|
||||
@ -73,9 +73,9 @@ class TestConvertToMarkdown:
|
||||
await mixin.convert_to_markdown("/test.docx")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mcp_office_tools.mixins.word.resolve_office_file_path')
|
||||
@patch('mcp_office_tools.mixins.word.validate_office_file')
|
||||
@patch('mcp_office_tools.mixins.word.detect_format')
|
||||
@patch('mcwaddams.mixins.word.resolve_office_file_path')
|
||||
@patch('mcwaddams.mixins.word.validate_office_file')
|
||||
@patch('mcwaddams.mixins.word.detect_format')
|
||||
async def test_convert_to_markdown_non_word_document(self, mock_detect, mock_validate, mock_resolve, mixin):
|
||||
"""Test that non-Word documents are rejected."""
|
||||
mock_resolve.return_value = "/test.xlsx"
|
||||
@ -90,9 +90,9 @@ class TestConvertToMarkdown:
|
||||
await mixin.convert_to_markdown("/test.xlsx")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mcp_office_tools.mixins.word.resolve_office_file_path')
|
||||
@patch('mcp_office_tools.mixins.word.validate_office_file')
|
||||
@patch('mcp_office_tools.mixins.word.detect_format')
|
||||
@patch('mcwaddams.mixins.word.resolve_office_file_path')
|
||||
@patch('mcwaddams.mixins.word.validate_office_file')
|
||||
@patch('mcwaddams.mixins.word.detect_format')
|
||||
async def test_convert_to_markdown_docx_success(self, mock_detect, mock_validate, mock_resolve, mixin):
|
||||
"""Test successful DOCX to markdown conversion."""
|
||||
# Setup mocks
|
||||
@ -141,9 +141,9 @@ class TestConvertToMarkdown:
|
||||
async def test_convert_to_markdown_parameter_handling(self, mixin):
|
||||
"""Test convert_to_markdown parameter validation and handling."""
|
||||
# Mock all dependencies for parameter testing
|
||||
with patch('mcp_office_tools.mixins.word.resolve_office_file_path') as mock_resolve:
|
||||
with patch('mcp_office_tools.mixins.word.validate_office_file') as mock_validate:
|
||||
with patch('mcp_office_tools.mixins.word.detect_format') as mock_detect:
|
||||
with patch('mcwaddams.mixins.word.resolve_office_file_path') as mock_resolve:
|
||||
with patch('mcwaddams.mixins.word.validate_office_file') as mock_validate:
|
||||
with patch('mcwaddams.mixins.word.detect_format') as mock_detect:
|
||||
mock_resolve.return_value = "/test.docx"
|
||||
mock_validate.return_value = {"is_valid": True, "errors": []}
|
||||
mock_detect.return_value = {"category": "word", "extension": ".docx", "format_name": "Word"}
|
||||
@ -185,9 +185,9 @@ class TestConvertToMarkdown:
|
||||
@pytest.mark.asyncio
|
||||
async def test_convert_to_markdown_bookmark_priority(self, mixin):
|
||||
"""Test that bookmark extraction takes priority over page ranges."""
|
||||
with patch('mcp_office_tools.mixins.word.resolve_office_file_path') as mock_resolve:
|
||||
with patch('mcp_office_tools.mixins.word.validate_office_file') as mock_validate:
|
||||
with patch('mcp_office_tools.mixins.word.detect_format') as mock_detect:
|
||||
with patch('mcwaddams.mixins.word.resolve_office_file_path') as mock_resolve:
|
||||
with patch('mcwaddams.mixins.word.validate_office_file') as mock_validate:
|
||||
with patch('mcwaddams.mixins.word.detect_format') as mock_detect:
|
||||
mock_resolve.return_value = "/test.docx"
|
||||
mock_validate.return_value = {"is_valid": True, "errors": []}
|
||||
mock_detect.return_value = {"category": "word", "extension": ".docx", "format_name": "Word"}
|
||||
@ -225,9 +225,9 @@ class TestConvertToMarkdown:
|
||||
@pytest.mark.asyncio
|
||||
async def test_convert_to_markdown_summary_mode(self, mixin):
|
||||
"""Test summary_only mode functionality."""
|
||||
with patch('mcp_office_tools.mixins.word.resolve_office_file_path') as mock_resolve:
|
||||
with patch('mcp_office_tools.mixins.word.validate_office_file') as mock_validate:
|
||||
with patch('mcp_office_tools.mixins.word.detect_format') as mock_detect:
|
||||
with patch('mcwaddams.mixins.word.resolve_office_file_path') as mock_resolve:
|
||||
with patch('mcwaddams.mixins.word.validate_office_file') as mock_validate:
|
||||
with patch('mcwaddams.mixins.word.detect_format') as mock_detect:
|
||||
mock_resolve.return_value = "/test.docx"
|
||||
mock_validate.return_value = {"is_valid": True, "errors": []}
|
||||
mock_detect.return_value = {"category": "word", "extension": ".docx", "format_name": "Word"}
|
||||
@ -373,9 +373,9 @@ class TestLegacyWordSupport:
|
||||
return mixin
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mcp_office_tools.mixins.word.resolve_office_file_path')
|
||||
@patch('mcp_office_tools.mixins.word.validate_office_file')
|
||||
@patch('mcp_office_tools.mixins.word.detect_format')
|
||||
@patch('mcwaddams.mixins.word.resolve_office_file_path')
|
||||
@patch('mcwaddams.mixins.word.validate_office_file')
|
||||
@patch('mcwaddams.mixins.word.detect_format')
|
||||
async def test_convert_legacy_doc_to_markdown(self, mock_detect, mock_validate, mock_resolve, mixin):
|
||||
"""Test conversion of legacy .doc files."""
|
||||
mock_resolve.return_value = "/test.doc"
|
||||
@ -425,9 +425,9 @@ class TestPageRangeFiltering:
|
||||
return mixin
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('mcp_office_tools.mixins.word.resolve_office_file_path')
|
||||
@patch('mcp_office_tools.mixins.word.validate_office_file')
|
||||
@patch('mcp_office_tools.mixins.word.detect_format')
|
||||
@patch('mcwaddams.mixins.word.resolve_office_file_path')
|
||||
@patch('mcwaddams.mixins.word.validate_office_file')
|
||||
@patch('mcwaddams.mixins.word.detect_format')
|
||||
async def test_page_range_filters_different_content(self, mock_detect, mock_validate, mock_resolve, mixin):
|
||||
"""Test that different page_range values return different content.
|
||||
|
||||
|
||||
@ -17,8 +17,8 @@ warnings.filterwarnings("ignore", category=FutureWarning)
|
||||
# Add src to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
|
||||
|
||||
from mcp_office_tools.mixins.excel import ExcelMixin
|
||||
from mcp_office_tools.mixins.word import WordMixin
|
||||
from mcwaddams.mixins.excel import ExcelMixin
|
||||
from mcwaddams.mixins.word import WordMixin
|
||||
|
||||
|
||||
# Test files - real files from user's system
|
||||
|
||||
2
uv.lock
generated
2
uv.lock
generated
@ -1537,7 +1537,7 @@ wheels = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mcp-office-tools"
|
||||
name = "mcwaddams"
|
||||
version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
# Quick script to open the test dashboard in browser
|
||||
|
||||
DASHBOARD_PATH="/home/rpm/claude/mcp-office-tools/reports/test_dashboard.html"
|
||||
DASHBOARD_PATH="/home/rpm/claude/mcwaddams/reports/test_dashboard.html"
|
||||
|
||||
echo "📊 Opening MCP Office Tools Test Dashboard..."
|
||||
echo "Dashboard: $DASHBOARD_PATH"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user