#
tokens: 49704/50000 63/85 files (page 1/3)
lines: off (toggle) GitHub
raw markdown copy
This is page 1 of 3. Use http://codebase.md/stevereiner/python-alfresco-mcp-server?page={x} to view the full context.

# Directory Structure

```
├── .gitattributes
├── .gitignore
├── .vscode
│   ├── mcp.json
│   └── settings.json
├── alfresco_mcp_server
│   ├── __init__.py
│   ├── config.py
│   ├── fastmcp_server.py
│   ├── prompts
│   │   ├── __init__.py
│   │   └── search_and_analyze.py
│   ├── resources
│   │   ├── __init__.py
│   │   └── repository_resources.py
│   ├── tools
│   │   ├── __init__.py
│   │   ├── core
│   │   │   ├── __init__.py
│   │   │   ├── browse_repository.py
│   │   │   ├── cancel_checkout.py
│   │   │   ├── checkin_document.py
│   │   │   ├── checkout_document.py
│   │   │   ├── create_folder.py
│   │   │   ├── delete_node.py
│   │   │   ├── download_document.py
│   │   │   ├── get_node_properties.py
│   │   │   ├── update_node_properties.py
│   │   │   └── upload_document.py
│   │   └── search
│   │       ├── __init__.py
│   │       ├── advanced_search.py
│   │       ├── cmis_search.py
│   │       ├── search_by_metadata.py
│   │       └── search_content.py
│   └── utils
│       ├── __init__.py
│       ├── connection.py
│       ├── file_type_analysis.py
│       └── json_utils.py
├── CHANGELOG.md
├── claude-desktop-config-pipx-macos.json
├── claude-desktop-config-pipx-windows.json
├── claude-desktop-config-uv-macos.json
├── claude-desktop-config-uv-windows.json
├── claude-desktop-config-uvx-macos.json
├── claude-desktop-config-uvx-windows.json
├── config.yaml
├── docs
│   ├── api_reference.md
│   ├── claude_desktop_setup.md
│   ├── client_configurations.md
│   ├── configuration_guide.md
│   ├── install_with_pip_pipx.md
│   ├── mcp_inspector_setup.md
│   ├── quick_start_guide.md
│   ├── README.md
│   ├── testing_guide.md
│   └── troubleshooting.md
├── examples
│   ├── batch_operations.py
│   ├── document_lifecycle.py
│   ├── error_handling.py
│   ├── examples_summary.md
│   ├── quick_start.py
│   ├── README.md
│   └── transport_examples.py
├── LICENSE
├── MANIFEST.in
├── mcp-inspector-http-pipx-config.json
├── mcp-inspector-http-uv-config.json
├── mcp-inspector-http-uvx-config.json
├── mcp-inspector-stdio-pipx-config.json
├── mcp-inspector-stdio-uv-config.json
├── mcp-inspector-stdio-uvx-config.json
├── prompts-for-claude.md
├── pyproject.toml
├── pytest.ini
├── README.md
├── run_server.py
├── sample-dot-env.txt
├── scripts
│   ├── run_tests.py
│   └── test.bat
├── tests
│   ├── __init__.py
│   ├── conftest.py
│   ├── mcp_specific
│   │   ├── MCP_INSPECTOR_CONNECTION.md
│   │   ├── mcp_testing_guide.md
│   │   ├── START_HTTP_SERVER.md
│   │   ├── START_MCP_INSPECTOR.md
│   │   ├── test_http_server.ps1
│   │   ├── test_with_mcp_inspector.md
│   │   └── TESTING_INSTRUCTIONS.md
│   ├── README.md
│   ├── test_coverage.py
│   ├── test_fastmcp_2_0.py
│   ├── test_integration.py
│   └── test_unit_tools.py
├── tests-debug
│   └── README.md
└── uv.lock
```

# Files

--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------

```
# Auto detect text files and perform LF normalization
* text=auto

```

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

```
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Additional testing framework artifacts
.pytest_cache/
pytest.ini.bak
test-results/
test-reports/
.testmondata
junit.xml
report.xml

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
#   For a library or package, you might want to ignore these files since the code is
#   intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# UV
#   Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
#   This is especially recommended for binary packages to ensure reproducibility, and is more
#   commonly ignored for libraries.
#uv.lock

# poetry
#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
#   This is especially recommended for binary packages to ensure reproducibility, and is more
#   commonly ignored for libraries.
#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
#   in version control.
#   https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
#  and can be added to the global gitignore or merged into this file.  For a more nuclear
#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# VS Code
.vscode/settings.json
.vscode/launch.json
.vscode/extensions.json
.vscode/tasks.json
.vscode/.ropeproject
# Keep workspace settings but ignore personal settings
!.vscode/settings.json.template
!.vscode/mcp.json

# Ruff stuff:
.ruff_cache/

# PyPI configuration file
.pypirc

# Cursor  
#  Cursor is an AI-powered code editor.`.cursorignore` specifies files/directories to 
#  exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
#  refer to https://docs.cursor.com/context/ignore-files
.cursorignore
.cursorindexingignore
*.cursormemory
.cursormemory
memory-bank/

# FastMCP 2.0 and MCP Development
# MCP client/server communication logs
*.mcp.log
mcp_*.log
fastmcp_*.log

# MCP development artifacts
.mcp_cache/
mcp_debug/
.fastmcp/

# Alfresco MCP Server specific
alfresco_mcp_server.log*
alfresco_*.log
connection_test.py
debug_*.py

# Development and testing artifacts
.benchmarks/
performance_results/
integration_test_results/
mock_data/
test_uploads/
test_downloads/

# Configuration files with secrets (keep templates)
config.local.yaml
config.dev.yaml
config.prod.yaml
.env.local
.env.dev
.env.prod
# Keep sample config files
!sample-*.txt
!config.sample.*
!.env.sample

# Experimental/scratch files
o.txt
*.tmp
test_*.py
*_test.py
scratch*
temp*
debug_output.txt
output.txt
```

--------------------------------------------------------------------------------
/tests-debug/README.md:
--------------------------------------------------------------------------------

```markdown
# Debug Test Files

This directory contains one-off debug and development test files that are not part of the main testing suite.

These files were moved from the root directory to clean up the project structure:

- `test_additional_clients.py` - Testing additional client imports
- `test_fixed_auth.py` - Authentication testing
- `test_mcp_direct.py` - Direct MCP testing
- `test_mcp_tools.py` - MCP tools testing  
- `test_sse_endpoint.py` - SSE endpoint testing
- `test_update_debug.py` - Update debugging
- `test_update_direct.py` - Direct update testing

These are development/debugging tools and not included in the main test suite in `/tests/`. 
```

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

```markdown
# Testing Structure

This directory contains tests organized by their purpose.

## MCP Server Tests
- `test_*.py` files in the root test the MCP server functionality
- `mcp_specific/` contains files specifically for testing MCP server deployment

### Main Test Files (MCP Server Functionality)
- `test_unit_tools.py` - Unit tests for MCP tools
- `test_integration.py` - Integration tests with Alfresco
- `test_fastmcp_2_0.py` - FastMCP 2.0 specific tests
- `test_coverage.py` - Test coverage validation
- `test_authentication.py` - Authentication tests
- `test_search_debug.py` - Search functionality debugging
- `test_simple_search.py` - Simple search tests
- `test_response_structure.py` - Response structure validation
- `test_comprehensive_scenarios.py` - Comprehensive scenario testing

### MCP Specific (Deployment & Inspector)
- `mcp_specific/test_with_mcp_client.py` - MCP client testing
- `mcp_specific/test_http_server.ps1` - HTTP server testing script
- `mcp_specific/mcp_testing_guide.md` - Testing guide
- `mcp_specific/test_with_mcp_inspector.md` - MCP Inspector testing
- `mcp_specific/test_server_status.py` - Server status check

## Running Tests

### Unit Tests
```bash
python -m pytest tests/test_unit_tools.py -v
```

### Integration Tests (requires live Alfresco)
```bash
python -m pytest tests/test_integration.py -v
```

### MCP Server Status
```bash
python tests/mcp_specific/test_server_status.py
```

## Testing with MCP Inspector
1. Start HTTP server: `fastmcp run alfresco_mcp_server.fastmcp_server --host localhost --port 8003`
2. Use MCP Inspector with URL: `http://localhost:8003`
3. Note: MCP Inspector shows warning about risky auth and includes token in URL

## Testing with Claude Desktop
1. Update `claude-desktop-config.json` with current paths
2. Restart Claude Desktop application
3. Test MCP tools directly in Claude conversation 
```

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

```markdown
# Alfresco MCP Server Documentation

Welcome to the documentation for the Alfresco MCP Server! This directory contains guides, tutorials, and reference materials to help you get the most out of your Alfresco integration.

## 📚 Documentation Index

### 🚀 Getting Started
- [`quick_start_guide.md`](quick_start_guide.md) - Get up and running in 5 minutes
- [`configuration_guide.md`](configuration_guide.md) - Configuration options and setup

### 🔧 Technical Guides
- [`api_reference.md`](api_reference.md) - Complete API reference for all 15 tools

### 🏗️ Development & Testing
- [`testing_guide.md`](testing_guide.md) - Running tests and validation

### 🆘 Support & Troubleshooting
- [`troubleshooting.md`](troubleshooting.md) - Common issues and solutions

## 🎯 Quick Navigation

| I want to... | Read this |
|--------------|-----------|
| **Get started quickly** | [`quick_start_guide.md`](quick_start_guide.md) |
| **Configure my setup** | [`configuration_guide.md`](configuration_guide.md) |
| **Learn about all tools** | [`api_reference.md`](api_reference.md) |
| **Run tests** | [`testing_guide.md`](testing_guide.md) |
| **Troubleshoot issues** | [`troubleshooting.md`](troubleshooting.md) |

## 📖 Documentation Standards

All documentation in this directory follows these standards:

- ✅ **Clear Examples**: Every feature includes working code examples
- ✅ **Step-by-Step**: Complex procedures broken into manageable steps
- ✅ **Cross-Referenced**: Related topics linked throughout
- ✅ **Tested**: All code examples are tested and verified
- ✅ **Version Aware**: Marked with applicable version information

## 🔄 Recent Updates

- **v1.1.0**: Updated tool count and improved documentation
- **v1.1.0**: Corrected tool count to 15 (4 search + 11 core tools)
- **v1.1.0**: Removed marketing language for professional tone
- **v1.0.0**: Initial production release with FastMCP 2.0

## 🤝 Contributing to Documentation

Found an error or want to improve the docs? Create an issue or pull request on GitHub.

## 📞 Need Help?

- 🔍 Browse [`troubleshooting.md`](troubleshooting.md) for solutions
- 🐛 Report issues via GitHub Issues

---

**📌 Pro Tip**: Bookmark this page and use it as your central hub for all Alfresco MCP Server documentation needs! 
```

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

```markdown
# Alfresco MCP Server Examples

This directory contains practical examples demonstrating how to use the Alfresco MCP Server's **15 tools** across search, core operations, and workflow management in different scenarios.

## 📋 Available Examples

### 🚀 Quick Start Examples
- [`quick_start.py`](quick_start.py) - Basic server setup and first tool call

### 🔧 Transport Examples  
- [`transport_examples.py`](transport_examples.py) - All transport protocols (STDIO, HTTP, SSE)

### 🛠️ Tool Usage Examples
- [`document_lifecycle.py`](document_lifecycle.py) - Complete document management workflow
- [`batch_operations.py`](batch_operations.py) - Bulk document processing

### 📊 Additional Examples
- [`error_handling.py`](error_handling.py) - Error handling patterns

### 📖 Documentation Summary
- [`examples_summary.md`](examples_summary.md) - Overview of all examples and documentation

## 🎯 Getting Started

1. **Install Dependencies**: Ensure you have the server installed
2. **Set Up Alfresco**: Configure your Alfresco connection
3. **Run Examples**: Each example is self-contained and well-documented

## 🔧 Prerequisites

```bash
# Install the package
pip install -e .

# Set environment variables
export ALFRESCO_URL="http://localhost:8080"
export ALFRESCO_USERNAME="admin"  
export ALFRESCO_PASSWORD="admin"
```

## 📖 Example Structure

Each example includes:
- ✅ **Clear documentation** of what it demonstrates
- ✅ **Step-by-step comments** explaining each operation
- ✅ **Error handling** best practices
- ✅ **Expected output** descriptions
- ✅ **Practical use cases** and scenarios

## 🚀 Running Examples

```bash
# Quick start example
python examples/quick_start.py

# Document lifecycle workflow
python examples/document_lifecycle.py

# Transport protocols demonstration
python examples/transport_examples.py

# Batch operations and performance
python examples/batch_operations.py

# Error handling patterns
python examples/error_handling.py
```

## 💡 Tips

- Start with `quick_start.py` for your first experience
- Check `error_handling.py` for production-ready patterns
- Use `batch_operations.py` for performance optimization insights
- Explore `transport_examples.py` for different connection methods
- Review `examples_summary.md` for documentation overview 
```

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

```markdown
# Python Alfresco MCP Server v1.1 🚀

[![PyPI version](https://img.shields.io/pypi/v/python-alfresco-mcp-server)](https://pypi.org/project/python-alfresco-mcp-server/)
[![PyPI downloads](https://pepy.tech/badge/python-alfresco-mcp-server)](https://pepy.tech/project/python-alfresco-mcp-server)
[![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://pypi.org/project/python-alfresco-mcp-server/)
[![License](https://img.shields.io/github/license/stevereiner/python-alfresco-mcp-server)](https://github.com/stevereiner/python-alfresco-mcp-server/blob/main/LICENSE)

**Model Context Protocol Server for Alfresco Content Services**

A full featured MCP server for Alfresco in search and content management areas. It provides the following tools: full text search (content and properties), advanced search, metadata search, CMIS SQL like search, upload, download,
checkin, checkout, cancel checkout, create folder, folder browse, delete node, and get/set properties. Also has a  tool for getting repository status/config (also a resource). Has one prompt example.
Built with [FastMCP 2.0](https://github.com/jlowin/FastMCP). 
Features complete documentation, examples, and 
config for various MCP clients (Claude Desktop, MCP Inspector, references to configuring others).

## 🌟 What's New in v1.1

### **Modular Architecture & Enhanced Testing**
- **FastMCP**: v1.0 had FastMCP 2.0 implementation that had all tools implementations in the fastmcp_server.py file
- **Code Modularization in v1.1**: Split monolithic single file into organized modular structure with separate files
- **Directory Organization**: Organized into `tools/search/`, `tools/core/`, `resources/`, `prompts/`, `utils/` directories
- **Enhanced Testing**: Complete test suite transformation - 143 tests with 100% pass rate
- **Client Configuration Files**: Added dedicated Claude Desktop and MCP Inspector configuration files
- **Live Integration Testing**: 21 Alfresco server validation tests for real-world functionality
- **Python-Alfresco-API**:  python-alfresco-mcp-server v1.1  requires the v1.1.1 python-alfresco-api package

## 📚 Complete Documentation

### **Documentation & Examples**
- **📚 Complete Documentation**: 10 guides covering setup to deployment
- **💡 Examples**: 6 practical examples from quick start to implementation patterns  
- **🔧 Configuration Management**: Environment variables, .env files, and command-line configuration
- **🏗️ Setup instruction for use with MCP client

### **Learning Resources**
- **🚀 [Quick Start Guide](./docs/quick_start_guide.md)**: 5-minute setup and first operations
- **🤖 [Claude Desktop Setup](./docs/claude_desktop_setup.md)**: Complete Claude Desktop configuration for users and developers
- **🔧 [Client Configurations](./docs/client_configurations.md)**: Setup guide for Cursor, Claude Code, and other MCP clients
- **📖 [Examples Library](./examples/README.md)**: Implementation patterns and examples

### 📖 Guides covering setup, deployment, and usage:

- **[📚 Documentation Hub](./docs/README.md)** - Complete navigation and overview
- **[🚀 Quick Start Guide](./docs/quick_start_guide.md)** - 5-minute setup and first operations
- **[📦 Installation with pip and pipx](./docs/install_with_pip_pipx.md)** - Traditional Python package installation methods
- **[🤖 Claude Desktop Setup](./docs/claude_desktop_setup.md)** - Complete Claude Desktop configuration for users and developers
- **[🔧 Client Configurations](./docs/client_configurations.md)** - Setup guide for Cursor, Claude Code, and other MCP clients
- **[🔍 MCP Inspector Setup](./docs/mcp_inspector_setup.md)** - Development and testing with MCP Inspector
- **[🔍 API Reference](./docs/api_reference.md)** - Complete tool and resource documentation
- **[⚙️ Configuration Guide](./docs/configuration_guide.md)** - Development to deployment
- **[🧪 Testing Guide](./docs/testing_guide.md)** - Quality assurance and test development
- **[🛠️ Troubleshooting Guide](./docs/troubleshooting.md)** - Problem diagnosis and resolution

## 🚀 Features

### Content Management and Search Tools
- **Search Tools**: 
  - **Full Text Search**: Basic content search with wildcard support (search_content)
  - **Advanced Search**: AFTS query language with date filters, sorting, and field targeting
  - **Metadata Search**: Property-based queries with operators (equals, contains, date ranges)
  - **CMIS Search**: SQL like queries for complex content discovery
- **Document Lifecycle**: Upload, download, check-in, checkout, cancel checkout
- **Version Management**: Create major/minor versions with comments
- **Folder Operations**: Create folders, delete folder nodes
- **Property Management**: Get and set document/folder properties and names
- **Node Operations**: Delete nodes (documents and folders) (trash or permanent)
- **Repository Info**: (Tool and Resource) Returns repository status, version and whether Community or Enterprise, and module configuration

### MCP Architecture
- **FastMCP 2.0 Framework**: Modern, high-performance MCP server implementation
- **Multiple Transports**: 
  - **STDIO** (direct MCP protocol) - Default and fastest
  - **HTTP** (RESTful API) - Web services and testing
  - **SSE** (Server-Sent Events) - Real-time streaming updates
- **Enterprise Security**: OAuth 2.1  (optional)
- **Type Safety**: Full Pydantic v2 models
- **In-Memory Testing**: Client testing with faster execution
- **Configuration**: Environment variables, .env files

### Alfresco Integration 
Works with Alfresco Community (tested) and Enterprise editions


## 📋 Requirements

- Python 3.10+
- Alfresco Content Services (Community or Enterprise)

> **Note**: The `python-alfresco-api >= 1.1.1` dependency is automatically installed with `python-alfresco-mcp-server`

## 🛠️ Installation

### Install Python

You need to have Python 3.10+ installed for the sections below. If not, download the latest 3.13.x version from:

[Python.org Downloads](https://www.python.org/downloads/)

### UV/UVX Setup (Recommended)

**UV** is a modern Python package manager written in **Rust** that provides both `uv` (package manager) and `uvx` (tool runner). **Much faster than pip** due to its compiled nature and optimized dependency resolution.

```bash
# Install UV (provides both uv and uvx commands)
# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"

# macOS/Linux  
curl -LsSf https://astral.sh/uv/install.sh | sh

# Or via pip if you prefer
pip install uv

# Verify installation (both commands should work)
uv --version
uvx --version
```

**UV Reference Links:**
- **[UV Installation Guide](https://docs.astral.sh/uv/getting-started/installation/)** - Official installation instructions and platform-specific options
- **[UV Documentation](https://docs.astral.sh/uv/)** - Complete UV documentation, guides, and advanced usage

### Option A: UVX - Modern Tool Runner (Recommended for Users)

**UVX** is UV's tool runner - similar to pipx but faster and more modern. Automatically handles isolation and global availability:

```bash
# Install python-alfresco-mcp-server with uvx (after UV/UVX setup above)
uvx python-alfresco-mcp-server --help

# This tests that installation worked - UVX automatically installs packages on first use!
```

**Why UVX?** UVX combines the benefits of pipx (isolated environments + global availability) with UV's Rust-based speed and modern dependency resolution. It automatically installs packages on first use.

### Option B: UV - Modern Package Manager (Recommended for Development)

**UV** is a modern Python package manager written in **Rust** that handles everything automatically. **Much faster than pip** due to its compiled nature and optimized dependency resolution.

```bash
# Install and run from PyPI (fastest for users)
uv tool install python-alfresco-mcp-server
uv tool run python-alfresco-mcp-server --help  # Tests that installation worked

# Or install from source (for development)
git clone https://github.com/stevereiner/python-alfresco-mcp-server.git
cd python-alfresco-mcp-server
uv run python-alfresco-mcp-server --help  # Tests that installation worked
```

### Option C: Traditional Methods (pip and pipx)

For traditional Python package management approaches, see the **[Installation with pip and pipx](./docs/install_with_pip_pipx.md)**.

**Note**: You still need to configure your MCP client (Claude Desktop, MCP Inspector, etc.) with the appropriate configuration. See the [MCP Client Setup and Use](#mcp-client-setup-and-use) section below for client configuration details.

### Source Installation (For Development)

For development or access to latest features:

```bash
# 1. Clone the repository
git clone https://github.com/stevereiner/python-alfresco-mcp-server.git
cd python-alfresco-mcp-server

# 2. UV handles everything automatically - run immediately!
uv run python-alfresco-mcp-server --help  # Tests that installation worked

# Or install dependencies explicitly for development:
uv sync                    # Basic dependencies
uv sync --extra dev        # With development tools  
uv sync --extra test       # With testing tools
uv sync --extra all        # Everything
```

### 4. Configure Alfresco Connection

**Option 1: Environment Variables**
```bash
# Linux/Mac
export ALFRESCO_URL="http://localhost:8080"
export ALFRESCO_USERNAME="admin"
export ALFRESCO_PASSWORD="admin"
export ALFRESCO_VERIFY_SSL="false"

# Windows PowerShell
$env:ALFRESCO_URL="http://localhost:8080"
$env:ALFRESCO_USERNAME="admin"
$env:ALFRESCO_PASSWORD="admin"
$env:ALFRESCO_VERIFY_SSL="false"

# Windows Command Prompt
set ALFRESCO_URL=http://localhost:8080
set ALFRESCO_USERNAME=admin
set ALFRESCO_PASSWORD=admin
set ALFRESCO_VERIFY_SSL=false
```

**Option 2: .env file** (recommended - cross-platform):
```bash
# Copy sample-dot-env.txt to .env and customize
# Linux/macOS
cp sample-dot-env.txt .env

# Windows
copy sample-dot-env.txt .env

# Edit .env file with your settings
ALFRESCO_URL=http://localhost:8080
ALFRESCO_USERNAME=admin
ALFRESCO_PASSWORD=admin
ALFRESCO_VERIFY_SSL=false
```
> **Note**: The `.env` file is not checked into git for security. Use `sample-dot-env.txt` as a template.

📖 **See [Configuration Guide](./docs/configuration_guide.md) for complete setup options**

## Alfresco Installation 

If you don't have an Alfresco server installed you can get a docker for the 
Community version from Github

```bash
git clone https://github.com/Alfresco/acs-deployment.git
```

**Move to Docker Compose directory**

```bash
cd acs-deployment/docker-compose
```

**Edit community-compose.yaml**
- Note: you will likely need to comment out activemq ports other than 8161

```bash   
   ports:
   - "8161:8161" # Web Console
   #- "5672:5672" # AMQP
   #- "61616:61616" # OpenWire
   #- "61613:61613" # STOMP
```      

**Start Alfresco with Docker Compose**

```bash
docker-compose -f community-compose.yaml up
```

## 🚀 Usage

### MCP Server Startup

**With UVX (Recommended - Automatic isolation and global availability):**

```bash
# Run MCP server with STDIO transport (default)
uvx python-alfresco-mcp-server

# HTTP transport for web services (matches MCP Inspector)
uvx python-alfresco-mcp-server --transport http --host 127.0.0.1 --port 8003

# SSE transport for real-time streaming  
uvx python-alfresco-mcp-server --transport sse --host 127.0.0.1 --port 8001
```

**With UV (For development or source installations):**

```bash
# Run MCP server with STDIO transport (default)
uv run python-alfresco-mcp-server

# HTTP transport for web services (matches MCP Inspector)
uv run python-alfresco-mcp-server --transport http --host 127.0.0.1 --port 8003

# SSE transport for real-time streaming  
uv run python-alfresco-mcp-server --transport sse --host 127.0.0.1 --port 8001
```

**With Traditional Methods (pip/pipx):**

See the **[Installation with pip and pipx](./docs/install_with_pip_pipx.md)** for pip and pipx usage instructions.

### MCP Client Setup and Use

Python-Alfresco-MCP-Server was tested with Claude Desktop which is recommended as an end user MCP client. Python-Alfresco-MCP-Server was also tested with MCP Inspector which is recommended for developers to test tools with argument values.

#### 🤖 **Claude Desktop** for Windows (tested) and MacOS (not tested)

📖 **Complete Setup Guide**: **[Claude Desktop Setup Guide](./docs/claude_desktop_setup.md)**

**📥 Download Claude Desktop (Free and Pro versions):**
- **[Download Claude Desktop](https://claude.ai/download)** - Official Anthropic download page
- Available for **Windows** and **macOS** only (no Linux version)
- **Free tier** includes full MCP support and Claude Sonnet 4 access with limits, older Claude models
(Claude Opus 4 only in Pro)


**🔧 Claude Desktop Configuration by Installation Method:**

The Claude Desktop configuration differs based on how you installed the MCP server:

**1. UVX (Recommended - Modern tool runner):**
```json
{
  "command": "uvx",
  "args": ["python-alfresco-mcp-server", "--transport", "stdio"]
}
```
- **Sample Config Files:**
  - Windows: [`claude-desktop-config-uvx-windows.json`](./claude-desktop-config-uvx-windows.json)
  - macOS: [`claude-desktop-config-uvx-macos.json`](./claude-desktop-config-uvx-macos.json)
- UVX automatically handles isolation and global availability
- Fastest and most modern approach

**2. UV (Development or source installations):**
```json
{
  "command": "uv",
  "args": ["run", "python-alfresco-mcp-server", "--transport", "stdio"],
  "cwd": "C:\\path\\to\\python-alfresco-mcp-server"
}
```
- **Sample Config Files:**
  - Windows: [`claude-desktop-config-uv-windows.json`](./claude-desktop-config-uv-windows.json)
  - macOS: [`claude-desktop-config-uv-macos.json`](./claude-desktop-config-uv-macos.json)
- Uses `uv run` with `cwd` pointing to your **project directory**
- UV automatically finds and uses the `.venv` from the project directory
- Works for both source installations and after `uv tool install`

**3. Traditional Methods (pipx/pip):**

For traditional installation methods, see the **[Installation with pip and pipx](./docs/install_with_pip_pipx.md)** which covers:
- **pipx**: [`claude-desktop-config-pipx-windows.json`](./claude-desktop-config-pipx-windows.json) / [`claude-desktop-config-pipx-macos.json`](./claude-desktop-config-pipx-macos.json)
- **pip**: Manual venv path configuration

**🔐 Tool-by-Tool Permission System:**
Claude Desktop will prompt you **individually for each tool** on first use. Since this MCP server has 15 tools, you may see up to 15 permission prompts if you use all features. For each tool, you can choose:
- **"Allow once"** - Approve this single tool use only
- **"Always allow"** - Approve all future uses of this specific tool automatically (recommended for regular use)

This tool-by-tool security feature ensures you maintain granular control over which external tools can be executed.

> **🛡️ Virus Scanner Note**: If you have virus checkers like Norton 360, don't worry if you get a "checking" message once for pip, pipx, uv, uvx, or python-alfresco-mcp-server.exe - this is normal security scanning behavior.

**Using the Tools:**

- **Chat naturally** about what you want to do with documents and search
- **Mention "Alfresco"** to ensure the MCP server is used (e.g., "In Alfresco...")
- **Use tool-related keywords** - mention something close to the tool name 
- **Follow-up prompts** will know the document from previous context

**Example 1: Document Management**

1. Upload a simple text document: "Please create a file called 'claude_test_doc-25 07 25 101 0 AM.txt' in the repository shared folder with this content: 'This is a test document created by Claude via MCP.' description 'Test document uploaded via Claude MCP'"
2. Update properties: "Set the description property of this document to 'my desc'"
3. Check out the document
4. Cancel checkout
5. Check out again  
6. Check in as a major version
7. Download the document
8. Upload a second document from "C:\1 sample files\cmispress.pdf"

> **Note**: Claude will figure out to use base64 encoding for the first upload on a second try

**Example 2: Search Operations**

"With Alfresco please test all 3 search methods and CMIS query:"
- Basic search for "txt" documents, return max 10
- Advanced search for documents created after 2024-01-01, return max 25
- Metadata search for documents where cm:title contains "test", limit to 50  
- CMIS search to find all txt documents, limit to 50

**More Examples: Create Folder, Browse Folders, Get Repository Info**

- "Create a folder called '25 07 25 01 18 am' in shared folder"
- "List docs and folders in shared folder" *(will use -shared-)*
- "Can you show me what's in my Alfresco home directory?" *(will use browse_repository -my-)*
- "Get info on Alfresco" *(will use repository_info tool)*

**Chat Box Buttons**

- Use **Search and tools button** (two horizontal lines with circles icon) in the chat box and choose "python-alfresco-mcp-server" - this allows you to enable/disable all tools or individual tools

- Click the **+ Button** → "Add from alfresco" for quick access to resources and prompts

**Search and Analyze Prompt:**
- Provides a form with query field for full-text search
- Analysis types: **summary**, **detailed**, **trends**, or **compliance**
- **Generates template text** to copy/paste into chat for editing

**Repository Info Resource (and Tool):**
- Provides status information in text format for viewing or copying

**Examples:**
- See [`prompts-for-claude.md`](./prompts-for-claude.md) for examples testing the tools


#### 🔍 **MCP Inspector** (Development/Testing)

> 📖 **Setup Guide**: Complete MCP Inspector setup and connection instructions in [MCP Inspector Setup Guide](./docs/mcp_inspector_setup.md)

**📥 Install MCP Inspector:**
- **Prerequisites**: Requires **Node.js 18+** - Download from **[nodejs.org](https://nodejs.org/)**
- **Install Command**: `npm install -g @modelcontextprotocol/inspector`
- **Or run directly**: `npx @modelcontextprotocol/inspector` (no global install needed)
- **Purpose**: Web-based tool for testing MCP servers and individual tools with custom parameters

**Working Method (Recommended):**

**1. Start MCP Server with HTTP transport:**

   ```bash
   # With UVX (recommended)
   uvx python-alfresco-mcp-server --transport http --port 8003

   # With UV (development)
   uv run python-alfresco-mcp-server --transport http --port 8003

   # Traditional methods - see Traditional Installation Guide
   ```

**2. Start MCP Inspector with config:**

   **UVX Installation (Recommended):**
   ```bash
   # Start with stdio transport
   npx @modelcontextprotocol/inspector --config mcp-inspector-stdio-uvx-config.json --server python-alfresco-mcp-server

   # Start with http transport  
   npx @modelcontextprotocol/inspector --config mcp-inspector-http-uvx-config.json --server python-alfresco-mcp-server
   ```

   **UV Installation (Development):**
   ```bash
   # From project directory where config files exist
   npx @modelcontextprotocol/inspector --config mcp-inspector-stdio-uv-config.json --server python-alfresco-mcp-server  # stdio transport
   npx @modelcontextprotocol/inspector --config mcp-inspector-http-uv-config.json --server python-alfresco-mcp-server   # http transport
   ```

   **Traditional Methods (pipx/pip):**

   See the **[Installation with pip and pipx](./docs/install_with_pip_pipx.md)** for pipx and pip configuration options.

**3. Open browser with pre-filled token:**

   - Use the URL provided in the output (includes authentication token)
   - Example: `http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=<token>`
   - This step applies to **all installation methods** (uv, uvx, pip, pipx)

This approach avoids proxy connection errors and provides direct authentication.


#### 🔧 **Other MCP Clients**

For Cursor, Claude Code, and other MCP clients:

📖 **Complete Setup Guide**: **[Client Configuration Guide](./docs/client_configurations.md)**


## 🛠️ Available Tools (15 Total)

### 🔍 Search Tools (4)
| Tool | Description | Parameters |
|------|-------------|------------|
| `search_content` | Search documents and folders | `query` (str), `max_results` (int), `node_type` (str) |
| `advanced_search` | Advanced search with filters | `query` (str), `content_type` (str), `created_after` (str), etc. |
| `search_by_metadata` | Search by metadata properties | `property_name` (str), `property_value` (str), `comparison` (str) |
| `cmis_search` | CMIS SQL queries | `cmis_query` (str), `preset` (str), `max_results` (int) |

### 🛠️ Core Tools (11)
| Tool | Description | Parameters |
|------|-------------|------------|
| `browse_repository` | Browse repository folders | `node_id` (str) |
| `repository_info` | Get repository information | None |
| `upload_document` | Upload new document | `filename` (str), `content_base64` (str), `parent_id` (str), `description` (str) |
| `download_document` | Download document content | `node_id` (str), `save_to_disk` (bool) |
| `create_folder` | Create new folder | `folder_name` (str), `parent_id` (str), `description` (str) |
| `get_node_properties` | Get node metadata | `node_id` (str) |
| `update_node_properties` | Update node metadata | `node_id` (str), `name` (str), `title` (str), `description` (str), `author` (str) |
| `delete_node` | Delete document/folder | `node_id` (str), `permanent` (bool) |
| `checkout_document` | Check out for editing | `node_id` (str), `download_for_editing` (bool) |
| `checkin_document` | Check in after editing | `node_id` (str), `comment` (str), `major_version` (bool), `file_path` (str) |
| `cancel_checkout` | Cancel checkout/unlock | `node_id` (str) |

📖 **See [API Reference](./docs/api_reference.md) for detailed tool documentation**

## 📊 Available Resources

### Repository Information
| Resource | Description | Access Method |
|----------|-------------|---------------|
| `repository_info` | Get comprehensive repository information including version, edition, license details, installed modules, and system status | Available as both MCP resource and tool |

The `repository_info` resource provides:
- **Repository Details**: ID, edition (Community/Enterprise), version information
- **License Information**: Issued/expires dates, remaining days, license holder, entitlements
- **System Status**: Read-only mode, audit enabled, quick share, thumbnail generation
- **Installed Modules**: Up to 10 modules with ID, title, version, and installation state

📖 **See [API Reference](./docs/api_reference.md) for detailed resource documentation**

## 🎯 Available Prompts

### Search and Analyze Prompt
| Prompt | Description | Parameters |
|--------|-------------|------------|
| `search_and_analyze` | Interactive form for guided content search and analysis | `query` (search terms), `analysis_type` (summary/detailed/trends/compliance) |

The Search and Analyze Prompt provides:
- **Interactive Form**: User-friendly interface with query input field
- **Analysis Options**: Choose from summary, detailed analysis, trends, or compliance reporting
- **Template Generation**: Creates copyable template text for chat conversations
- **Query Assistance**: Helps users structure effective search queries
- **Multiple Search Types**: Integrates with all 4 search tools (content, advanced, metadata, CMIS)

📖 **See [API Reference](./docs/api_reference.md) for detailed prompt documentation**

## 🔧 Configuration Options

| Environment Variable | Default | Description |
|---------------------|---------|-------------|
| `ALFRESCO_URL` | `http://localhost:8080` | Alfresco server URL |
| `ALFRESCO_USERNAME` | `admin` | Username for authentication |
| `ALFRESCO_PASSWORD` | `admin` | Password for authentication |
| `ALFRESCO_VERIFY_SSL` | `false` | Verify SSL certificates |
| `ALFRESCO_TIMEOUT` | `30` | Request timeout (seconds) |
| `FASTAPI_HOST` | `localhost` | FastAPI host |
| `FASTAPI_PORT` | `8000` | FastAPI port |
| `LOG_LEVEL` | `INFO` | Logging level |
| `MAX_FILE_SIZE` | `100000000` | Max upload size (bytes) |

⚙️ **See [Configuration Guide](./docs/configuration_guide.md) for deployment options**

## 🏗️ Architecture

```
┌─────────────────────────────────────────────────────┐
│                   MCP Clients                       │
│  Claude Desktop │ MCP Inspector │ Cursor │ Claude   │
│     Code │ n8n │ LangFlow │ Custom MCP Client App   │
└─────────────────┬───────────────────────────────────┘
                  │ stdio/HTTP/SSE
┌─────────────────▼───────────────────────────────────┐
│             FastMCP 2.0 MCP Server                  │
│  ┌─────────────┬─────────────┬─────────────────┐    │
│  │ MCP Tools   │ MCP         │ HTTP/SSE API    │    │
│  │ (15 total)  │ Resources   │                 │    │
│  │             │ MCP Prompts │                 │    │
│  └─────────────┴─────────────┴─────────────────┘    │
└─────────────────┬───────────────────────────────────┘
                  │ python-alfresco-api
┌─────────────────▼───────────────────────────────────┐
│            Alfresco Content Services                │
│         (Community/Enterprise Edition)              │
└─────────────────────────────────────────────────────┘
```

## 🧪 Testing & Quality

### Test Suite Overview
- **143 Total Tests**: **100% passed** - Coverage of all functionality
- **122 Unit Tests**: **100% passed** - Core functionality validated with mocking (FastMCP 2.0, tools, coverage)
- **21 Integration Tests**: **100% passed** - Live server testing (search, upload, download, document lifecycle)
- **Integration Tests**: Automated end-to-end testing covering all core document lifecycle scenarios
- **Performance Validated**: Search <1s, concurrent operations, resource access

### Coverage Report (Post-Cleanup)
- **Overall Coverage**: 51% (1,829 statements tested)
- **FastMCP 2.0 Core**: Well tested with comprehensive unit coverage
- **Configuration Module**: 93% coverage - Fully tested
- **Package Initialization**: 100% coverage (5/5 lines) - Complete
- **Overall Project**: 51% coverage of comprehensive codebase

### Run Tests

```bash
# Run full test suite
pytest

# Run with coverage report
pytest --cov=alfresco_mcp_server --cov-report=term-missing

# Run specific test categories
pytest -m "unit"           # Unit tests only
pytest -m "fastmcp"        # FastMCP 2.0 tests
pytest -m "integration"    # Integration tests (requires Alfresco)
```

🧪 **See [Testing Guide](./docs/testing_guide.md) for detailed testing strategies**

### 🧪 Test Categories and Execution

The project includes **4 levels of testing**:

1. **📋 Unit Tests** (122 tests) - Fast, mocked, isolated component testing
2. **🔗 Integration Tests** (21 tests) - Live Alfresco server testing  
3. **📝 Comprehensive Tests** - Automated core document lifecycle scenarios
4. **📊 Coverage Tests** - Edge cases and error path coverage



## 🧪 Development

### Setup Development Environment

```bash
git clone <repository>
cd python-alfresco-mcp-server

# UV handles everything automatically - no manual venv setup needed!
uv sync --extra dev        # Install with development tools
uv sync --extra test       # With testing tools
uv sync --extra all        # Everything

# Run immediately to test that installation worked
uv run python-alfresco-mcp-server --help

# Install python-alfresco-api for local development (if needed)
uv add --editable ../python-alfresco-api
```

**Traditional Development Setup:**

See the **[Installation with pip and pipx](./docs/install_with_pip_pipx.md)** for pip-based development setup.

## 💡 Examples

### Real-world implementation patterns from beginner to enterprise:

- **[💡 Examples Library](./examples/README.md)** - Complete navigation and learning paths
- **[🏃 Quick Start](./examples/quick_start.py)** - 5-minute introduction and basic operations
- **[📋 Document Lifecycle](./examples/document_lifecycle.py)** - Complete process demonstration
- **[🚀 Transport Examples](./examples/transport_examples.py)** - STDIO, HTTP, and SSE protocols
- **[⚡ Batch Operations](./examples/batch_operations.py)** - High-performance bulk processing
- **[🛡️ Error Handling](./examples/error_handling.py)** - Resilience patterns
- **[📊 Examples Summary](./examples/examples_summary.md)** - Overview and statistics

## 🤝 Contributing

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/new-feature`)
3. Commit your changes (`git commit -m 'Add new feature'`)
4. Push to the branch (`git push origin feature/new-feature`)
5. Open a Pull Request

## 📄 License

This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details.

## 🔗 Related Projects and References

- **[Hyland Alfresco](https://www.hyland.com/en/solutions/products/alfresco-platform)** - Content management platform (Enterprise and Community editions)
- **[python-alfresco-api](https://github.com/stevereiner/python-alfresco-api)** - The underlying Alfresco API library
- **[FastMCP 2.0](https://github.com/jlowin/FastMCP)** - Modern framework for building MCP servers
- **[FastMCP Documentation](https://gofastmcp.com/)** - Complete FastMCP framework documentation and guides
- **[Model Context Protocol](https://modelcontextprotocol.io)** - Official MCP specification and documentation
- **[Playbooks.com MCP List](https://playbooks.com/mcp/stevereiner-alfresco-content-services)** - Python Alfresco MCP Server listing
- **[PulseMCP.com MCP List](https://www.pulsemcp.com/servers/stevereiner-alfresco-content-services)** - Python Alfresco MCP Server listing
- **[Glama.ai MCP List](https://glama.ai/mcp/servers?query=alfresco)** - Glama Alfresco list including Python Alfresco MCP Server listing
- **[MCPMarket.com MCP List](https://mcpmarket.com/server/alfresco)** - Python Alfresco MCP Server listing

## 🙋‍♂️ Support

- 📚 **Documentation**: Complete guides in [`./docs/`](./docs/README.md)
- 💡 **Examples**: Implementation patterns in [`./examples/`](./examples/README.md)
- 🧪 **Testing**: Quality assurance in [`./docs/testing_guide.md`](./docs/testing_guide.md)
- 🔍 **MCP Inspector**: Development testing in [`./docs/mcp_inspector_setup.md`](./docs/mcp_inspector_setup.md)
- 🛠️ **Troubleshooting**: Problem solving in [`./docs/troubleshooting.md`](./docs/troubleshooting.md)
- 🐛 **Issues**: [GitHub Issues](https://github.com/stevereiner/python-alfresco-mcp-server/issues)

---

**🚀 MCP server built with [python-alfresco-api](https://github.com/stevereiner/python-alfresco-api) and [FastMCP 2.0](https://github.com/paulinephelan/FastMCP)**

```

--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------

```python

```

--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------

```json
{
    "svn.ignoreMissingSvnWarning": true
}
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/prompts/__init__.py:
--------------------------------------------------------------------------------

```python
# Prompts module

from . import search_and_analyze

__all__ = [
    "search_and_analyze",
] 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/resources/__init__.py:
--------------------------------------------------------------------------------

```python
# Resources module

from . import repository_resources

__all__ = [
    "repository_resources",
] 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/tools/__init__.py:
--------------------------------------------------------------------------------

```python
"""
Tools module for Alfresco MCP Server.
Contains core and search tools organized hierarchically.
"""

# Import subdirectories
from . import core
from . import search

__all__ = [
    "core",
    "search",
] 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/tools/search/__init__.py:
--------------------------------------------------------------------------------

```python
# Search tools module

from . import (
    advanced_search,
    cmis_search,
    search_by_metadata,
    search_content,
)

__all__ = [
    "advanced_search",
    "cmis_search", 
    "search_by_metadata",
    "search_content",
] 
```

--------------------------------------------------------------------------------
/mcp-inspector-stdio-pipx-config.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "python-alfresco-mcp-server": {
      "command": "python-alfresco-mcp-server",
      "args": ["--transport", "stdio"],
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin", 
        "ALFRESCO_PASSWORD": "admin"
      }
    }
  }
} 
```

--------------------------------------------------------------------------------
/config.yaml:
--------------------------------------------------------------------------------

```yaml
# Alfresco MCP Server Configuration
alfresco:
  base_url: "http://localhost:8080"  # Base URL only - API paths added by individual clients
  username: "admin"
  password: "admin"
  verify_ssl: false
  timeout: 30

# MCP Server Configuration  
server:
  name: "python-alfresco-mcp-server"
  version: "1.1.0"
```

--------------------------------------------------------------------------------
/mcp-inspector-http-pipx-config.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "python-alfresco-mcp-server": {
      "command": "python-alfresco-mcp-server", 
      "args": ["--transport", "http", "--port", "8003"],
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin"
      }
    }
  }
} 
```

--------------------------------------------------------------------------------
/claude-desktop-config-pipx-macos.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "python-alfresco-mcp-server": {
      "command": "python-alfresco-mcp-server",
      "args": [
        "--transport", 
        "stdio"
      ],
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin"
      }
    }
  }
} 
```

--------------------------------------------------------------------------------
/claude-desktop-config-uvx-macos.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "python-alfresco-mcp-server": {
      "command": "uvx",
      "args": [
        "python-alfresco-mcp-server",
        "--transport", 
        "stdio"
      ],
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin"
      }
    }
  }
} 
```

--------------------------------------------------------------------------------
/mcp-inspector-stdio-uvx-config.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "python-alfresco-mcp-server": {
      "command": "uvx",
      "args": [
        "python-alfresco-mcp-server",
        "--transport", 
        "stdio"
      ],
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin"
      }
    }
  }
} 
```

--------------------------------------------------------------------------------
/claude-desktop-config-pipx-windows.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "python-alfresco-mcp-server": {
      "command": "python-alfresco-mcp-server",
      "args": [
        "--transport", 
        "stdio"
      ],
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin",
        "PYTHONIOENCODING": "utf-8",
        "PYTHONLEGACYWINDOWSSTDIO": "1"
      }
    }
  }
} 
```

--------------------------------------------------------------------------------
/claude-desktop-config-uv-macos.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "python-alfresco-mcp-server": {
      "command": "uv",
      "args": [
        "run", 
        "python-alfresco-mcp-server",
        "--transport", 
        "stdio"
      ],
      "cwd": "/path/to/python-alfresco-mcp-server",
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin"
      }
    }
  }
} 
```

--------------------------------------------------------------------------------
/mcp-inspector-http-uvx-config.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "python-alfresco-mcp-server": {
      "command": "uvx",
      "args": [
        "python-alfresco-mcp-server",
        "--transport", 
        "http",
        "--host",
        "127.0.0.1",
        "--port",
        "8003"
      ],
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin"
      }
    }
  }
} 
```

--------------------------------------------------------------------------------
/claude-desktop-config-uvx-windows.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "python-alfresco-mcp-server": {
      "command": "uvx",
      "args": [
        "python-alfresco-mcp-server",
        "--transport", 
        "stdio"
      ],
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin",
        "PYTHONIOENCODING": "utf-8",
        "PYTHONLEGACYWINDOWSSTDIO": "1"
      }
    }
  }
} 
```

--------------------------------------------------------------------------------
/.vscode/mcp.json:
--------------------------------------------------------------------------------

```json
"servers": {
  "my-mcp-server": {
    "type": "http",
    "command": "python",
    "args": ["${workspaceFolder}/run_server.py", "--transport", "http", "--port", "8003", "--host", "127.0.0.1"],
    "dev": {
      "watch": "${workspaceFolder}/alfresco_mcp_server/**/*.py",
      "debug": { "type": "python" }
    },
    "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin"
      }
  }
}

```

--------------------------------------------------------------------------------
/mcp-inspector-stdio-uv-config.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "python-alfresco-mcp-server": {
      "command": "uv",
      "args": [
        "run", 
        "python-alfresco-mcp-server",
        "--transport", 
        "stdio"
      ],
      "cwd": "C:\\newdev3\\python-alfresco-mcp-server",
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin",
        "PYTHONIOENCODING": "utf-8",
        "PYTHONLEGACYWINDOWSSTDIO": "1"
      }
    }
  }
} 
```

--------------------------------------------------------------------------------
/claude-desktop-config-uv-windows.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "python-alfresco-mcp-server": {
      "command": "uv",
      "args": [
        "run", 
        "python-alfresco-mcp-server",
        "--transport", 
        "stdio"
      ],
      "cwd": "C:\\path\\to\\python-alfresco-mcp-server",
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin",
        "PYTHONIOENCODING": "utf-8",
        "PYTHONLEGACYWINDOWSSTDIO": "1"
      }
    }
  }
} 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/tools/core/__init__.py:
--------------------------------------------------------------------------------

```python
# Core tools module

from . import (
    browse_repository,
    cancel_checkout,
    checkin_document,
    checkout_document,
    create_folder,
    delete_node,
    download_document,
    get_node_properties,
    update_node_properties,
    upload_document,
)

__all__ = [
    "browse_repository",
    "cancel_checkout",
    "checkin_document", 
    "checkout_document",
    "create_folder",
    "delete_node",
    "download_document",
    "get_node_properties",
    "update_node_properties",
    "upload_document",
] 
```

--------------------------------------------------------------------------------
/mcp-inspector-http-uv-config.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "python-alfresco-mcp-server": {
      "command": "uv",
      "args": [
        "run", 
        "python-alfresco-mcp-server",
        "--transport", 
        "http",
        "--host",
        "127.0.0.1",
        "--port",
        "8003"
      ],
      "cwd": "C:\\newdev3\\python-alfresco-mcp-server",
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin",
        "PYTHONIOENCODING": "utf-8",
        "PYTHONLEGACYWINDOWSSTDIO": "1"
      }
    }
  }
} 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/__init__.py:
--------------------------------------------------------------------------------

```python
"""
MCP Server for Alfresco

Model Context Protocol server for Alfresco Content Services.
Provides AI-native access to Alfresco content management and search operations.
"""

__version__ = "1.1.0"
__title__ = "MCP Server for Alfresco"
__description__ = "Model Context Protocol server for Alfresco Content Services"

from .config import AlfrescoConfig, load_config

# Import subpackages to make them available
from . import tools
from . import resources  
from . import prompts
from . import utils

__all__ = [
    "AlfrescoConfig", 
    "load_config",
    "tools",
    "resources", 
    "prompts",
    "utils",
] 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/utils/__init__.py:
--------------------------------------------------------------------------------

```python
# Utilities module for Alfresco MCP Server

from .connection import (
    get_alfresco_config,
    get_connection,
    get_search_utils,
    get_node_utils,
)

from .file_type_analysis import (
    detect_file_extension_from_content,
    analyze_content_type,
)

from .json_utils import (
    make_json_safe,
    safe_format_output,
    escape_unicode_for_json,
)

__all__ = [
    # Connection utilities
    "get_alfresco_config",
    "get_connection", 
    "get_search_utils",
    "get_node_utils",
    # File type analysis
    "detect_file_extension_from_content",
    "analyze_content_type",
    # JSON utilities
    "make_json_safe",
    "safe_format_output",
    "escape_unicode_for_json",
] 
```

--------------------------------------------------------------------------------
/sample-dot-env.txt:
--------------------------------------------------------------------------------

```
# Alfresco MCP Server Configuration
# Copy this file to .env and customize for your environment
# The .env file will be ignored by git for security

# === REQUIRED: Alfresco Connection ===
ALFRESCO_URL=http://localhost:8080
ALFRESCO_USERNAME=admin
ALFRESCO_PASSWORD=admin

# === OPTIONAL: Connection Settings ===
ALFRESCO_VERIFY_SSL=false
ALFRESCO_TIMEOUT=30

# === OPTIONAL: Server Settings ===
LOG_LEVEL=INFO
MAX_FILE_SIZE=100000000

# === OPTIONAL: HTTP Transport Settings ===
FASTAPI_HOST=localhost
FASTAPI_PORT=8000

# === NOTES ===
# - Environment variables take precedence over defaults
# - python-alfresco-api may have its own configuration (check its docs)
# - For production, use environment variables or secure secret management
# - Boolean values: true/false (case insensitive)
# - File size in bytes (100000000 = 100MB) 
```

--------------------------------------------------------------------------------
/tests/mcp_specific/START_HTTP_SERVER.md:
--------------------------------------------------------------------------------

```markdown
# Starting MCP Server for Testing

## For MCP Inspector Testing (HTTP)

### Option 1: Using PowerShell
```powershell
cd C:\newdev3\python-alfresco-mcp-server
.\venv_clean\Scripts\activate
fastmcp run alfresco_mcp_server.fastmcp_server --host localhost --port 8003
```

### Option 2: Using Command Prompt
```cmd
cd C:\newdev3\python-alfresco-mcp-server
venv_clean\Scripts\activate.bat
fastmcp run alfresco_mcp_server.fastmcp_server --host localhost --port 8003
```

## For Claude Desktop Testing (STDIO)
Claude Desktop uses the stdio transport via the config file:
`claude-desktop-config.json`

## Testing Server Status
After starting the HTTP server, run:
```bash
python tests/mcp_specific/test_server_status.py
```

## MCP Inspector Setup
1. Start HTTP server on port 8003 (see above)
2. Open MCP Inspector in browser
3. Use URL: `http://localhost:8003`
4. **Note**: MCP Inspector will show warning about "risky auth" and include token in URL - this is normal for development testing

## Important Notes
- Make sure you're using `venv_clean` which has all the correct dependencies
- The FastMCP server will show import success messages when starting correctly
- If you see pydantic import errors, make sure you're using the correct virtual environment 
```

--------------------------------------------------------------------------------
/run_server.py:
--------------------------------------------------------------------------------

```python
#!/usr/bin/env python3
"""
Simple wrapper to run the Alfresco MCP Server
"""
import sys
import os

# Add the current directory to Python path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

# Import and run the server
from alfresco_mcp_server.fastmcp_server import mcp

if __name__ == "__main__":
    import argparse
    
    parser = argparse.ArgumentParser(description="Run Alfresco MCP Server")
    parser.add_argument("--port", type=int, default=8003, help="Port to run server on")
    parser.add_argument("--host", type=str, default="127.0.0.1", help="Host to bind to")
    parser.add_argument("--transport", type=str, default="http", choices=["stdio", "http", "sse"], help="Transport method to use")
    
    args = parser.parse_args()
    
    if args.transport == "stdio":
        print(">> Starting Alfresco MCP Server with stdio transport")
        mcp.run(transport="stdio")
    elif args.transport == "http":
        print(f">> Starting Alfresco MCP Server with HTTP transport on {args.host}:{args.port}")
        mcp.run(transport="http", host=args.host, port=args.port)
    else:
        print(f">> Starting Alfresco MCP Server with SSE transport on {args.host}:{args.port}")
        mcp.run(transport="sse", host=args.host, port=args.port) 
```

--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------

```
[tool:pytest]
# Pytest configuration for Alfresco MCP Server
minversion = 6.0
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*

# Add options
addopts = 
    --strict-markers
    --strict-config
    --verbose
    --tb=short
    --cov=alfresco_mcp_server
    --cov-report=html:htmlcov
    --cov-report=term-missing
    --cov-report=xml
    --cov-branch
    --cov-fail-under=85

# Test markers
markers =
    unit: marks tests as unit tests (fast, mocked dependencies)
    integration: marks tests as integration tests (requires live Alfresco)
    slow: marks tests as slow running
    performance: marks tests as performance benchmarks

# Async test configuration
asyncio_mode = auto

# Filter warnings
filterwarnings =
    ignore::DeprecationWarning
    ignore::PendingDeprecationWarning
    ignore::pytest.PytestUnraisableExceptionWarning

# Coverage configuration
[tool:coverage:run]
source = alfresco_mcp_server
omit = 
    tests/*
    venv/*
    */site-packages/*
    */test_*
    setup.py

[tool:coverage:report]
exclude_lines =
    pragma: no cover
    def __repr__
    if self.debug:
    if settings.DEBUG
    raise AssertionError
    raise NotImplementedError
    if 0:
    if __name__ == .__main__.:
    class .*\bProtocol\):
    @(abc\.)?abstractmethod

[tool:coverage:html]
directory = htmlcov

[tool:coverage:xml]
output = coverage.xml 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/prompts/search_and_analyze.py:
--------------------------------------------------------------------------------

```python
"""
Search and analyze prompt for Alfresco MCP Server.
Self-contained prompt for generating comprehensive search and analysis workflows.
"""


async def search_and_analyze_impl(query: str, analysis_type: str = "summary") -> str:
    """Generate comprehensive search and analysis prompts for Alfresco documents.
    
    Args:
        query: Search query for documents
        analysis_type: Type of analysis (summary, detailed, trends, compliance)
    
    Returns:
        Formatted prompt for document analysis workflow
    """
    base_prompt = f"""**Alfresco Document Analysis Request**

Please search for documents matching "{query}" and provide a {analysis_type} analysis.

**Step 1: Search**
Use the `search_content` tool to find relevant documents.

**Step 2: Analysis**
Based on the search results, provide:
"""
    
    if analysis_type == "summary":
        base_prompt += """
- Document count and types
- Key themes and topics
- Most relevant documents
- Quick insights
"""
    elif analysis_type == "detailed":
        base_prompt += """
- Comprehensive document inventory
- Metadata analysis (dates, authors, sizes)
- Content categorization
- Compliance status
- Recommended actions
- Related search suggestions
"""
    elif analysis_type == "trends":
        base_prompt += """
- Temporal patterns (creation/modification dates)
- Document lifecycle analysis
- Usage and access patterns
- Version history insights
- Storage optimization recommendations
"""
    elif analysis_type == "compliance":
        base_prompt += """
- Document retention analysis
- Security classification review
- Access permissions audit
- Regulatory compliance status
- Risk assessment
- Remediation recommendations
"""
    
    base_prompt += f"""
**Step 3: Recommendations**
Provide actionable insights and next steps based on the {analysis_type} analysis.
"""
    
    return base_prompt 
```

--------------------------------------------------------------------------------
/scripts/test.bat:
--------------------------------------------------------------------------------

```
@echo off
REM Windows batch script for running Alfresco MCP Server tests

echo ================================
echo  Alfresco MCP Server Test Suite
echo ================================

REM Check if Python is available
python --version >nul 2>&1
if %errorlevel% neq 0 (
    echo Error: Python is not installed or not in PATH
    exit /b 1
)

REM Set environment variables for Alfresco
set ALFRESCO_URL=http://localhost:8080
set ALFRESCO_USERNAME=admin
set ALFRESCO_PASSWORD=admin
set ALFRESCO_VERIFY_SSL=false

REM Parse command line arguments
set MODE=unit
set INSTALL_DEPS=false

:parse_args
if "%1"=="--help" goto :show_help
if "%1"=="--unit" set MODE=unit
if "%1"=="--integration" set MODE=integration
if "%1"=="--performance" set MODE=performance
if "%1"=="--coverage" set MODE=coverage
if "%1"=="--all" set MODE=all
if "%1"=="--lint" set MODE=lint
if "%1"=="--install-deps" set INSTALL_DEPS=true
shift
if not "%1"=="" goto :parse_args

REM Install dependencies if requested
if "%INSTALL_DEPS%"=="true" (
    echo Installing test dependencies...
    python -m pip install pytest pytest-asyncio pytest-cov pytest-mock coverage httpx
    if %errorlevel% neq 0 (
        echo Error: Failed to install dependencies
        exit /b 1
    )
)

REM Run the test runner
echo Running tests in %MODE% mode...
python scripts/run_tests.py --mode %MODE%

if %errorlevel% neq 0 (
    echo Tests failed!
    exit /b 1
)

echo.
echo ================================
echo  Tests completed successfully!
echo ================================
echo Coverage report: htmlcov/index.html
exit /b 0

:show_help
echo Usage: test.bat [OPTIONS]
echo.
echo Options:
echo   --unit          Run unit tests (default)
echo   --integration   Run integration tests (requires Alfresco)
echo   --performance   Run performance tests
echo   --coverage      Run coverage analysis
echo   --all           Run all tests
echo   --lint          Run code linting
echo   --install-deps  Install test dependencies
echo   --help          Show this help
exit /b 0 
```

--------------------------------------------------------------------------------
/tests/mcp_specific/MCP_INSPECTOR_CONNECTION.md:
--------------------------------------------------------------------------------

```markdown
# Connecting MCP Inspector to Your Running Server

## 🔍 Current Setup
- **MCP Inspector**: Running on port 6274 ✅
- **Your MCP Server**: Running on port 8003 ✅
- **URL Pattern**: `http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=...#tools`

## 🔗 How to Connect

### Step 1: MCP Inspector Server Connection
In your MCP Inspector interface:

1. **Look for "Add Server" or "Connect Server"** button
2. **Enter your server URL**: `http://localhost:8003`
3. **Transport**: Select "HTTP" 
4. **No additional auth needed** - it's local

### Step 2: Expected URL Structure
Once connected, your URL will be:
```
http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=<token>&server=http://localhost:8003#tools
```

### Step 3: What You Should See
- ✅ **Server**: "Alfresco Document Management Server"
- ✅ **15 Tools Available**:
  - search_content
  - search_by_metadata
  - advanced_search
  - cmis_search
  - browse_repository
  - repository_info
  - upload_document
  - download_document
  - checkout_document
  - checkin_document
  - cancel_checkout
  - delete_node
  - get_node_properties
  - update_node_properties
  - create_folder

## 🚨 If Connection Fails

### Check Server Status
```bash
python tests/mcp_specific/test_server_status.py
```

### Check Both Ports
```bash
netstat -an | findstr ":6274\|:8003"
```

### Restart If Needed
```bash
# Stop your MCP server
taskkill /PID 57420 /F

# Restart with explicit logging
fastmcp run alfresco_mcp_server/fastmcp_server.py --transport http --host localhost --port 8003 --log-level DEBUG
```

## 🎯 Quick Test
1. **Open your MCP Inspector**: `http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=4b4a857ba6eecd2ac7029bf0c9daaf97e311b7f5cee35dec3a25ddf739ffd66b#tools`
2. **Add server**: `http://localhost:8003`
3. **Test a tool**: Try "browse_repository" or "search_content"

## 🔒 The "Risky Auth" Token
The `MCP_PROXY_AUTH_TOKEN=4b4a857ba6eecd2ac7029bf0c9daaf97e311b7f5cee35dec3a25ddf739ffd66b` is:
- ✅ **Normal**: Generated by MCP Inspector for security
- ✅ **Local only**: Not transmitted anywhere
- ✅ **Safe**: Standard MCP development practice 
```

--------------------------------------------------------------------------------
/tests/mcp_specific/START_MCP_INSPECTOR.md:
--------------------------------------------------------------------------------

```markdown
# How to Start MCP Inspector

## 🚀 Quick Start Methods

### Method 1: Using Config File (Recommended - Avoids Proxy Errors)
```bash
# 1. Start your MCP server first
python -m alfresco_mcp_server.fastmcp_server --transport http --port 8003

# 2. Start MCP Inspector with pre-configured server
npx @modelcontextprotocol/inspector --config mcp-inspector-http-config.json --server python-alfresco-mcp-server
```

**Expected Output:**
```
Starting MCP inspector...
⚙️ Proxy server listening on 127.0.0.1:6277
🔑 Session token: d7a62ab6e032eefe5d85e807c50e13b9fffcd12badbf8bbc3377659c0be4fa8d
Use this token to authenticate requests or set DANGEROUSLY_OMIT_AUTH=true to disable auth

🔗 Open inspector with token pre-filled:
   http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=d7a62ab6e032eefe5d85e807c50e13b9fffcd12badbf8bbc3377659c0be4fa8d
```

### Method 2: Using npx (Basic)
```bash
npx @modelcontextprotocol/inspector
```

### Method 2: Using npm (if globally installed)
```bash
npm run dev
# or
npm start
```

### Method 3: Direct GitHub (if you have it locally)
```bash
# If you have the repo cloned
cd path/to/mcp-inspector
npm run dev
```

### Method 4: Using the pre-built package
```bash
npx @modelcontextprotocol/inspector@latest
```

## 📍 Expected Output
When MCP Inspector starts, you should see:
```
> Local:   http://localhost:6274
> Network: http://192.168.x.x:6274
```

## 🔗 After Starting

### 1. Open Browser
Navigate to: `http://localhost:6274`

### 2. Connect to Your Server
- **Click "Add Server"** or server connection field
- **Enter**: `http://localhost:8003`
- **Transport**: HTTP
- **Connect**

### 3. Expected URL
```
http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=<new-token>&server=http://localhost:8003#tools
```

## 🛠️ Troubleshooting

### If Port 6274 is Busy
```bash
# MCP Inspector will auto-find next available port
# Check what port it actually uses in the startup message
```

### If npx Fails
```bash
# Update npm/npx
npm install -g npm@latest

# Try with explicit version
npx @modelcontextprotocol/inspector@latest
```

### Check if Already Running
```bash
netstat -an | findstr :6274
# or check other common ports
netstat -an | findstr ":3000\|:5173\|:6274"
```

## 🎯 Quick Test Command
```bash
# This should start MCP Inspector on port 6274
npx @modelcontextprotocol/inspector
```

Then open `http://localhost:6274` and connect to your server at `http://localhost:8003`! 
```

--------------------------------------------------------------------------------
/tests/mcp_specific/TESTING_INSTRUCTIONS.md:
--------------------------------------------------------------------------------

```markdown
# MCP Server Testing Instructions

## 🟢 Server Status: RUNNING
- **Process**: Python PID 57420 ✅
- **Port**: 8003 LISTENING ✅  
- **URL**: `http://localhost:8003/mcp/` ✅

## 🌐 MCP Inspector Testing

### Why Browser Shows Error
The error `"Not Acceptable: Client must accept text/event-stream"` is **NORMAL**:
- MCP servers use Server-Sent Events (SSE) format
- Regular browsers can't handle MCP protocol
- You need the MCP Inspector tool

### Setup MCP Inspector
1. **Download**: [MCP Inspector](https://github.com/modelcontextprotocol/inspector)
2. **Install**: Follow their setup instructions
3. **Connect to**: `http://localhost:8003`
4. **No token required** - it's local development

### Expected in MCP Inspector
- ✅ Server connection successful
- ✅ 10 tools available (search_content, browse_repository, etc.)
- ✅ "Risky auth" warning - NORMAL for development
- ✅ Can test individual tools

## 💬 Claude Desktop Testing (Easier)

### Setup
1. **Config ready**: `claude-desktop-config.json` ✅
2. **Restart**: Claude Desktop application
3. **Ready**: MCP tools appear in conversation

### Your Config File
```json
{
  "mcpServers": {
    "alfresco": {
      "command": "C:\\newdev3\\python-alfresco-mcp-server\\venv_clean\\Scripts\\python.exe",
      "args": [
        "C:\\newdev3\\python-alfresco-mcp-server\\alfresco_mcp_server\\fastmcp_server.py"
      ],
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin", 
        "ALFRESCO_PASSWORD": "admin"
      }
    }
  }
}
```

### Testing in Claude
Ask Claude to:
- Search for content: "Search for documents about 'project'"
- Browse repository: "Show me the repository structure"
- Get node info: "Get details about the root folder"

## 🔒 Security Notes

### LOCAL ONLY - No Central Registration
- ✅ Server runs on `localhost:8003` only
- ✅ Not accessible from internet
- ✅ Not registered in any central directory
- ✅ Private development server

### If You're Concerned About Name
The server name "Alfresco Document Management Server" is just a display name:
- ✅ Not registered anywhere
- ✅ Only visible to connected clients
- ✅ Can be changed in code if desired

## 🧪 Quick Test Commands

### Test Server Status
```bash
python tests/mcp_specific/test_server_status.py
```

### Test Server Tools
```bash
python tests/mcp_specific/test_server_fixed.py
```

### Stop Server
```bash
# Find and kill process 57420
taskkill /PID 57420 /F
```

## 🎯 Recommended Testing Order
1. **Start with Claude Desktop** (easiest to test)
2. **Then try MCP Inspector** (if you want detailed tool testing)
3. **Both use the same server** - no conflicts 
```

--------------------------------------------------------------------------------
/tests/mcp_specific/test_http_server.ps1:
--------------------------------------------------------------------------------

```
# Test script for Python Alfresco MCP Server HTTP transport
Write-Host "🌐 Testing Python Alfresco MCP Server - HTTP Transport" -ForegroundColor Green
Write-Host "=" * 60

$baseUrl = "http://127.0.0.1:8001"

# Wait for server to start
Write-Host "⏳ Waiting for server to start..." -ForegroundColor Yellow
Start-Sleep -Seconds 3

try {
    # Test 1: Server health/info
    Write-Host "`n🏥 Testing Server Health..." -ForegroundColor Cyan
    try {
        $response = Invoke-WebRequest -Uri "$baseUrl/" -Method GET -TimeoutSec 10
        Write-Host "✅ Server is responding" -ForegroundColor Green
        Write-Host "   Status: $($response.StatusCode)" -ForegroundColor Gray
    }
    catch {
        Write-Host "ℹ️  Root endpoint: $($_.Exception.Message)" -ForegroundColor Yellow
    }

    # Test 2: List tools (if available)
    Write-Host "`n🔧 Testing Tools Endpoint..." -ForegroundColor Cyan
    try {
        $response = Invoke-WebRequest -Uri "$baseUrl/tools" -Method GET -TimeoutSec 10
        Write-Host "✅ Tools endpoint accessible" -ForegroundColor Green
        $content = $response.Content | ConvertFrom-Json
        Write-Host "   Tools found: $($content.tools.Count)" -ForegroundColor Gray
    }
    catch {
        Write-Host "ℹ️  Tools endpoint: $($_.Exception.Message)" -ForegroundColor Yellow
    }

    # Test 3: List resources (if available)
    Write-Host "`n📦 Testing Resources Endpoint..." -ForegroundColor Cyan
    try {
        $response = Invoke-WebRequest -Uri "$baseUrl/resources" -Method GET -TimeoutSec 10
        Write-Host "✅ Resources endpoint accessible" -ForegroundColor Green
    }
    catch {
        Write-Host "ℹ️  Resources endpoint: $($_.Exception.Message)" -ForegroundColor Yellow
    }

    # Test 4: Call a tool (if supported)
    Write-Host "`n🔍 Testing Tool Call..." -ForegroundColor Cyan
    try {
        $body = @{
            query = "test"
            max_results = 5
        } | ConvertTo-Json

        $response = Invoke-WebRequest -Uri "$baseUrl/tools/search_content" -Method POST -Body $body -ContentType "application/json" -TimeoutSec 10
        Write-Host "✅ Tool call successful" -ForegroundColor Green
        Write-Host "   Response length: $($response.Content.Length) chars" -ForegroundColor Gray
    }
    catch {
        Write-Host "ℹ️  Tool call: $($_.Exception.Message)" -ForegroundColor Yellow
    }

    Write-Host "`n🎉 HTTP testing completed!" -ForegroundColor Green

}
catch {
    Write-Host "❌ HTTP testing failed: $($_.Exception.Message)" -ForegroundColor Red
}

Write-Host "`n📖 Next steps:" -ForegroundColor Cyan
Write-Host "1. Try MCP Inspector: npx @modelcontextprotocol/inspector" -ForegroundColor Gray
Write-Host "2. Test STDIO transport: python test_with_mcp_client.py" -ForegroundColor Gray
Write-Host "3. Run existing test suite: python -m pytest tests/" -ForegroundColor Gray 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/tools/core/delete_node.py:
--------------------------------------------------------------------------------

```python
"""
Delete node tool for Alfresco MCP Server.
Self-contained tool for deleting documents or folders from Alfresco repository.
"""
import logging
from typing import Optional
from fastmcp import Context

from ...utils.connection import ensure_connection
from ...utils.json_utils import safe_format_output

logger = logging.getLogger(__name__)


async def delete_node_impl(
    node_id: str, 
    permanent: bool = False,
    ctx: Optional[Context] = None
) -> str:
    """Delete a document or folder from Alfresco.
    
    Args:
        node_id: Node ID to delete
        permanent: Whether to permanently delete (bypass trash)
        ctx: MCP context for progress reporting
    
    Returns:
        Deletion confirmation
    """
    if ctx:
        delete_type = "permanently delete" if permanent else "move to trash"
        await ctx.info(f"Preparing to {delete_type}: {node_id}")
        await ctx.info("Validating deletion request...")
        await ctx.report_progress(0.1)
    
    if not node_id.strip():
        return safe_format_output("❌ Error: node_id is required")
    
    try:
        await ensure_connection()
        from ...utils.connection import get_client_factory
        
        # Get client factory and create core client (working pattern from test)
        client_factory = await get_client_factory()
        core_client = client_factory.create_core_client()
        
        # Clean the node ID (remove any URL encoding or extra characters)
        clean_node_id = node_id.strip()
        if clean_node_id.startswith('alfresco://'):
            # Extract node ID from URI format
            clean_node_id = clean_node_id.split('/')[-1]
        
        logger.info(f"Attempting to delete node: {clean_node_id}")
        
        if ctx:
            await ctx.report_progress(0.7)
        
        # Get node information first to validate it exists (working pattern from test)
        node_response = core_client.nodes.get(clean_node_id)
        
        if not hasattr(node_response, 'entry'):
            return safe_format_output(f"❌ Failed to get node information for: {clean_node_id}")
        
        node_info = node_response.entry
        filename = getattr(node_info, 'name', f"document_{clean_node_id}")
        
        # Use the working high-level API pattern from test script
        core_client.nodes.delete(clean_node_id)
        
        status = "permanently deleted" if permanent else "moved to trash"
        logger.info(f"✅ Node {status}: {filename}")
        
        if ctx:
            await ctx.report_progress(1.0)
        return safe_format_output(f"""✅ **Deletion Complete**

📄 **Node**: {node_info.name}
🗑️ **Status**: {status.title()}
{"⚠️ **WARNING**: This action cannot be undone" if permanent else "ℹ️ **INFO**: Can be restored from trash"}

🆔 **Node ID**: {clean_node_id}""")
        
    except Exception as e:
        error_msg = f"ERROR: Deletion failed: {str(e)}"
        if ctx:
            await ctx.error(error_msg)
        logger.error(f"Deletion failed: {e}")
        return error_msg 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/config.py:
--------------------------------------------------------------------------------

```python
"""
Configuration management for MCP Server for Alfresco.
"""

import os
from typing import Optional
from pydantic import BaseModel, Field


class AlfrescoConfig(BaseModel):
    """Configuration for MCP Server for Alfresco."""
    
    # Alfresco server connection
    alfresco_url: str = Field(
        default_factory=lambda: os.getenv("ALFRESCO_URL", "http://localhost:8080"),
        description="Alfresco server URL"
    )
    
    # Authentication
    username: str = Field(
        default_factory=lambda: os.getenv("ALFRESCO_USERNAME", "admin"),
        description="Alfresco username"
    )
    
    password: str = Field(
        default_factory=lambda: os.getenv("ALFRESCO_PASSWORD", "admin"),
        description="Alfresco password"
    )
    
    # Connection settings
    verify_ssl: bool = Field(
        default_factory=lambda: os.getenv("ALFRESCO_VERIFY_SSL", "false").lower() == "true",
        description="Verify SSL certificates"
    )
    
    timeout: int = Field(
        default_factory=lambda: int(os.getenv("ALFRESCO_TIMEOUT", "30")),
        description="Request timeout in seconds"
    )
    
    # MCP Server settings
    server_name: str = Field(
        default="python-alfresco-mcp-server",
        description="MCP server name"
    )
    
    server_version: str = Field(
        default="1.0.0",
        description="MCP server version"
    )
    
    # FastAPI settings (for HTTP transport)
    fastapi_host: str = Field(
        default_factory=lambda: os.getenv("FASTAPI_HOST", "localhost"),
        description="FastAPI host"
    )
    
    fastapi_port: int = Field(
        default_factory=lambda: int(os.getenv("FASTAPI_PORT", "8000")),
        description="FastAPI port"
    )
    
    fastapi_prefix: str = Field(
        default_factory=lambda: os.getenv("FASTAPI_PREFIX", "/mcp"),
        description="FastAPI URL prefix"
    )
    
    # Logging
    log_level: str = Field(
        default_factory=lambda: os.getenv("LOG_LEVEL", "INFO"),
        description="Logging level"
    )
    
    # Content settings
    max_file_size: int = Field(
        default_factory=lambda: int(os.getenv("MAX_FILE_SIZE", "100000000")),  # 100MB
        description="Maximum file size for uploads in bytes"
    )
    
    allowed_extensions: list[str] = Field(
        default_factory=lambda: [
            ".txt", ".pdf", ".doc", ".docx", ".xls", ".xlsx", 
            ".ppt", ".pptx", ".jpg", ".jpeg", ".png", ".gif", 
            ".zip", ".xml", ".json", ".csv"
        ],
        description="Allowed file extensions for uploads"
    )
    
    class Config:
        env_prefix = "ALFRESCO_"
        case_sensitive = False
        
    def model_post_init(self, __context) -> None:
        """Normalize URLs after initialization."""
        if self.alfresco_url.endswith("/"):
            self.alfresco_url = self.alfresco_url.rstrip("/")


def load_config() -> AlfrescoConfig:
    """Load configuration from environment variables and defaults."""
    return AlfrescoConfig()

# Global config instance for import
config = load_config() 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/tools/core/create_folder.py:
--------------------------------------------------------------------------------

```python
"""
Create folder tool for Alfresco MCP Server.
Self-contained tool for creating folders in Alfresco repository.
"""
import logging
from typing import Optional
from fastmcp import Context

from ...utils.connection import ensure_connection
from ...utils.json_utils import safe_format_output

logger = logging.getLogger(__name__)


async def create_folder_impl(
    folder_name: str, 
    parent_id: str = "-shared-", 
    description: str = "",
    ctx: Optional[Context] = None
) -> str:
    """Create a new folder in Alfresco.
    
    Args:
        folder_name: Name of the new folder
        parent_id: Parent folder ID (default: shared folder)
        description: Folder description
        ctx: MCP context for progress reporting
    
    Returns:
        Folder creation confirmation with details
    """
    if ctx:
        await ctx.info(f">> Creating folder '{folder_name}' in {parent_id}")
        await ctx.info("Validating folder parameters...")
        await ctx.report_progress(0.0)
    
    if not folder_name.strip():
        return safe_format_output("❌ Error: folder_name is required")
    
    try:
        # Ensure connection and get client factory (working pattern from test)
        await ensure_connection()
        from ...utils.connection import get_client_factory
        
        # Get client factory and create core client (working pattern from test)
        client_factory = await get_client_factory()
        core_client = client_factory.create_core_client()
        
        if ctx:
            await ctx.info("Creating folder in Alfresco...")
            await ctx.report_progress(0.5)
        
        logger.info(f"Creating folder '{folder_name}' in parent {parent_id}")
        
        # Prepare properties
        properties = {"cm:title": folder_name}
        if description:
            properties["cm:description"] = description
        
        logger.info(f"Using high-level API: core_client.nodes.create_folder()")
        
        # Use the working high-level API pattern from test script
        folder_response = core_client.nodes.create_folder(
            name=folder_name,
            parent_id=parent_id,
            properties=properties
        )
        
        if folder_response and hasattr(folder_response, 'entry'):
            entry = folder_response.entry
            logger.info("✅ Folder created successfully")
            
            # Extract folder details from response
            folder_id = getattr(entry, 'id', 'Unknown')
            folder_name_response = getattr(entry, 'name', folder_name)
            created_at = getattr(entry, 'createdAt', 'Unknown')
            node_type = getattr(entry, 'nodeType', 'cm:folder')
        else:
            raise Exception(f"Failed to create folder - invalid response from core client")
        
        if ctx:
            await ctx.info("Processing folder creation response...")
            await ctx.report_progress(0.9)
        
        if ctx:
            await ctx.info("Folder created!")
            await ctx.report_progress(1.0)
            await ctx.info(f"SUCCESS: Folder '{folder_name_response}' created successfully")
            
        # Clean JSON-friendly formatting (no markdown syntax)
        return safe_format_output(f"""✅ Folder Created Successfully!

📁 Name: {folder_name_response}
🆔 Folder ID: {folder_id}
📍 Parent: {parent_id}
📅 Created: {created_at}
🏷️ Type: {node_type}
📝 Description: {description or 'None'}""")
        
    except Exception as e:
        error_msg = f"❌ Folder creation failed: {str(e)}"
        if ctx:
            await ctx.error(error_msg)
        logger.error(f"Folder creation failed: {e}")
        return safe_format_output(error_msg) 
```

--------------------------------------------------------------------------------
/tests/mcp_specific/test_with_mcp_inspector.md:
--------------------------------------------------------------------------------

```markdown
# Testing with MCP Inspector

## Step 1: Start MCP Inspector

```bash
# Launch MCP Inspector
npx @modelcontextprotocol/inspector
```

This will open a web interface (usually at http://localhost:3000) where you can interactively test your MCP server.

## Step 2: Configure Server Connection

In the MCP Inspector interface:

1. **Server Type**: Select "stdio"
2. **Command**: Enter `python`
3. **Arguments**: Add these as separate entries:
   - `-m`
   - `alfresco_mcp_server.fastmcp_server`
4. **Environment Variables** (if needed):
   - `ALFRESCO_URL`: `http://localhost:8080`
   - `ALFRESCO_USERNAME`: `admin`
   - `ALFRESCO_PASSWORD`: `admin`
   - `ALFRESCO_VERIFY_SSL`: `false`

## Step 3: Connect and Explore

### Available Tools (15 total):
1. **search_content** - Search documents and folders
2. **search_by_metadata** - Search by metadata properties
3. **advanced_search** - Advanced search with filters
4. **cmis_search** - CMIS SQL-based search
5. **upload_document** - Upload new documents
6. **download_document** - Download document content
7. **browse_repository** - Browse repository structure
8. **repository_info** - Get repository information and status
9. **checkout_document** - Check out for editing
10. **checkin_document** - Check in after editing
11. **cancel_checkout** - Cancel document checkout
12. **delete_node** - Delete documents/folders
13. **get_node_properties** - Get node metadata
14. **update_node_properties** - Update node metadata
15. **create_folder** - Create new folders

### Available Resources (5 total):
1. **alfresco://repository/info** - Repository information
2. **alfresco://repository/health** - Health status
3. **alfresco://repository/stats** - Usage statistics
4. **alfresco://repository/config** - Configuration details
5. **alfresco://repository/{section}** - Dynamic repository info

### Available Prompts (1 total):
1. **search_and_analyze** - AI-friendly search template

## Step 4: Test Examples

### Quick Tests (No Alfresco Required):
- List tools: Should show all 15 tools
- List resources: Should show all 5 resources
- List prompts: Should show search_and_analyze prompt

### With Live Alfresco Server:
1. **Test Search**: 
   - Tool: `search_content`
   - Parameters: `{"query": "test", "max_results": 5}`

2. **Test Repository Info**:
   - Resource: `alfresco://repository/info`

3. **Test Create Folder**:
   - Tool: `create_folder`
   - Parameters: `{"folder_name": "MCP Test Folder", "description": "Created via MCP Inspector"}`

## Step 5: Advanced Testing

### Error Handling:
- Try invalid parameters
- Test without Alfresco connection
- Test with wrong credentials

### Performance:
- Large search queries
- Multiple concurrent operations
- File upload/download operations

## Troubleshooting

### Common Issues:
1. **Inspector won't start**: Check Node.js version, try `npm install -g @modelcontextprotocol/inspector`
2. **Server connection fails**: Verify Python path and module installation
3. **Alfresco errors**: Check server status, credentials, and network connectivity
4. **Tool execution fails**: Verify parameters match schema requirements

### Environment Setup:
```bash
# Windows PowerShell
$env:ALFRESCO_URL="http://localhost:8080"
$env:ALFRESCO_USERNAME="admin"
$env:ALFRESCO_PASSWORD="admin"
$env:ALFRESCO_VERIFY_SSL="false"

# Or use .env file (recommended)
# Copy sample-dot-env.txt to .env and modify
```

## Next Steps

1. **Start simple**: Test tool/resource listing first
2. **Add credentials**: Set up environment variables for Alfresco
3. **Test incrementally**: One tool at a time
4. **Explore features**: Try different parameters and combinations
5. **Production testing**: Test with your actual Alfresco deployment 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/utils/connection.py:
--------------------------------------------------------------------------------

```python
"""
Connection utilities for Alfresco MCP Server.
Handles client creation and connection management.
"""
import logging
import os
from typing import Optional


logger = logging.getLogger(__name__)

# Global connection cache
_master_client = None
_client_factory = None

def get_alfresco_config() -> dict:
    """Get Alfresco configuration from environment variables."""
    return {
        'alfresco_url': os.getenv('ALFRESCO_URL', 'http://localhost:8080'),
        'username': os.getenv('ALFRESCO_USERNAME', 'admin'),
        'password': os.getenv('ALFRESCO_PASSWORD', 'admin'),
        'verify_ssl': os.getenv('ALFRESCO_VERIFY_SSL', 'false').lower() == 'true',
        'timeout': int(os.getenv('ALFRESCO_TIMEOUT', '30'))
    }


async def ensure_connection():
    """Ensure we have a working connection to Alfresco using python-alfresco-api."""
    global _master_client, _client_factory
    
    if _master_client is None:
        try:
            # Import here to avoid circular imports
            from python_alfresco_api import ClientFactory
            
            config = get_alfresco_config()
            
            logger.info(">> Creating Alfresco clients...")
            
            # Use ClientFactory to create authenticated client (original Sunday pattern)
            factory = ClientFactory(
                base_url=config['alfresco_url'],
                username=config['username'],
                password=config['password'],
                verify_ssl=config['verify_ssl'],
                timeout=config['timeout']
            )
            
            # Store the factory globally for other functions to use
            _client_factory = factory
            
            _master_client = factory.create_master_client()
            logger.info("Master client created successfully")
                        
            # Test connection - use method that initializes and gets
            try:
                # Use ensure_httpx_client to initialize, then test simple call
                _master_client.core.ensure_httpx_client()
                logger.info("Connection test successful!")
            except Exception as conn_error:
                logger.warning(f"Connection test failed: {conn_error}")
            
        except Exception as e:
            logger.error(f"ERROR: Failed to create clients: {str(e)}")
            raise e
    
    return _master_client


def get_connection():
    """Get the cached connection without async (for sync operations)."""
    return _master_client


async def get_search_client():
    """Get the search client for search operations (using master_client for auth compatibility)."""
    master_client = await ensure_connection()
    # Return master_client which has simple_search access and working authentication
    return master_client


async def get_core_client():
    """Get the core client for core operations (using master_client for auth compatibility)."""
    master_client = await ensure_connection()
    # Return the actual core client that has nodes, folders, etc.
    return master_client.core


async def get_client_factory():
    """Get the client factory for advanced operations."""
    await ensure_connection()
    if not _client_factory:
        raise RuntimeError("Connection not initialized. Call ensure_connection() first.")
    return _client_factory


def get_search_utils():
    """Get the search_utils module from python-alfresco-api."""
    try:
        from python_alfresco_api.utils import search_utils
        return search_utils
    except ImportError as e:
        logger.error(f"Failed to import search_utils: {e}")
        raise


def get_node_utils():
    """Get the node_utils module from python-alfresco-api."""
    try:
        from python_alfresco_api.utils import node_utils
        return node_utils
    except ImportError as e:
        logger.error(f"Failed to import node_utils: {e}")
        raise
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/utils/json_utils.py:
--------------------------------------------------------------------------------

```python
"""
JSON utilities for Alfresco MCP Server.
Handles proper Unicode emoji encoding for MCP protocol transport.
"""
import json
import logging


logger = logging.getLogger(__name__)


def make_json_safe(text: str) -> str:
    """
    Make text JSON-safe for MCP protocol transport.
    Properly encodes Unicode emojis to prevent character map errors.
    
    Args:
        text: Input text that may contain Unicode emojis
        
    Returns:
        JSON-safe text with properly encoded Unicode characters
    """
    if not text:
        return text
        
    try:
        # Ensure proper Unicode normalization
        import unicodedata
        normalized = unicodedata.normalize('NFC', text)
        
        # Test if it can be safely JSON serialized
        json.dumps(normalized)
        return normalized
        
    except (UnicodeError, TypeError) as e:
        logger.warning(f"Unicode encoding issue, falling back to ASCII: {e}")
        # Fall back to ASCII-safe version with emoji descriptions
        return text.encode('ascii', errors='ignore').decode('ascii')


def safe_format_output(text: str) -> str:
    """
    Format output text to be safe for MCP JSON transport.
    Replaces emojis with text equivalents to prevent character map errors.
    
    Args:
        text: Text to format
        
    Returns:
        Safely formatted text with emojis replaced
    """
    if not text:
        return text
        
    try:
        # Define emoji replacements for common ones used in the tools
        emoji_replacements = {
            '🔗': '[LINK]',
            '🔓': '[UNLOCKED]', 
            '📄': '[DOCUMENT]',
            '🆔': '[ID]',
            '📏': '[SIZE]',
            '💾': '[SAVED]',
            '🔒': '[LOCKED]',
            '🕒': '[TIME]',
            '📥': '[DOWNLOAD]',
            'ℹ️': '[INFO]',
            '⚠️': '[WARNING]',
            '👤': '[USER]',
            '✅': '[SUCCESS]',
            '❌': '[ERROR]',
            '🏷️': '[TAG]',
            '🧩': '[MODULE]',
            '📁': '[FOLDER]',
            '📍': '[LOCATION]',
            '📅': '[DATE]',
            '📝': '[NOTE]',
            '🔢': '[VERSION]',
            '📊': '[SIZE]',
            '🗑️': '[DELETE]',
            '🔍': '[SEARCH]',
            '📤': '[UPLOAD]',
            '🧹': '[CLEANUP]',
            '🏢': '[REPOSITORY]',
            '🔧': '[TOOL]',
            '📦': '[PACKAGE]'
        }
        
        # Replace emojis with text equivalents
        safe_text = text
        for emoji, replacement in emoji_replacements.items():
            safe_text = safe_text.replace(emoji, replacement)
        
        # Test if the result is JSON-safe
        test_json = json.dumps(safe_text, ensure_ascii=True)
        json.loads(test_json)
        
        return safe_text
        
    except Exception as e:
        logger.warning(f"JSON formatting issue: {e}")
        try:
            # Ultimate fallback: remove all non-ASCII characters
            ascii_text = text.encode('ascii', errors='ignore').decode('ascii')
            return ascii_text
        except Exception as fallback_error:
            logger.error(f"ASCII fallback failed: {fallback_error}")
            return "Error: Text encoding failed"


def escape_unicode_for_json(text: str) -> str:
    """
    Alternative approach: explicitly escape Unicode characters for JSON.
    Use this if the regular approach doesn't work.
    
    Args:
        text: Input text with Unicode characters
        
    Returns:
        Text with Unicode characters escaped for JSON
    """
    if not text:
        return text
        
    try:
        # Use json.dumps to properly escape Unicode, then remove the quotes
        escaped = json.dumps(text, ensure_ascii=False)
        # Remove the surrounding quotes added by json.dumps
        if escaped.startswith('"') and escaped.endswith('"'):
            escaped = escaped[1:-1]
        return escaped
        
    except Exception as e:
        logger.warning(f"Unicode escaping failed: {e}")
        return text 
```

--------------------------------------------------------------------------------
/docs/install_with_pip_pipx.md:
--------------------------------------------------------------------------------

```markdown
# Installation with pip and pipx

This guide covers traditional Python package installation methods (pip and pipx) for Python Alfresco MCP Server. For modern UV/UVX installation (recommended), see the [main README](../README.md).

## 🛠️ Installation Options

### Option 1: pipx (Recommended for Traditional Methods)

pipx automatically creates isolated environments for each tool while making commands globally available - eliminates dependency conflicts while providing system-wide access.

```bash
# First install pipx if you don't have it (one-time setup)
pip install pipx

# Install python-alfresco-mcp-server in isolated environment
pipx install python-alfresco-mcp-server

# Test that installation worked
python-alfresco-mcp-server --help
```

**Why pipx?** pipx automatically creates isolated environments for each tool while making commands globally available - eliminates dependency conflicts while providing system-wide access.

### Option 2: pip (Traditional Package Manager)

```bash
# Recommended: Create virtual environment first
python -m venv venv
source venv/bin/activate     # Linux/macOS
# venv\Scripts\activate      # Windows

# Install python-alfresco-mcp-server
pip install python-alfresco-mcp-server

# Test that installation worked
python-alfresco-mcp-server --help
```

## 🚀 Usage

### MCP Server Startup

**With pipx (Global installation - no venv needed):**

```bash
# Run MCP server with STDIO transport (default)
python-alfresco-mcp-server

# HTTP transport for web services (matches MCP Inspector)
python-alfresco-mcp-server --transport http --host 127.0.0.1 --port 8003

# SSE transport for real-time streaming  
python-alfresco-mcp-server --transport sse --host 127.0.0.1 --port 8001
```

**With pip (Activate venv first if installed in one):**

```bash
# Activate virtual environment first (if used during installation)
source venv/bin/activate     # Linux/macOS
# venv\Scripts\activate      # Windows

# Run MCP server with STDIO transport (default)
python-alfresco-mcp-server

# HTTP transport for web services (matches MCP Inspector)
python-alfresco-mcp-server --transport http --host 127.0.0.1 --port 8003

# SSE transport for real-time streaming  
python-alfresco-mcp-server --transport sse --host 127.0.0.1 --port 8001
```

## 🔧 Claude Desktop Configuration

The Claude Desktop configuration differs based on how you installed the MCP server:

### pipx Installation (Global Tool)

```json
{
  "command": "python-alfresco-mcp-server",
  "args": ["--transport", "stdio"]
}
```

- Uses the **global command name** directly (no path needed)
- pipx makes tools globally available in your PATH
- Simplest configuration

**Sample Config Files:**
- Windows: [`claude-desktop-config-pipx-windows.json`](../claude-desktop-config-pipx-windows.json)
- macOS: [`claude-desktop-config-pipx-macos.json`](../claude-desktop-config-pipx-macos.json)

### pip Installation (Manual venv)

```json
{
  "command": "C:\\path\\to\\venv\\Scripts\\python-alfresco-mcp-server.exe",
  "args": ["--transport", "stdio"]
}
```

- Uses **direct path to executable** in your virtual environment
- Path points to `Scripts/` directory in your venv (Windows) or `bin/` (Linux/macOS)
- Replace `C:\\path\\to\\venv` with your actual venv location

## 🔍 MCP Inspector Configuration

For development and testing with MCP Inspector:

### pipx Installation

Use the sample config files:
- **stdio transport**: [`mcp-inspector-stdio-pipx-config.json`](../mcp-inspector-stdio-pipx-config.json)
- **http transport**: [`mcp-inspector-http-pipx-config.json`](../mcp-inspector-http-pipx-config.json)

```bash
# Start with stdio transport
npx @modelcontextprotocol/inspector --config mcp-inspector-stdio-pipx-config.json --server python-alfresco-mcp-server

# Start with http transport  
npx @modelcontextprotocol/inspector --config mcp-inspector-http-pipx-config.json --server python-alfresco-mcp-server
```

### pip Installation

Copy one of the pipx sample files above and modify the `"command"` field to point to your venv executable:
- Change `"python-alfresco-mcp-server"` to `"C:\\path\\to\\venv\\Scripts\\python-alfresco-mcp-server.exe"` (Windows)
- Or `"/path/to/venv/bin/python-alfresco-mcp-server"` (Linux/macOS) 
```

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

```toml
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "python-alfresco-mcp-server"
version = "1.1.0"
description = "FastMCP 2.0 server for Alfresco Content Services integration"
authors = [{name = "Steve Reiner", email = "[email protected]"}]
license = {text = "Apache-2.0"}
readme = "README.md"
requires-python = ">=3.10"
keywords = ["alfresco", "mcp", "content-management", "fastmcp", "ai"]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: Apache Software License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Content Management System",
    "Topic :: Internet :: WWW/HTTP :: Indexing/Search",
    "Topic :: Database :: Database Engines/Servers",
    "Topic :: Scientific/Engineering :: Artificial Intelligence",
]

dependencies = [
    # FastMCP 2.0 - Modern MCP framework with enhanced features
    "fastmcp>=2.9.0",
    
    # Alfresco integration
    "python-alfresco-api>=1.1.1",
    
    # Configuration and utilities
    "pydantic>=2.0.0",
    "pydantic-settings>=2.0.0",
    "PyYAML>=6.0",
    
    # HTTP client
    "httpx>=0.24.0",
    
    # File handling
    "python-multipart>=0.0.6",
]

[project.urls]
Homepage = "https://github.com/stevereiner/python-alfresco-mcp-server"
Repository = "https://github.com/stevereiner/python-alfresco-mcp-server"
Issues = "https://github.com/stevereiner/python-alfresco-mcp-server/issues"
Documentation = "https://github.com/stevereiner/python-alfresco-mcp-server#readme"

[project.optional-dependencies]
dev = [
    "black>=23.0.0",
    "ruff>=0.1.0",
    "mypy>=1.0.0",
]
test = [
    "pytest>=7.0.0",
    "pytest-asyncio>=0.21.0",
    "pytest-cov>=4.0.0",
    "pytest-xdist>=3.0.0",
    "pytest-mock>=3.10.0",
    "coverage[toml]>=7.0.0",
    "httpx>=0.24.0",
]
all = [
    "python-alfresco-mcp-server[dev]",
    "python-alfresco-mcp-server[test]",
]

[project.scripts]
python-alfresco-mcp-server = "alfresco_mcp_server.fastmcp_server:main"

[tool.setuptools]
include-package-data = true

[tool.setuptools.packages.find]
where = ["."]
include = ["alfresco_mcp_server*"]
exclude = ["tests*", "tests-debug*", "venv*", "*.egg-info*"]

[tool.setuptools.package-data]
alfresco_mcp_server = [
    "*.py",
    "*.yaml", 
    "*.yml",
    "*.json",
    "*.md",
    "tools/**/*.py",
    "resources/**/*.py", 
    "prompts/**/*.py",
    "utils/**/*.py",
]

[tool.pytest.ini_options]
minversion = "7.0"
addopts = [
    "-ra",
    "-v",
    "--tb=short",
    "--strict-markers",
    "--disable-warnings",
]
testpaths = ["tests"]
filterwarnings = [
    "ignore::DeprecationWarning",
    "ignore::PendingDeprecationWarning",
]
markers = [
    "asyncio: marks tests as async",
    "integration: marks tests as integration tests requiring live Alfresco server",
    "unit: marks tests as unit tests",
    "fastmcp: marks tests as FastMCP 2.0 specific tests",
]
asyncio_mode = "auto"

[tool.coverage.run]
source = ["alfresco_mcp_server"]
omit = [
    "*/tests/*",
    "*/venv/*",
    "*/__pycache__/*",
]

[tool.coverage.report]
exclude_lines = [
    "pragma: no cover",
    "def __repr__",
    "if self.debug:",
    "if settings.DEBUG",
    "raise AssertionError",
    "raise NotImplementedError",
    "if 0:",
    "if __name__ == .__main__.:",
    "class .*\\bProtocol\\):",
    "@(abc\\.)?abstractmethod",
]

[tool.black]
line-length = 88
target-version = ['py310']
include = '\.pyi?$'
extend-exclude = '''
/(
  # directories
  \.eggs
  | \.git
  | \.hg
  | \.mypy_cache
  | \.tox
  | \.venv
  | venv
  | build
  | dist
)/
'''

[tool.ruff]
target-version = "py310"
line-length = 88
select = [
    "E",  # pycodestyle errors
    "W",  # pycodestyle warnings
    "F",  # pyflakes
    "I",  # isort
    "B",  # flake8-bugbear
    "C4", # flake8-comprehensions
    "UP", # pyupgrade
]
ignore = [
    "E501",  # line too long, handled by black
    "B008",  # do not perform function calls in argument defaults
    "C901",  # too complex
]

[tool.ruff.per-file-ignores]
"__init__.py" = ["F401"]
"tests/**/*" = ["B018", "B019"]

[tool.mypy]
python_version = "3.10"
check_untyped_defs = true
disallow_any_generics = true
disallow_incomplete_defs = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
strict_equality = true

[dependency-groups]
dev = [
    "build>=1.2.2.post1",
    "twine>=6.1.0",
]

```

--------------------------------------------------------------------------------
/examples/quick_start.py:
--------------------------------------------------------------------------------

```python
#!/usr/bin/env python3
"""
Quick Start Example for Alfresco MCP Server

This example demonstrates:
- Basic server connection
- Making your first tool call
- Handling responses
- Simple error handling

Prerequisites:
- Alfresco MCP Server running
- Environment variables set (ALFRESCO_URL, ALFRESCO_USERNAME, ALFRESCO_PASSWORD)
"""

import asyncio
import os
from fastmcp import Client
from alfresco_mcp_server.fastmcp_server import mcp


async def quick_start_example():
    """Quick start example showing basic MCP server usage."""
    
    print("🚀 Alfresco MCP Server - Quick Start Example")
    print("=" * 50)
    
    # Check environment setup
    print("\n📋 Checking Environment Setup...")
    required_vars = ["ALFRESCO_URL", "ALFRESCO_USERNAME", "ALFRESCO_PASSWORD"]
    for var in required_vars:
        value = os.getenv(var)
        if value:
            print(f"✅ {var}: {value}")
        else:
            print(f"❌ {var}: Not set (using defaults)")
    
    try:
        # Connect to the MCP server
        print("\n🔌 Connecting to MCP Server...")
        async with Client(mcp) as client:
            print("✅ Connected successfully!")
            
            # List available tools
            print("\n🛠️ Available Tools:")
            tools = await client.list_tools()
            for i, tool in enumerate(tools, 1):
                print(f"  {i:2d}. {tool.name} - {tool.description}")
            
            # List available resources
            print("\n📚 Available Resources:")
            resources = await client.list_resources()
            for i, resource in enumerate(resources, 1):
                print(f"  {i:2d}. {resource.uri}")
            
            # List available prompts
            print("\n💭 Available Prompts:")
            prompts = await client.list_prompts()
            for i, prompt in enumerate(prompts, 1):
                print(f"  {i:2d}. {prompt.name} - {prompt.description}")
            
            # Example 1: Simple search
            print("\n🔍 Example 1: Simple Document Search")
            print("-" * 40)
            search_result = await client.call_tool("search_content", {
                "query": "*",  # Search for all documents
                "max_results": 5
            })
            
            if search_result:
                print("Search Result:")
                print(search_result[0].text)
            
            # Example 2: Get repository info
            print("\n📊 Example 2: Repository Information")
            print("-" * 40)
            repo_info = await client.read_resource("alfresco://repository/info")
            if repo_info:
                print("Repository Info:")
                print(repo_info[0].text)
            
            # Example 3: Create a test folder
            print("\n📁 Example 3: Create Test Folder")
            print("-" * 40)
            folder_result = await client.call_tool("create_folder", {
                "folder_name": f"MCP_Test_Folder_{asyncio.current_task().get_name()}",
                "parent_id": "-root-",
                "description": "Test folder created by MCP Quick Start example"
            })
            
            if folder_result:
                print("Folder Creation Result:")
                print(folder_result[0].text)
            
            # Example 4: Get analysis prompt
            print("\n💡 Example 4: Analysis Prompt")
            print("-" * 40)
            prompt_result = await client.get_prompt("search_and_analyze", {
                "query": "financial reports",
                "analysis_type": "summary"
            })
            
            if prompt_result.messages:
                print("Generated Prompt:")
                print(prompt_result.messages[0].content.text[:300] + "...")
            
            print("\n✅ Quick Start Complete!")
            print("Next steps:")
            print("- Explore other examples in this directory")
            print("- Check the documentation in ../docs/")
            print("- Try the document lifecycle example")
            
    except Exception as e:
        print(f"\n❌ Error: {e}")
        print("\nTroubleshooting:")
        print("1. Ensure Alfresco server is running")
        print("2. Check environment variables")
        print("3. Verify network connectivity")
        return False
    
    return True


def main():
    """Main function to run the quick start example."""
    print("Starting Alfresco MCP Server Quick Start Example...")
    
    # Run the async example
    success = asyncio.run(quick_start_example())
    
    if success:
        print("\n🎉 Example completed successfully!")
    else:
        print("\n💥 Example failed. Please check the error messages above.")


if __name__ == "__main__":
    main() 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/tools/core/update_node_properties.py:
--------------------------------------------------------------------------------

```python
"""
Update node properties tool for Alfresco MCP Server.
Self-contained tool for updating document/folder metadata and properties.
"""
import logging
import os
from typing import Optional
from fastmcp import Context

from ...utils.connection import ensure_connection, get_core_client

logger = logging.getLogger(__name__)


async def update_node_properties_impl(
    node_id: str,
    name: str = "",
    title: str = "",
    description: str = "",
    author: str = "",
    ctx: Optional[Context] = None
) -> str:
    """Update metadata and properties of a document or folder.
    
    Args:
        node_id: Node ID to update (required)
        name: New name for the node (optional)
        title: Document title (cm:title) (optional)
        description: Document description (cm:description) (optional)
        author: Document author (cm:author) (optional)
        ctx: MCP context for progress reporting
    
    Returns:
        Update confirmation with changes made
    """
    if ctx:
        await ctx.info(f"Updating properties for: {node_id}")
        await ctx.report_progress(0.1)
    
    if not node_id.strip():
        return "ERROR: node_id is required"
    
    if not any([name, title, description, author]):
        return "ERROR: At least one property (name, title, description, or author) must be provided"
    
    try:
        # Ensure connection and get core client
        await ensure_connection()
        core_client = await get_core_client()
        
        # Clean the node ID (remove any URL encoding or extra characters)
        clean_node_id = node_id.strip()
        if clean_node_id.startswith('alfresco://'):
            # Extract node ID from URI format
            clean_node_id = clean_node_id.split('/')[-1]
        
        logger.info(f"Updating properties for node: {clean_node_id}")
        
        if ctx:
            await ctx.report_progress(0.3)
        
        # Get node information first to validate it exists
        try:
            node_response = core_client.nodes.get(node_id=clean_node_id)
            if not hasattr(node_response, 'entry'):
                return f"ERROR: Failed to get node information for: {clean_node_id}"
            
            node_info = node_response.entry
            current_name = getattr(node_info, 'name', f"document_{clean_node_id}")
            
        except Exception as get_error:
            return f"ERROR: Failed to validate node {clean_node_id}: {str(get_error)}"
        
        if ctx:
            await ctx.report_progress(0.5)
        
        # Prepare updates for actual API call
        properties_updates = {}
        if title and title.strip():
            properties_updates['cm:title'] = title.strip()
        if description and description.strip():
            properties_updates['cm:description'] = description.strip()
        if author and author.strip():
            properties_updates['cm:author'] = author.strip()
        
        # Import the UpdateNodeRequest model
        try:
            from python_alfresco_api.clients.core.nodes.models import UpdateNodeRequest
        except ImportError:
            return "ERROR: Failed to import UpdateNodeRequest model"
        
        # Prepare update request
        update_request = UpdateNodeRequest()
        
        if name and name.strip():
            update_request.name = name.strip()
        if properties_updates:
            update_request.properties = properties_updates
            
        if ctx:
            await ctx.report_progress(0.7)
        
        # Use the core client's update method
        try:
            updated_node = core_client.nodes.update(
                node_id=clean_node_id,
                request=update_request
            )
            logger.info("Node properties updated successfully")
            
        except Exception as update_error:
            logger.error(f"Update failed: {update_error}")
            return f"ERROR: Update failed: {str(update_error)}"

        if ctx:
            await ctx.report_progress(1.0)
        
        changes = []
        if name:
            changes.append(f"- Name: {name}")
        if title:
            changes.append(f"- Title: {title}")
        if description:
            changes.append(f"- Description: {description}")
        if author:
            changes.append(f"- Author: {author}")
        
        updated_properties = "\n".join(changes)
        
        # Clean JSON-friendly formatting (no markdown syntax)
        return f"Node Updated Successfully\n\nNode ID: {clean_node_id}\nUpdated Properties:\n{updated_properties}\nUpdate completed successfully"
        
    except Exception as e:
        error_msg = f"ERROR: Update failed: {str(e)}"
        if ctx:
            await ctx.error(error_msg)
        logger.error(f"Update failed: {e}")
        return error_msg 
```

--------------------------------------------------------------------------------
/docs/client_configurations.md:
--------------------------------------------------------------------------------

```markdown
# MCP Client Configuration Guide

This guide covers configuration for various MCP clients beyond Claude Desktop. For Claude Desktop configuration, see the [Claude Desktop Setup Guide](./claude_desktop_setup.md).

## 🔷 Cursor Configuration

Cursor is a VS Code fork with AI capabilities and MCP support.

> 📖 **Complete Setup Guide**: Detailed Cursor configuration instructions available at [playbooks.com](https://playbooks.com/mcp/stevereiner-alfresco-content-services#cursor-setup)

### For Users (PyPI Installation)

If you installed the package via PyPI with pipx:

```json
{
    "mcpServers": {
        "python-alfresco-mcp-server": {
            "command": "python-alfresco-mcp-server",
            "args": ["--transport", "stdio"],
            "env": {
                "ALFRESCO_URL": "http://localhost:8080",
                "ALFRESCO_USERNAME": "admin",
                "ALFRESCO_PASSWORD": "admin"
            }
        }
    }
}
```

### For Developers (Source Installation)

If you're using the source code with UV:

```json
{
    "mcpServers": {
        "python-alfresco-mcp-server": {
            "command": "uv",
            "args": ["run", "python-alfresco-mcp-server"],
            "cwd": "/path/to/python-alfresco-mcp-server",
            "env": {
                "ALFRESCO_URL": "http://localhost:8080",
                "ALFRESCO_USERNAME": "admin",
                "ALFRESCO_PASSWORD": "admin"
            }
        }
    }
}
```

## ⚡ Claude Code Configuration

Claude Code is Anthropic's VS Code extension with MCP support.

> 📖 **Complete Setup Guide**: Claude Code configuration instructions at [playbooks.com](https://playbooks.com/mcp/stevereiner-alfresco-content-services#claude-code-setup)

### For Users (PyPI Installation)

```bash
claude mcp add-json "python-alfresco-mcp-server" '{
  "command": "python-alfresco-mcp-server",
  "args": ["--transport", "stdio"],
  "env": {
    "ALFRESCO_URL": "http://localhost:8080",
    "ALFRESCO_USERNAME": "admin",
    "ALFRESCO_PASSWORD": "admin"
  }
}'
```

### For Developers (Source Installation)

```bash
claude mcp add-json "python-alfresco-mcp-server" '{
  "command": "uv",
  "args": ["run", "python-alfresco-mcp-server"],
  "cwd": "/path/to/python-alfresco-mcp-server",
  "env": {
    "ALFRESCO_URL": "http://localhost:8080",
    "ALFRESCO_USERNAME": "admin",
    "ALFRESCO_PASSWORD": "admin"
  }
}'
```

## 🔧 Other MCP Clients

For any MCP-compatible client, use these connection parameters based on your installation method:

### PyPI Installation (Users)

- **Command**: `python-alfresco-mcp-server` (assumes pipx installation)
- **Args**: `["--transport", "stdio"]`
- **Transport Options**: 
  - STDIO (default) - Direct MCP protocol
  - HTTP (add `--port 8001`) - RESTful API  
  - SSE (add `--port 8003`) - Server-Sent Events

### Source Installation (Developers)

- **Command**: `uv`
- **Args**: `["run", "python-alfresco-mcp-server"]`
- **Working Directory**: Path to cloned repository
- **Transport Options**: Same as above

### Traditional Python Installation

If using traditional pip in a virtual environment:

- **Command**: `/path/to/venv/bin/python-alfresco-mcp-server` (full path to executable)
- **Args**: `["--transport", "stdio"]`
- **Transport Options**: Same as above

## 🔧 Environment Variables

All clients need these environment variables configured:

| Variable | Default | Description |
|----------|---------|-------------|
| `ALFRESCO_URL` | `http://localhost:8080` | Alfresco server URL |
| `ALFRESCO_USERNAME` | `admin` | Username for authentication |
| `ALFRESCO_PASSWORD` | `admin` | Password for authentication |
| `ALFRESCO_VERIFY_SSL` | `false` | Verify SSL certificates |
| `ALFRESCO_TIMEOUT` | `30` | Request timeout (seconds) |

### Windows-Specific Variables (if needed)

For Windows systems experiencing character encoding issues:

```json
"env": {
  "ALFRESCO_URL": "http://localhost:8080",
  "ALFRESCO_USERNAME": "admin",
  "ALFRESCO_PASSWORD": "admin",
  "PYTHONIOENCODING": "utf-8",
  "PYTHONLEGACYWINDOWSSTDIO": "1"
}
```

## 🚀 Transport Options

The MCP server supports three transport protocols:

### STDIO (Default)
- **Fastest** and most efficient
- Direct MCP protocol communication
- Recommended for most use cases
- Args: `["--transport", "stdio"]` (optional, it's the default)

### HTTP 
- RESTful API interface
- Useful for web services and testing
- Args: `["--transport", "http", "--port", "8001"]`

### SSE (Server-Sent Events)
- Real-time streaming updates
- Good for live monitoring
- Args: `["--transport", "sse", "--port", "8003"]`

## 🧪 Testing Your Configuration

After setting up your MCP client:

1. **Start Your Client**: Launch your MCP-enabled application
2. **Check Connection**: Look for "python-alfresco-mcp-server" in connected servers
3. **Test Basic Functionality**:
   - Try the `repository_info` tool to verify connection
   - Run a simple `search_content` query
   - Check that all 15 tools are available

## 🛠️ Troubleshooting

### Common Issues

1. **Command Not Found**
   - Ensure the package is installed correctly
   - For pipx: Run `pipx list` to verify installation
   - For source: Ensure UV is installed and working directory is correct

2. **Connection Failures**
   - Check Alfresco server is running
   - Verify environment variables are set correctly
   - Test connection with `curl http://localhost:8080/alfresco`

3. **Permission Errors**
   - Verify Alfresco username/password
   - Check that user has appropriate permissions
   - Try with admin credentials first

4. **Character Encoding (Windows)**
   - Add Windows-specific environment variables
   - Ensure UTF-8 encoding is configured

### Getting Help

- 📚 **Documentation**: Complete guides in [`../docs/`](./README.md)
- 🛠️ **Troubleshooting**: [Troubleshooting Guide](./troubleshooting.md)
- 🐛 **Issues**: [GitHub Issues](https://github.com/stevereiner/python-alfresco-mcp-server/issues)

## ⚠️ Security Notes

- **Never commit configuration files** with real credentials to version control
- **Use environment variables** for production deployments
- **Consider using .env files** for local development (they're ignored by git)
- **Use strong passwords** for production Alfresco servers 
```

--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------

```python
"""
Pytest configuration and shared fixtures for Alfresco MCP Server tests.
Provides configuration for unit tests, integration tests, and code coverage.
"""
import pytest
import pytest_asyncio
import asyncio
import os
import httpx
import tempfile
import shutil
from typing import Generator, AsyncGenerator
from unittest.mock import MagicMock, AsyncMock, patch

# Test markers
pytest_plugins = ["pytest_asyncio"]

def pytest_configure(config):
    """Configure pytest markers."""
    config.addinivalue_line(
        "markers", "unit: marks tests as unit tests (fast, mocked)"
    )
    config.addinivalue_line(
        "markers", "integration: marks tests as integration tests (requires live Alfresco)"
    )
    config.addinivalue_line(
        "markers", "slow: marks tests as slow running"
    )
    config.addinivalue_line(
        "markers", "performance: marks tests as performance benchmarks"
    )


@pytest.fixture(scope="session")
def event_loop():
    """Create an instance of the default event loop for the test session."""
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()


@pytest.fixture
def temp_dir() -> Generator[str, None, None]:
    """Create a temporary directory for test files."""
    with tempfile.TemporaryDirectory() as temp_dir:
        yield temp_dir


@pytest.fixture
def mock_alfresco_factory():
    """Mock Alfresco client factory for unit tests."""
    factory = MagicMock()
    
    # Mock search client
    search_client = AsyncMock()
    search_client.search = AsyncMock()
    factory.create_search_client.return_value = search_client
    
    # Mock core client  
    core_client = AsyncMock()
    core_client.get_node = AsyncMock()
    core_client.create_node = AsyncMock()
    core_client.update_node = AsyncMock()
    core_client.delete_node = AsyncMock()
    core_client.get_node_content = AsyncMock()
    factory.create_core_client.return_value = core_client
    
    return factory


@pytest.fixture
def mock_auth_util():
    """Mock authentication utility for unit tests."""
    auth_util = AsyncMock()
    auth_util.ensure_authenticated = AsyncMock()
    auth_util.get_auth_headers = AsyncMock(return_value={"Authorization": "Bearer mock-token"})
    auth_util.is_authenticated.return_value = True
    return auth_util


@pytest_asyncio.fixture
async def fastmcp_client():
    """FastMCP in-memory client for testing."""
    from fastmcp import Client
    from alfresco_mcp_server.fastmcp_server import mcp
    
    async with Client(mcp) as client:
        yield client


@pytest.fixture
async def http_client() -> AsyncGenerator[httpx.AsyncClient, None]:
    """HTTP client for integration tests."""
    async with httpx.AsyncClient(timeout=30.0) as client:
        yield client


@pytest.fixture
def alfresco_config():
    """Alfresco configuration for tests."""
    return {
        "url": os.getenv("ALFRESCO_URL", "http://localhost:8080"),
        "username": os.getenv("ALFRESCO_USERNAME", "admin"),
        "password": os.getenv("ALFRESCO_PASSWORD", "admin"),
        "verify_ssl": os.getenv("ALFRESCO_VERIFY_SSL", "false").lower() == "true"
    }


@pytest.fixture
def sample_documents():
    """Sample document data for testing."""
    import base64
    
    return {
        "text_doc": {
            "filename": "test_document.txt",
            "content": "This is a test document for MCP server testing.",
            "content_base64": base64.b64encode(
                "This is a test document for MCP server testing.".encode()
            ).decode(),
            "mime_type": "text/plain"
        },
        "json_doc": {
            "filename": "test_data.json",
            "content": '{"test": "data", "numbers": [1, 2, 3]}',
            "content_base64": base64.b64encode(
                '{"test": "data", "numbers": [1, 2, 3]}'.encode()
            ).decode(),
            "mime_type": "application/json"
        }
    }


@pytest.fixture
def mock_search_results():
    """Mock search results for testing."""
    from types import SimpleNamespace
    
    def create_mock_entry(name, node_id, is_folder=False):
        entry = SimpleNamespace()
        entry.entry = SimpleNamespace()
        entry.entry.name = name
        entry.entry.id = node_id
        entry.entry.isFolder = is_folder
        entry.entry.modifiedAt = "2024-01-15T10:30:00Z"
        entry.entry.createdByUser = {"displayName": "Test User"}
        entry.entry.content = {"sizeInBytes": 1024} if not is_folder else None
        entry.entry.path = {"name": "/Shared/Test"}
        return entry
    
    results = SimpleNamespace()
    results.list = SimpleNamespace()
    results.list.entries = [
        create_mock_entry("Test Document 1.pdf", "doc-123", False),
        create_mock_entry("Test Folder", "folder-456", True),
        create_mock_entry("Test Document 2.txt", "doc-789", False)
    ]
    
    return results


def pytest_collection_modifyitems(config, items):
    """Modify test collection to handle markers."""
    if config.getoption("--integration"):
        # Only run integration tests
        skip_unit = pytest.mark.skip(reason="Integration test run - skipping unit tests")
        for item in items:
            if "integration" not in item.keywords:
                item.add_marker(skip_unit)
    else:
        # Skip integration tests by default
        skip_integration = pytest.mark.skip(reason="Integration tests require --integration flag")
        for item in items:
            if "integration" in item.keywords:
                item.add_marker(skip_integration)


def pytest_addoption(parser):
    """Add custom command line options."""
    parser.addoption(
        "--integration",
        action="store_true",
        default=False,
        help="Run integration tests (requires live Alfresco server)"
    )
    parser.addoption(
        "--performance",
        action="store_true", 
        default=False,
        help="Run performance benchmarks"
    )


@pytest.fixture
def check_alfresco_available(alfresco_config):
    """Check if Alfresco server is available for integration tests."""
    def _check():
        try:
            response = httpx.get(
                f"{alfresco_config['url']}/alfresco/api/-default-/public/alfresco/versions/1/probes/-ready-",
                timeout=5.0
            )
            return response.status_code == 200
        except Exception:
            return False
    
    return _check 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/tools/core/cancel_checkout.py:
--------------------------------------------------------------------------------

```python
"""
Cancel checkout tool implementation for Alfresco MCP Server.
Handles canceling document checkout with cleanup and unlock management.
"""
import logging
import pathlib
import json
from datetime import datetime
from fastmcp import Context

from ...utils.connection import get_core_client
from ...utils.json_utils import safe_format_output

logger = logging.getLogger(__name__)


async def cancel_checkout_impl(
    node_id: str,
    ctx: Context = None
) -> str:
    """Cancel checkout of a document, discarding any working copy.
    
    Args:
        node_id: Original node ID that was checked out
        ctx: MCP context for progress reporting
    
    Returns:
        Cancellation confirmation and cleanup status
    """
    if ctx:
        await ctx.info(f"Cancelling checkout for: {node_id}")
        await ctx.report_progress(0.1)
    
    if not node_id.strip():
        return safe_format_output("❌ Error: node_id is required")
    
    try:
        logger.info(f"Starting cancel checkout: node {node_id}")
        core_client = await get_core_client()
        
        # Clean the node ID
        clean_node_id = node_id.strip()
        if clean_node_id.startswith('alfresco://'):
            clean_node_id = clean_node_id.split('/')[-1]
        
        if ctx:
            await ctx.info("Checking node status...")
            await ctx.report_progress(0.3)
        
        # Get node information to validate using high-level core client
        node_response = core_client.nodes.get(node_id=clean_node_id)
        
        if not hasattr(node_response, 'entry'):
            return f"ERROR: Failed to get node information for: {clean_node_id}"
        
        node_info = node_response.entry
        filename = getattr(node_info, 'name', f"document_{clean_node_id}")
        
        if ctx:
            await ctx.info(">> Performing Alfresco unlock using high-level client...")
            await ctx.report_progress(0.5)
        
        # Use high-level core client unlock method
        try:
            logger.info(f"Attempting to unlock document: {clean_node_id}")
            unlock_response = core_client.versions.cancel_checkout(node_id=clean_node_id)
            if unlock_response and hasattr(unlock_response, 'entry'):
                api_status = "✅ Document unlocked in Alfresco"
            else:
                api_status = "✅ Document unlocked in Alfresco"
            logger.info(f"Document unlocked successfully: {clean_node_id}")
        except Exception as unlock_error:
            error_str = str(unlock_error)
            if "404" in error_str:
                # Document might not be locked
                api_status = "ℹ️ Document was not locked in Alfresco"
                logger.info(f"Document was not locked: {clean_node_id}")
            elif "405" in error_str:
                # Server doesn't support lock/unlock APIs
                api_status = "WARNING: Server doesn't support lock/unlock APIs (treating as unlocked)"
                logger.warning(f"Server doesn't support unlock API for {clean_node_id}")
            else:
                api_status = f"WARNING: Alfresco unlock failed: {error_str}"
                logger.error(f"Failed to unlock document {clean_node_id}: {error_str}")
        
        if ctx:
            await ctx.info("Cleaning up local files...")
            await ctx.report_progress(0.7)
        
        # Clean up local checkout tracking
        downloads_dir = pathlib.Path.home() / "Downloads"
        checkout_dir = downloads_dir / "checkout"
        checkout_manifest_path = checkout_dir / ".checkout_manifest.json"
        
        checkout_data = {}
        cleanup_status = [api_status]
        
        if checkout_manifest_path.exists():
            try:
                with open(checkout_manifest_path, 'r') as f:
                    checkout_data = json.load(f)
            except:
                checkout_data = {}
        
        # Check if this node is tracked in local checkouts
        if 'checkouts' in checkout_data and clean_node_id in checkout_data['checkouts']:
            checkout_info = checkout_data['checkouts'][clean_node_id]
            checkout_filename = checkout_info['local_file']
            checkout_file_path = checkout_dir / checkout_filename
            
            # Remove local checkout file
            try:
                if checkout_file_path.exists():
                    checkout_file_path.unlink()
                    cleanup_status.append("🗑️ Local checkout file removed")
                    logger.info(f"Removed local checkout file: {checkout_file_path}")
                else:
                    cleanup_status.append("ℹ️ Local checkout file already removed")
            except Exception as e:
                cleanup_status.append(f"WARNING: Could not remove local file: {e}")
                logger.warning(f"Failed to remove local file {checkout_file_path}: {e}")
            
            # Remove from tracking
            del checkout_data['checkouts'][clean_node_id]
            
            # Update manifest
            try:
                with open(checkout_manifest_path, 'w') as f:
                    json.dump(checkout_data, f, indent=2)
                cleanup_status.append(">> Checkout tracking updated")
            except Exception as e:
                cleanup_status.append(f"WARNING: Could not update tracking: {e}")
        else:
            cleanup_status.append("ℹ️ No local checkout tracking found")
        
        if ctx:
            await ctx.info("Document unlocked!")
            await ctx.report_progress(1.0)
        
        # Clean JSON-friendly formatting (no markdown syntax)
        result = f"🔓 Document Unlocked\n\n"
        result += f">> Document: {filename}\n"
        result += f"ID: Node ID: {clean_node_id}\n"
        result += f"🕒 Unlocked: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
        result += f"🧹 Cleanup Status:\n"
        
        for status in cleanup_status:
            result += f"   {status}\n"
        
        result += f"\nINFO: Note: Document is now available for others to edit."
        result += f"\nWARNING: Important: Any unsaved changes in the local file have been discarded."
        
        return safe_format_output(result)
        
    except Exception as e:
        error_msg = f"❌ Cancel checkout failed: {str(e)}"
        if ctx:
            await ctx.error(error_msg)
        logger.error(f"Cancel checkout failed: {e}")
        return safe_format_output(error_msg) 
```

--------------------------------------------------------------------------------
/tests/mcp_specific/mcp_testing_guide.md:
--------------------------------------------------------------------------------

```markdown
# MCP Testing Guide - Base64 Examples and Utilities

## Base64 Example Strings

### Simple Examples
```
"SGVsbG8gV29ybGQ="           # "Hello World"
"VGVzdCBkb2N1bWVudA=="       # "Test document"
"U2FtcGxlIGNvbnRlbnQ="       # "Sample content"
```

### Document Content Examples
```python
# Text document
text_content = "This is a test document for MCP server testing."
text_base64 = "VGhpcyBpcyBhIHRlc3QgZG9jdW1lbnQgZm9yIE1DUCBzZXJ2ZXIgdGVzdGluZy4="

# JSON document  
json_content = '{"test": "data", "numbers": [1, 2, 3]}'
json_base64 = "eyJ0ZXN0IjogImRhdGEiLCAibnVtYmVycyI6IFsxLCAyLCAzXX0="
```

## Base64 Generation Functions

### Simple Generator Function
```python
import base64

def generate_base64(text_content):
    """Convert text to base64 string."""
    return base64.b64encode(text_content.encode('utf-8')).decode('utf-8')

def decode_base64(base64_string):
    """Decode base64 string to text."""
    return base64.b64decode(base64_string).decode('utf-8')

# Examples
text = "Hello World"
encoded = generate_base64(text)  # "SGVsbG8gV29ybGQ="
decoded = decode_base64(encoded)  # "Hello World"
```

### Document Generator for MCP Testing
```python
import base64
import json
import time
import uuid

def generate_test_document(filename, content_type="text"):
    """Generate a test document with base64 encoding for MCP upload."""
    
    if content_type == "text":
        content = f"""Test Document - {filename}
        
Created: {time.strftime('%Y-%m-%d %H:%M:%S')}
Document ID: {uuid.uuid4()}
Type: MCP Test Document

This is a sample document created for testing the Alfresco MCP server.
It contains structured content for validation purposes.

Sections:
1. Header information
2. Timestamp data
3. Unique identifier
4. Content body
5. Footer notes

End of document.
"""
    elif content_type == "json":
        content = json.dumps({
            "document_id": str(uuid.uuid4()),
            "filename": filename,
            "created_at": time.strftime('%Y-%m-%d %H:%M:%S'),
            "content_type": "test_data",
            "test_data": {
                "numbers": [1, 2, 3, 4, 5],
                "strings": ["hello", "world", "test"],
                "nested": {
                    "level1": {
                        "level2": "deep_value"
                    }
                }
            }
        }, indent=2)
    
    content_base64 = base64.b64encode(content.encode('utf-8')).decode('utf-8')
    
    return {
        "filename": filename,
        "content": content,
        "content_base64": content_base64,
        "size": len(content),
        "encoded_size": len(content_base64)
    }

# Usage examples
text_doc = generate_test_document("test_file.txt", "text")
json_doc = generate_test_document("test_data.json", "json")
```

### Batch Document Generator
```python
def generate_batch_documents(count=5, prefix="batch_doc"):
    """Generate multiple test documents for batch operations."""
    
    documents = []
    session_id = str(uuid.uuid4())[:8]
    
    for i in range(count):
        content = f"""Batch Document {i+1}
        
Session ID: {session_id}
Document Index: {i+1} of {count}
Created: {time.strftime('%Y-%m-%d %H:%M:%S')}
Unique ID: {uuid.uuid4()}

This is batch document number {i+1} created for testing 
concurrent operations and bulk uploads in the MCP server.

Content sections:
- Document metadata
- Sequential numbering
- Timestamp information
- Unique identification

Processing notes:
- Part of batch operation
- Sequential creation
- Automated generation
"""
        
        doc = {
            "filename": f"{prefix}_{session_id}_{i+1:03d}.txt",
            "content": content,
            "content_base64": base64.b64encode(content.encode('utf-8')).decode('utf-8'),
            "description": f"Batch document {i+1} from session {session_id}"
        }
        documents.append(doc)
    
    return documents

# Generate 5 test documents
batch_docs = generate_batch_documents(5)
```

## MCP Upload Examples

### Single Document Upload
```json
{
  "filename": "test_document.txt",
  "content_base64": "VGhpcyBpcyBhIHRlc3QgZG9jdW1lbnQgZm9yIE1DUCBzZXJ2ZXIgdGVzdGluZy4=",
  "parent_id": "-root-",
  "description": "Test upload via MCP Inspector"
}
```

### JSON Document Upload
```json
{
  "filename": "test_data.json", 
  "content_base64": "eyJ0ZXN0IjogImRhdGEiLCAibnVtYmVycyI6IFsxLCAyLCAzXX0=",
  "parent_id": "-root-",
  "description": "JSON test data"
}
```

## Validation Functions

### Base64 Validation
```python
import re

def validate_base64(base64_string):
    """Validate base64 string format."""
    try:
        # Check format
        if not re.match(r'^[A-Za-z0-9+/]*={0,2}$', base64_string):
            return False, "Invalid base64 characters"
        
        # Test decode
        decoded = base64.b64decode(base64_string, validate=True)
        return True, f"Valid base64 ({len(decoded)} bytes)"
        
    except Exception as e:
        return False, f"Decode error: {str(e)}"

# Test validation
valid, message = validate_base64("SGVsbG8gV29ybGQ=")
print(f"Validation: {valid}, Message: {message}")
```

### Content Size Calculation
```python
def calculate_base64_size(text_content):
    """Calculate the base64 encoded size before encoding."""
    original_bytes = len(text_content.encode('utf-8'))
    base64_bytes = ((original_bytes + 2) // 3) * 4
    return {
        "original_size": original_bytes,
        "base64_size": base64_bytes,
        "size_increase": f"{((base64_bytes / original_bytes - 1) * 100):.1f}%"
    }

# Example
size_info = calculate_base64_size("Hello World")
print(size_info)
# {'original_size': 11, 'base64_size': 16, 'size_increase': '45.5%'}
```

## Quick Reference

### Common Test Strings
```python
TEST_STRINGS = {
    "hello": "SGVsbG8gV29ybGQ=",
    "test": "dGVzdA==", 
    "sample": "c2FtcGxl",
    "document": "ZG9jdW1lbnQ=",
    "content": "Y29udGVudA==",
    "data": "ZGF0YQ==",
    "file": "ZmlsZQ==",
    "upload": "dXBsb2Fk"
}
```

### Usage in MCP Inspector
1. **Generate content**: Use the generator functions above
2. **Copy base64**: Use the `content_base64` field from generated documents  
3. **Test upload**: Paste into MCP Inspector's upload_document tool
4. **Validate**: Check successful upload in Alfresco

### File Size Limits
- **Small files**: < 1MB (recommended for testing)
- **Medium files**: 1-10MB (stress testing)  
- **Large files**: > 10MB (may require chunking)

Remember: Base64 encoding increases file size by approximately 33%, so plan accordingly for upload limits. 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/utils/file_type_analysis.py:
--------------------------------------------------------------------------------

```python
"""
File type analysis utility for Alfresco MCP Server.
Provides content type analysis and suggestions for different file types.
"""
import pathlib
import mimetypes
from typing import Dict, List, Optional


def detect_file_extension_from_content(content: bytes) -> Optional[str]:
    """Detect file type from content and return appropriate extension.
    
    Args:
        content: Raw file content bytes
        
    Returns:
        File extension (e.g., '.pdf', '.txt', '.jpg') or None if unknown
    """
    if not content:
        return None
    
    # Check for common file signatures (magic numbers)
    if content.startswith(b'%PDF'):
        return '.pdf'
    elif content.startswith(b'\xff\xd8\xff'):
        return '.jpg'
    elif content.startswith(b'\x89PNG\r\n\x1a\n'):
        return '.png'
    elif content.startswith(b'GIF87a') or content.startswith(b'GIF89a'):
        return '.gif'
    elif content.startswith(b'PK\x03\x04') or content.startswith(b'PK\x05\x06') or content.startswith(b'PK\x07\x08'):
        # ZIP-based formats (could be .zip, .docx, .xlsx, .pptx, etc.)
        # For simplicity, assume .zip unless we detect Office formats
        if b'word/' in content[:1024] or b'xl/' in content[:1024] or b'ppt/' in content[:1024]:
            # Likely Office document but hard to determine exact type
            return '.zip'  # Conservative choice
        return '.zip'
    elif content.startswith(b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1'):
        # Microsoft Office old format
        return '.doc'  # Could also be .xls, .ppt but .doc is most common
    elif content.startswith(b'<?xml') or content.startswith(b'\xef\xbb\xbf<?xml'):
        return '.xml'
    elif content.startswith(b'<!DOCTYPE html') or content.startswith(b'<html'):
        return '.html'
    elif content.startswith(b'{\n') or content.startswith(b'{"') or content.startswith(b'[\n') or content.startswith(b'[{'):
        return '.json'
    else:
        # Try to detect if it's text content
        try:
            # Try to decode as UTF-8 text
            text_content = content.decode('utf-8')
            # Check if it contains mostly printable characters
            printable_chars = sum(1 for c in text_content if c.isprintable() or c.isspace())
            if len(text_content) > 0 and printable_chars / len(text_content) > 0.8:
                return '.txt'
        except (UnicodeDecodeError, UnicodeError):
            pass
    
    # Unknown binary format
    return None


def analyze_content_type(filename: str, mime_type: str, content: bytes) -> dict:
    """Analyze file type and provide relevant suggestions.
    
    Args:
        filename: Name of the file
        mime_type: MIME type of the file
        content: File content as bytes
        
    Returns:
        Dictionary with category, suggestions, and file_size
    """
    file_size = len(content)
    
    # Get case-insensitive filename for macOS/Windows compatibility
    filename_lower = filename.lower()
    
    # Determine file category based on MIME type and extension
    if mime_type.startswith('image/'):
        category = 'images'
        suggestions = [
            "Can be used for thumbnails and previews",
            "Consider image optimization for web use"
        ]
    elif mime_type.startswith('video/') or mime_type.startswith('audio/'):
        category = 'media'
        suggestions = [
            "Large files may need streaming support",
            "Consider format compatibility for playback"
        ]
    elif mime_type in ['application/pdf', 'application/msword', 
                      'application/vnd.openxmlformats-officedocument.wordprocessingml.document']:
        category = 'documents'
        suggestions = [
            "PDF files support full-text search",
            ">> Consider using text extraction for searchable content",
            "Can be previewed in most browsers"
        ]
    elif mime_type in ['application/vnd.ms-excel',
                      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']:
        category = 'documents'
        suggestions = [
            ">> Excel spreadsheet - can be opened with Excel or LibreOffice Calc",
            ">> May contain tracked changes or comments"
        ]
    elif mime_type in ['application/vnd.ms-powerpoint',
                      'application/vnd.openxmlformats-officedocument.presentationml.presentation']:
        category = 'documents'
        suggestions = [
            "Presentation files support slide previews",
            "Consider extracting slide content for search"
        ]
    elif mime_type.startswith('text/') or 'javascript' in mime_type or 'json' in mime_type:
        if filename_lower.endswith(('.py', '.js', '.java', '.cpp', '.c', '.cs', '.php', '.rb')):
            category = 'code'
            suggestions = [
                "Source code files support syntax highlighting",
                ">> Check contents before extraction for security",
                "Can be indexed for code search"
            ]
        else:
            category = 'documents'
            suggestions = [
                ">> Review for security before execution",
                ">> May require specific runtime environment"
            ]
    elif mime_type in ['application/zip', 'application/x-rar-compressed', 'application/gzip']:
        category = 'archives'
        suggestions = [
            "Archive contents can be extracted and indexed",
            ">> Check contents before extraction for security",
            "May contain multiple file types"
        ]
    else:
        category = 'other'
        suggestions = [
            "Unknown file type - review content manually",
            "Consider file format documentation"
        ]
    
    # Add size-based suggestions
    if file_size > 100 * 1024 * 1024:  # > 100MB
        suggestions.append("WARNING: Large file - consider network and storage impact")
    
    # Add security suggestions for executable files (case-insensitive for cross-platform)
    executable_extensions = (
        '.exe', '.bat', '.sh', '.com', '.scr',  # Windows & shell scripts
        '.app', '.dmg', '.pkg',                 # macOS
        '.deb', '.rpm', '.run', '.bin', '.appimage'  # Linux
    )
    if filename_lower.endswith(executable_extensions):
        suggestions = [
            "WARNING: Executable file - scan for security before running",
            "Consider sandboxed execution environment"
        ]
        category = 'executable'
    
    return {
        'category': category,
        'suggestions': suggestions,
        'file_size': file_size
    } 
```

--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------

```markdown
# Changelog

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.1.0] - 2025-01-25

### Alfresco_MCP_Server dir code changes for v1.1
- **Code Split**: Refactored from monolithic single file to modular structure with separate files
- **Directory Structure**: Reorganized from flat to hierarchical structure
  - `tools/search/` - 4 search-related tools
  - `tools/core/` - 11 core management tools
  - `resources/` - Repository information
  - `prompts/` - AI templates
  - `utils/` - Shared utilities  
- fix: implement Windows UTF-8 encoding support (emoji character encoding)
- get to work with python-alfresco-api 1.1.x
- add imports and __all__ to get files included in packaging

### UV Package Management Support
- **Rust-based Performance**: UV provides faster dependency resolution and package management
- **Automatic Environment Management**: UV handles virtual environment creation and activation
- **Simplified Installation**: One-command setup replaces multi-step manual process
- **Cross-platform Compatibility**: Consistent behavior across Windows, macOS, and Linux

### PyPI Distribution
- **Direct Installation**: Available via `pip install python-alfresco-mcp-server`
- **UV Integration**: Compatible with `uv pip install python-alfresco-mcp-server`
- **Immediate Usage**: Run server directly after installation without source code

### Update Tests for v1.1
- `test: transform test suite from 76+ failures to 143/143 passing`
- `test: add comprehensive live integration testing with 21 Alfresco tests`
- `test: implement cross-platform emoji handling for Windows compatibility`
- `test: enhance coverage reporting with HTML output`
- `test: add automated testing for all manual scenarios`
- `test: implement strip_emojis() function for Windows compatibility`
- `test: add comprehensive unit test coverage (122 tests)`
- `test: fix test import paths for modular architecture`

### MCP Clients: config files added for v1.1
-  Added Claude Desktop and MCP Inspector config files
- `config: added claude-desktop-config-developer-windows.json with UV support`
- `config: added claude-desktop-config-developer-macos.json with UV support`
- `config: added claude-desktop-config-user-windows.json`
- `config: added claude-desktop-config-user-macos.json`
- `config: added Windows UTF-8 encoding variables to windows configs`
- `config: added mcp-inspector-http-config.json for HTTP transport using uv`
- `config: added mcp-inspector-stdio-config.json for STDIO transport using uv`
- 'examples: prompts-for-claude.md has example prompt text to test tools

### Update docs for v1.1
- `docs: create comprehensive CHANGELOG.md for v1.0.0 and v1.1.0`
- `docs: update quick start guide for UV approach`
- 'docs: api_reference.md added details for all tools, etc
- `docs: added claude_desktop_setup.md, mcp_inspector_setup.md, and client_configurations.md

### Documentation Updates for v1.1
- **Installation Instructions**: Added PyPI installation methods
- **Configuration Examples**: Updated for UV approach
- **Testing Procedures**: Comprehensive test execution guidance
- **Troubleshooting**: Enhanced problem resolution guidance

### Update Readme for v1.1
- added install from PyPI 
- added how to install UV package manager
- added claude desktop and mcp inspector setup sections
- reword technical sections for accuracy
- add v1.1 list of changes, depend on python-alfresco 1.1.1, python 3.10+
- added how to install Alfresco Community from github

### Update project files and misc root dir files for v1.1
- gitignore now ignores cursor memory and memory-bank dir, keep .vscode/mcp.json
- config.yaml has changed alfresco_url to have base_url, added timeout, added
  mcp server name and version confg
- MANIFEST.in added for proper package distrubution config
- pyproject.toml change to have v1.1.0 release, pypi distrib settings, remove
  fastMCP version restriction, update to require python 3.10 or greater, add
  pypi keyword and topic, settings, project urls for pypi, configure setuptools
  for includes, excludes
- run_server.py script added
- .vscode/mcp.json  uses run_server.py for debugging mcp server
-  uv.lock add now that use uv, and uv lock --upgrade updated v0.12.5 from v0.12.4 of ruff

### Fixes for pyproject.toml  for v1.1
 - require python-alfresco-api >= 1.1.1 not 1.0.0 in dependencies
 - have python 3.10 instead of 3.8 for tool.mypy, and py310 not py38 for tool.ruff and tool.black
 
### Example Code Updates for v1.1
- `examples: update transport examples for UV approach`
- `examples: enhance document lifecycle example`
- `examples: add UV-specific installation examples`
- `examples: update batch operations for improved performance`
- `examples: refactor error handling patterns` 

### Requirements
- **Python Versions**: 3.10+ (unchanged)
- **python-alfresco-api**: >= 1.1.1 (updated requirement)
- **FastMCP**: Tested with v2.10.6

### Tested
- **Alfresco Versions**: Community 25.1 (tested), Enterprise (not tested in v1.1)
- **Operating Systems**: 
  - Windows (tested)
  - macOS (needs testing)
  - Linux (needs testing, note: no Claude Desktop support on Linux)
- **MCP Clients**: 
  - Claude Desktop (tested and validated)
  - MCP Inspector (tested and validated)
  - Cursor (configuration provided, not tested in v1.1)
  - Claude Code (configuration provided, not tested in v1.1) 

## [1.0.0] - 2024-06-24

### Added
- Initial FastMCP 2.0 server implementation
- 15 content management tools across search and core operations
- Full text search with wildcard support
- Advanced search using AFTS query language
- Metadata search with property-based queries
- CMIS SQL search capabilities
- Complete document lifecycle management (upload, download, checkout, checkin)
- Version management with major/minor version support
- Folder operations and repository browsing
- Property management for documents and folders
- Multiple transport protocols (STDIO, HTTP, SSE)
- Configuration via environment variables and .env files
- Claude Desktop integration with Windows and macOS configs
- MCP Inspector support for development testing
- Comprehensive documentation and examples
- Testing framework with unit and integration tests
- Error handling and recovery patterns
- Repository discovery and status reporting

### Technical Implementation
- python-alfresco-api integration for content services access
- Pydantic v2 models for type safety
- Async support for concurrent operations
- Connection pooling and authentication management
- Progress reporting and context logging
- Configuration validation and environment setup 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/tools/core/browse_repository.py:
--------------------------------------------------------------------------------

```python
"""
Browse repository tool for Alfresco MCP Server.
Self-contained tool for browsing Alfresco repository structure.
"""
import logging
from typing import Optional
from fastmcp import Context

from ...utils.connection import ensure_connection

logger = logging.getLogger(__name__)


async def browse_repository_impl(
    parent_id: str = "-my-",
    max_items: int = 25,
    ctx: Optional[Context] = None
) -> str:
    """Browse the Alfresco repository structure.
    
    Args:
        parent_id: Parent node ID to browse (default: user's personal space)
        max_items: Maximum number of items to return (default: 25)
        ctx: MCP context for progress reporting
    
    Returns:
        Formatted listing of repository contents
    """
    # Parameter validation and extraction
    try:
        # Extract parameters with fallback handling
        if hasattr(parent_id, 'value'):
            actual_parent_id = str(parent_id.value)
        else:
            actual_parent_id = str(parent_id)
            
        if hasattr(max_items, 'value'):
            actual_max_items = int(max_items.value)
        else:
            actual_max_items = int(max_items)
        
        # Clean and normalize for display (preserve Unicode characters)
        safe_parent_id_display = str(actual_parent_id)
        
    except Exception as e:
        logger.error(f"Parameter extraction error: {e}")
        return f"ERROR: Parameter error: {str(e)}"
    
    if ctx:
        await ctx.info(f"Browsing repository node: {safe_parent_id_display}")
        await ctx.report_progress(0.0)
    
    try:
        # Get all clients that ensure_connection() already created
        master_client = await ensure_connection()
        
        # Access the core client that was already created
        core_client = master_client.core
        
        # Try high-level API first, then use raw client property (NEW: cleaner access)
        # Check if we can use high-level nodes.get_children()
        try:
            # Use high-level API for browsing (preferred approach)
            children_result = core_client.nodes.get_children(actual_parent_id, max_items=actual_max_items)
            if children_result and hasattr(children_result, 'list') and hasattr(children_result.list, 'entries'):
                entries = children_result.list.entries
                logger.info(f"Browse response via high-level API: {len(entries)} entries found")
            else:
                raise Exception("High-level API returned unexpected format")
        except Exception as high_level_error:
            logger.info(f"High-level API failed, using raw client: {high_level_error}")
            # Fallback to raw client (ensure initialization)
            if not core_client.is_initialized:
                return safe_format_output("❌ Error: Alfresco server unavailable")
            # Use httpx_client property directly on AlfrescoCoreClient
            core_httpx = core_client.httpx_client
        
        logger.info(f"Browsing repository node: {safe_parent_id_display}")
        logger.info(f"Max items: {actual_max_items}")
        logger.info(f"Using URL: /nodes/{actual_parent_id}/children")
        
        if ctx:
            await ctx.report_progress(0.3)
        
        # If high-level API didn't work, use HTTPx fallback
        if 'entries' not in locals():
            if ctx:
                await ctx.report_progress(0.5)
            
            try:
                # Use HTTPx client as fallback
                url = f"/nodes/{actual_parent_id}/children"
                if actual_max_items != 25:
                    url += f"?maxItems={actual_max_items}"
                
                response = core_httpx.get(url)
                
                if response.status_code == 200:
                    result_data = response.json()
                    entries = result_data.get("list", {}).get("entries", [])
                    logger.info(f"Browse response via HTTPx fallback: {len(entries)} entries found")
                    
                else:
                    error_text = response.text if hasattr(response, 'text') else str(response)
                    raise Exception(f"Browse failed with status {response.status_code}: {error_text}")
                    
            except Exception as browse_error:
                raise Exception(f"Repository browse operation failed: {str(browse_error)}")
        
        # Check if we have entries
        if not entries:
            return f"Repository Browse Results\n\nNode: {safe_parent_id_display}\n\nNo child items found in this location."
        
        if ctx:
            await ctx.report_progress(1.0)
        
        # Process final results
        if entries:
            logger.info(f"Found {len(entries)} repository items")
            
            # Clean JSON-friendly formatting (no markdown syntax)
            result_text = f"Repository Browse Results\n\nNode: {safe_parent_id_display}\n\n"
            result_text += f"Parent Node: {safe_parent_id_display}\n"
            result_text += f"Found {len(entries)} item(s):\n\n"
            
            for i, entry_wrapper in enumerate(entries, 1):
                # Handle JSON response structure correctly
                if isinstance(entry_wrapper, dict) and 'entry' in entry_wrapper:
                    entry = entry_wrapper['entry']
                else:
                    entry = entry_wrapper
                
                # Extract values from dictionary
                name = str(entry.get('name', 'Unknown'))
                node_id = str(entry.get('id', 'Unknown'))
                node_type = str(entry.get('nodeType', 'Unknown'))
                is_folder = entry.get('isFolder', False)
                created_at = str(entry.get('createdAt', 'Unknown'))
                
                # Choose icon based on type
                icon = "[FOLDER]" if is_folder else "[FILE]"
                
                result_text += f"{i}. {icon} {name}\n"
                result_text += f"   - ID: {node_id}\n"
                result_text += f"   - Type: {node_type}\n"
                result_text += f"   - Created: {created_at}\n\n"
            
            result_text += f"Navigation help:\n"
            result_text += "• Use the node ID to browse deeper: browse_repository(parent_id=\"<node_id>\")\n"
            result_text += "• Common parent IDs: -root- (repository root), -shared- (shared folder), -my- (my files)\n"
            
            return result_text
        else:
            return f"Repository Browse Results\n\nNode: {safe_parent_id_display}\n\nNo child items found in this location."
            
    except Exception as e:
        # Preserve Unicode characters in error messages
        error_msg = f"ERROR: Repository browse failed: {str(e)}"
        if ctx:
            await ctx.error(error_msg)
        return error_msg 

    if ctx:
        await ctx.info("Repository browse completed!") 
```

--------------------------------------------------------------------------------
/scripts/run_tests.py:
--------------------------------------------------------------------------------

```python
#!/usr/bin/env python3
"""
Comprehensive test runner for Alfresco MCP Server.
Provides different test modes: unit, integration, coverage, performance.
"""
import sys
import os
import subprocess
import argparse
from pathlib import Path


def run_command(cmd, description=""):
    """Run a command and handle output."""
    print(f"\n{'='*60}")
    print(f"🚀 {description}" if description else f"Running: {' '.join(cmd)}")
    print('='*60)
    
    try:
        result = subprocess.run(cmd, check=True, capture_output=True, text=True)
        print(result.stdout)
        if result.stderr:
            print("STDERR:", result.stderr)
        return True
    except subprocess.CalledProcessError as e:
        print(f"❌ Command failed with exit code {e.returncode}")
        print("STDOUT:", e.stdout)
        print("STDERR:", e.stderr)
        return False


def install_dependencies():
    """Install test dependencies."""
    dependencies = [
        "pytest>=7.0.0",
        "pytest-asyncio>=0.21.0",
        "pytest-cov>=4.0.0",
        "pytest-xdist>=3.0.0",  # For parallel test execution
        "pytest-mock>=3.10.0",
        "coverage>=7.0.0",
        "httpx>=0.24.0",
    ]
    
    print("📦 Installing test dependencies...")
    for dep in dependencies:
        cmd = [sys.executable, "-m", "pip", "install", dep]
        if not run_command(cmd, f"Installing {dep}"):
            return False
    return True


def run_unit_tests():
    """Run unit tests with mocking."""
    cmd = [
        sys.executable, "-m", "pytest",
        "tests/test_coverage.py",
        "tests/test_fastmcp_2_0.py",
        "tests/test_unit_tools.py",
        "-v",
        "--tb=short",
        "-m", "unit",
        "--cov=alfresco_mcp_server",
        "--cov-report=term-missing",
        "--cov-report=html:htmlcov",
        "--cov-branch"
    ]
    
    return run_command(cmd, "Running Unit Tests (Fast, Mocked)")


def run_integration_tests():
    """Run integration tests with live Alfresco."""
    cmd = [
        sys.executable, "-m", "pytest",
        "tests/test_integration.py",
        "-v",
        "--tb=short",
        "-m", "integration",
        "--integration"
    ]
    
    return run_command(cmd, "Running Integration Tests (Requires Live Alfresco)")


def run_performance_tests():
    """Run performance benchmarks."""
    cmd = [
        sys.executable, "-m", "pytest",
        "tests/test_integration.py",
        "-v",
        "--tb=short",
        "-m", "performance",
        "--integration",
        "--performance"
    ]
    
    return run_command(cmd, "Running Performance Tests")


def run_all_tests():
    """Run all tests with comprehensive coverage."""
    cmd = [
        sys.executable, "-m", "pytest",
        "tests/",
        "-v",
        "--tb=short",
        "--cov=alfresco_mcp_server",
        "--cov-report=html:htmlcov",
        "--cov-report=term-missing",
        "--cov-report=xml",
        "--cov-branch",
        "--cov-fail-under=85",
        "-x"  # Stop on first failure
    ]
    
    return run_command(cmd, "Running All Tests with Coverage")


def run_coverage_only():
    """Run coverage analysis only."""
    # First run tests to generate coverage data
    cmd = [
        sys.executable, "-m", "pytest",
        "tests/test_coverage.py",
        "tests/test_fastmcp_2_0.py", 
        "tests/test_unit_tools.py",
        "--cov=alfresco_mcp_server",
        "--cov-report=html:htmlcov",
        "--cov-report=xml",
        "--cov-branch",
        "-q"  # Quiet mode
    ]
    
    if not run_command(cmd, "Generating Coverage Data"):
        return False
    
    # Generate coverage report
    cmd = [sys.executable, "-m", "coverage", "report", "--show-missing"]
    return run_command(cmd, "Coverage Report")


def check_alfresco_availability():
    """Check if Alfresco server is available."""
    try:
        import httpx
        response = httpx.get("http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/probes/-ready-", timeout=5.0)
        if response.status_code == 200:
            print("✅ Alfresco server is available at localhost:8080")
            return True
    except Exception as e:
        print(f"⚠️ Alfresco server not available: {e}")
        print("Integration tests will be skipped")
    return False


def lint_code():
    """Run code linting."""
    print("🔍 Running code quality checks...")
    
    # Try to install and run linting tools
    linting_commands = [
        ([sys.executable, "-m", "pip", "install", "black", "ruff", "mypy"], "Installing linting tools"),
        ([sys.executable, "-m", "black", "--check", "alfresco_mcp_server/"], "Black formatting check"),
        ([sys.executable, "-m", "ruff", "check", "alfresco_mcp_server/"], "Ruff linting"),
    ]
    
    success = True
    for cmd, desc in linting_commands:
        if not run_command(cmd, desc):
            success = False
    
    return success


def main():
    parser = argparse.ArgumentParser(description="Comprehensive test runner for Alfresco MCP Server")
    parser.add_argument("--mode", choices=["unit", "integration", "performance", "coverage", "all", "lint"], 
                       default="unit", help="Test mode to run")
    parser.add_argument("--install-deps", action="store_true", help="Install test dependencies")
    parser.add_argument("--check-alfresco", action="store_true", help="Check Alfresco availability")
    
    args = parser.parse_args()
    
    # Change to project root
    project_root = Path(__file__).parent.parent
    os.chdir(project_root)
    
    print(f"🧪 Alfresco MCP Server Test Runner")
    print(f"📁 Working directory: {os.getcwd()}")
    print(f"🎯 Test mode: {args.mode}")
    
    # Install dependencies if requested
    if args.install_deps:
        if not install_dependencies():
            sys.exit(1)
    
    # Check Alfresco availability if requested
    if args.check_alfresco:
        check_alfresco_availability()
    
    # Run tests based on mode
    success = True
    
    if args.mode == "unit":
        success = run_unit_tests()
    elif args.mode == "integration":
        if not check_alfresco_availability():
            print("❌ Alfresco server required for integration tests")
            sys.exit(1)
        success = run_integration_tests()
    elif args.mode == "performance":
        if not check_alfresco_availability():
            print("❌ Alfresco server required for performance tests")
            sys.exit(1)
        success = run_performance_tests()
    elif args.mode == "coverage":
        success = run_coverage_only()
    elif args.mode == "all":
        alfresco_available = check_alfresco_availability()
        success = run_all_tests()
        if success and alfresco_available:
            print("\n🔄 Running integration tests...")
            success = run_integration_tests()
    elif args.mode == "lint":
        success = lint_code()
    
    # Summary
    print(f"\n{'='*60}")
    if success:
        print("✅ All tests completed successfully!")
        print("\n📊 Coverage report available at: htmlcov/index.html")
    else:
        print("❌ Some tests failed!")
        sys.exit(1)
    print('='*60)


if __name__ == "__main__":
    main() 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/tools/search/search_by_metadata.py:
--------------------------------------------------------------------------------

```python
"""
Search by metadata tool for Alfresco MCP Server.
Each tool is self-contained with its own validation, business logic, and env handling.
"""
import logging
from typing import Optional
from fastmcp import Context

from ...utils.connection import ensure_connection
from ...utils.json_utils import safe_format_output

logger = logging.getLogger(__name__)


async def search_by_metadata_impl(
    term: str = "",
    creator: str = "",
    content_type: str = "",
    max_results: int = 25,
    ctx: Optional[Context] = None
) -> str:
    """Search for content in Alfresco by metadata fields.
    
    Args:
        term: Search term to include (primary search parameter)
        creator: Creator username to filter by
        content_type: Content type to filter by (e.g., "cm:content", "cm:folder")
        max_results: Maximum number of results to return (default: 25)
        ctx: MCP context for progress reporting
    
    Returns:
        Formatted search results with metadata
    """
    # Parameter validation and extraction
    try:
        # Extract parameters with fallback handling
        if hasattr(creator, 'value'):
            actual_creator = str(creator.value)
        else:
            actual_creator = str(creator)
            
        if hasattr(content_type, 'value'):
            actual_content_type = str(content_type.value)
        else:
            actual_content_type = str(content_type)
            
        if hasattr(term, 'value'):
            actual_term = str(term.value)
        else:
            actual_term = str(term)
            
        if hasattr(max_results, 'value'):
            actual_max_results = int(max_results.value)
        else:
            actual_max_results = int(max_results)
        
        # Clean and normalize for display (preserve Unicode characters)
        safe_creator_display = str(actual_creator)
        safe_content_type_display = str(actual_content_type)
        safe_term_display = str(actual_term)
        
    except Exception as e:
        logger.error(f"Parameter extraction error: {e}")
        return f"ERROR: Parameter error: {str(e)}"
    
    if ctx:
        await ctx.info(safe_format_output(f"Searching by metadata in Alfresco..."))
        await ctx.report_progress(0.0)
    
    try:
        # Get all clients that ensure_connection() already created
        master_client = await ensure_connection()
        
        # Import search_utils
        from python_alfresco_api.utils import search_utils
        
        # Access the search client that was already created
        search_client = master_client.search
        
        logger.info(f"Searching Alfresco by metadata - creator: '{safe_creator_display}', type: '{safe_content_type_display}', term: '{safe_term_display}'")
        
        if ctx:
            await ctx.report_progress(0.3)
        
        # Build query using search_utils.build_query() utility
        search_query = search_utils.build_query(
            term=actual_term if actual_term.strip() else None,
            content_type=actual_content_type if actual_content_type.strip() else None,
            creator=actual_creator if actual_creator.strip() else None
        )
        
        # If no parameters provided, search everything
        if not search_query or search_query.strip() == "":
            search_query = "*"
        
        # Execute search using existing search client
        if ctx:
            await ctx.report_progress(0.5)
        
        try:
            # Use correct working pattern: search_utils.simple_search with existing search_client
            search_results = search_utils.simple_search(search_client, search_query, max_items=actual_max_results)
            
            if not search_results or not hasattr(search_results, 'list_'):
                return safe_format_output(f"ERROR: Search failed - invalid response from Alfresco")
                
        except Exception as e:
            logger.error(f"Search failed: {e}")
            return safe_format_output(f"ERROR: Search failed: {str(e)}")
        
        # Process results using correct pattern
        entries = []
        if hasattr(search_results, 'list_') and search_results.list_ and hasattr(search_results.list_, 'entries'):
            entries = search_results.list_.entries if search_results.list_ else []
        
        if ctx:
            await ctx.report_progress(1.0)
        
        # Process final results
        if entries:
            logger.info(f"Found {len(entries)} search results")
            result_text = f"Found {len(entries)} item(s) matching the metadata criteria:\n\n"
            
            for i, entry in enumerate(entries, 1):
                # Debug: Log the entry structure
                logger.debug(f"Entry {i} type: {type(entry)}, content: {entry}")
                
                # Handle different possible entry structures
                node = None
                if isinstance(entry, dict):
                    if 'entry' in entry:
                        node = entry['entry']
                    elif 'name' in entry:  # Direct node structure
                        node = entry
                    else:
                        logger.warning(f"Unknown entry structure: {entry}")
                        continue
                elif hasattr(entry, 'entry'):  # ResultSetRowEntry object
                    node = entry.entry
                else:
                    logger.warning(f"Entry is not a dict or ResultSetRowEntry: {type(entry)}")
                    continue
                
                if node:
                    # Handle both dict and ResultNode objects
                    if isinstance(node, dict):
                        name = str(node.get('name', 'Unknown'))
                        node_id = str(node.get('id', 'Unknown'))
                        node_type_actual = str(node.get('nodeType', 'Unknown'))
                        created_at = str(node.get('createdAt', 'Unknown'))
                    else:
                        # ResultNode object - access attributes directly
                        name = str(getattr(node, 'name', 'Unknown'))
                        node_id = str(getattr(node, 'id', 'Unknown'))
                        node_type_actual = str(getattr(node, 'node_type', 'Unknown'))
                        created_at = str(getattr(node, 'created_at', 'Unknown'))
                    
                    # Clean JSON-friendly formatting (no markdown syntax)
                    # Apply safe formatting to individual fields to prevent emoji encoding issues
                    safe_name = safe_format_output(name)
                    safe_node_id = safe_format_output(node_id)
                    safe_node_type = safe_format_output(node_type_actual)
                    safe_created_at = safe_format_output(created_at)
                    
                    result_text += f"{i}. {safe_name}\n"
                    result_text += f"   - ID: {safe_node_id}\n"
                    result_text += f"   - Type: {safe_node_type}\n"
                    result_text += f"   - Created: {safe_created_at}\n\n"
        
            return safe_format_output(result_text)
        else:
            # Simple "0" for zero results as requested
            return "0"
        
    except Exception as e:
        # Preserve Unicode characters in error messages
        error_msg = f"ERROR: Metadata search failed: {str(e)}"
        if ctx:
            await ctx.error(safe_format_output(error_msg))
        return safe_format_output(error_msg) 

    if ctx:
        await ctx.info(safe_format_output("Metadata search completed!")) 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/tools/search/cmis_search.py:
--------------------------------------------------------------------------------

```python
"""
CMIS search tool for Alfresco MCP Server.
Each tool is self-contained with its own validation, business logic, and env handling.
"""
import logging
from typing import Optional
from fastmcp import Context

from ...utils.connection import ensure_connection
from ...utils.json_utils import safe_format_output

logger = logging.getLogger(__name__)


async def cmis_search_impl(
    cmis_query: str,
    max_results: int = 25,
    ctx: Optional[Context] = None
) -> str:
    """Search using CMIS SQL syntax.
    
    Args:
        cmis_query: CMIS SQL query string
        max_results: Maximum number of results to return (default: 25)
        ctx: MCP context for progress reporting
    
    Returns:
        Formatted search results from CMIS query
    """
    # Parameter validation and extraction
    try:
        # Extract parameters with fallback handling
        if hasattr(cmis_query, 'value'):
            actual_query = str(cmis_query.value)
        else:
            actual_query = str(cmis_query)
            
        if hasattr(max_results, 'value'):
            actual_max_results = int(max_results.value)
        else:
            actual_max_results = int(max_results)
        
        # Clean and normalize for display (preserve Unicode characters)
        safe_query_display = str(actual_query)
        
    except Exception as e:
        logger.error(f"Parameter extraction error: {e}")
        return f"ERROR: Parameter error: {str(e)}"
    
    if not actual_query.strip():
        return """CMIS Search Tool

Usage: Provide a CMIS SQL query to search Alfresco repository.

Example CMIS queries:
- SELECT * FROM cmis:document WHERE cmis:name LIKE 'test%'
- SELECT * FROM cmis:folder WHERE CONTAINS('project')
- SELECT * FROM cmis:document WHERE cmis:creationDate > '2024-01-01T00:00:00.000Z'
- SELECT * FROM cmis:document WHERE cmis:contentStreamMimeType = 'application/pdf'

CMIS provides precise SQL queries for exact matching and filtering.
"""
    
    if ctx:
        await ctx.info(safe_format_output(f"CMIS search for: '{safe_query_display}'"))
        await ctx.report_progress(0.0)
    
    try:
        # Get all clients that ensure_connection() already created
        master_client = await ensure_connection()
        
        # Access the search client that was already created (same as other search tools)
        search_client = master_client.search
        
        logger.info(f"CMIS search for: '{safe_query_display}'")
        
        if ctx:
            await ctx.report_progress(0.3)
        
        # Use same pattern as other search tools but with CMIS language
        try:
            # Import the SearchRequest model for CMIS queries
            from python_alfresco_api.raw_clients.alfresco_search_client.search_client.models import SearchRequest, RequestQuery, RequestPagination, RequestQueryLanguage
            from python_alfresco_api.raw_clients.alfresco_search_client.search_client.types import UNSET
            
            # Create CMIS search request (same pattern as search_utils.simple_search but with CMIS language)
            request_query = RequestQuery(
                query=actual_query,
                language=RequestQueryLanguage.CMIS  # Use CMIS instead of AFTS
            )
            
            request_pagination = RequestPagination(
                max_items=actual_max_results,
                skip_count=0
            )
            
            search_request = SearchRequest(
                query=request_query,
                paging=request_pagination,
                include=UNSET
            )
            
            # Use same pattern as search_utils.simple_search
            search_results = search_client.search.search(search_request)
            
            if search_results and hasattr(search_results, 'list_'):
                entries_list = search_results.list_.entries if search_results.list_ else []
                logger.info(f"Found {len(entries_list)} CMIS search results")
                
                if ctx:
                    await ctx.report_progress(1.0)
                
                if not entries_list:
                    return "0"
                
                result_text = f"Found {len(entries_list)} item(s) matching the CMIS query:\n\n"
                
                for i, entry in enumerate(entries_list, 1):
                    # Debug: Log the entry structure
                    logger.debug(f"Entry {i} type: {type(entry)}, content: {entry}")
                    
                    # Handle different possible entry structures
                    node = None
                    if isinstance(entry, dict):
                        if 'entry' in entry:
                            node = entry['entry']
                        elif 'name' in entry:  # Direct node structure
                            node = entry
                        else:
                            logger.warning(f"Unknown entry structure: {entry}")
                            continue
                    elif hasattr(entry, 'entry'):  # ResultSetRowEntry object
                        node = entry.entry
                    else:
                        logger.warning(f"Entry is not a dict or ResultSetRowEntry: {type(entry)}")
                        continue
                    
                    if node:
                        # Handle both dict and ResultNode objects
                        if isinstance(node, dict):
                            name = str(node.get('name', 'Unknown'))
                            node_id = str(node.get('id', 'Unknown'))
                            node_type = str(node.get('nodeType', 'Unknown'))
                            created_at = str(node.get('createdAt', 'Unknown'))
                        else:
                            # ResultNode object - access attributes directly
                            name = str(getattr(node, 'name', 'Unknown'))
                            node_id = str(getattr(node, 'id', 'Unknown'))
                            node_type = str(getattr(node, 'node_type', 'Unknown'))
                            created_at = str(getattr(node, 'created_at', 'Unknown'))
                        
                        # Clean JSON-friendly formatting (no markdown syntax)
                        # Apply safe formatting to individual fields to prevent emoji encoding issues
                        safe_name = safe_format_output(name)
                        safe_node_id = safe_format_output(node_id)
                        safe_node_type = safe_format_output(node_type)
                        safe_created_at = safe_format_output(created_at)
                        
                        result_text += f"{i}. {safe_name}\n"
                        result_text += f"   - ID: {safe_node_id}\n"
                        result_text += f"   - Type: {safe_node_type}\n"
                        result_text += f"   - Created: {safe_created_at}\n\n"
                
                return safe_format_output(result_text)
            else:
                return safe_format_output(f"ERROR: CMIS search failed - invalid response from Alfresco")
                
        except Exception as e:
            logger.error(f"CMIS search failed: {e}")
            return safe_format_output(f"ERROR: CMIS search failed: {str(e)}")
        
    except Exception as e:
        # Preserve Unicode characters in error messages
        error_msg = f"ERROR: CMIS search failed: {str(e)}"
        if ctx:
            await ctx.error(safe_format_output(error_msg))
        return safe_format_output(error_msg)

    if ctx:
        await ctx.info(safe_format_output("CMIS search completed!")) 
```

--------------------------------------------------------------------------------
/docs/quick_start_guide.md:
--------------------------------------------------------------------------------

```markdown
# Quick Start Guide

Get up and running with the Alfresco MCP Server in 5 minutes! This guide walks you through installation, configuration, and your first successful connection.

## ⏱️ 5-Minute Setup

### Step 1: Prerequisites (30 seconds)

Ensure you have:
- ✅ Python 3.10+ installed
- ✅ Access to an Alfresco server (local or remote)
- ✅ Administrator credentials for Alfresco

```bash
# Check Python version
python --version  # Should be 3.10 or higher
```

### Step 2: Installation (1 minute)

**Option A: UV (Recommended - Automatic dependency management)**

```bash
# Install UV if you don't have it
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"  # Windows
# curl -LsSf https://astral.sh/uv/install.sh | sh          # macOS/Linux

# Clone the repository
git clone https://github.com/your-org/python-alfresco-mcp-server.git
cd python-alfresco-mcp-server

# That's it! UV will handle venv and dependencies automatically
# Test with:
uv run python-alfresco-mcp-server --help
```

**Option B: Traditional pip (Manual venv management)**

```bash
# Clone the repository
git clone https://github.com/your-org/python-alfresco-mcp-server.git
cd python-alfresco-mcp-server

# Create and activate virtual environment
python -m venv venv
source venv/bin/activate  # Linux/macOS
# venv\Scripts\activate    # Windows

# Install the package
pip install -e .

# Verify installation
python -m alfresco_mcp_server.fastmcp_server --help
```

### Step 3: Configuration (1 minute)

Set up your Alfresco connection:

```bash
# Option 1: Environment variables (recommended)
export ALFRESCO_URL="http://localhost:8080"
export ALFRESCO_USERNAME="admin"
export ALFRESCO_PASSWORD="admin"

# Option 2: Create config.yaml (alternative)
cp alfresco_mcp_server/config.yaml.example config.yaml
# Edit config.yaml with your settings
```

### Step 4: Test Connection (30 seconds)

```bash
# With UV (recommended)
uv run python examples/quick_start.py

# With traditional pip
python examples/quick_start.py
```

Expected output:
```
🚀 Alfresco MCP Server - Quick Start Example
==================================================
✅ Connected successfully!
🛠️ Available Tools:
  1. search_content - Search for documents and folders
  2. upload_document - Upload a new document
  ...
```

### Step 5: First MCP Operation (2 minutes)

Run your first document search:

```python
# Create test_search.py
import asyncio
from fastmcp import Client
from alfresco_mcp_server.fastmcp_server import mcp

async def first_search():
    async with Client(mcp) as client:
        # Search for documents
        result = await client.call_tool("search_content", {
            "query": "*",
            "max_results": 5
        })
        print("Search Results:")
        print(result[0].text)

# Run the search
asyncio.run(first_search())
```

## 🎉 Success!

If you see search results, congratulations! Your Alfresco MCP Server is working.

## 🚀 Next Steps

Now that you're connected, try these:

### 1. Upload Your First Document

```python
import base64
import asyncio
from fastmcp import Client
from alfresco_mcp_server.fastmcp_server import mcp

async def upload_demo():
    async with Client(mcp) as client:
        # Create sample content
        content = "Hello from MCP Server!"
        content_b64 = base64.b64encode(content.encode()).decode()
        
        # Upload document
        result = await client.call_tool("upload_document", {
            "filename": "hello_mcp.txt",
            "content_base64": content_b64,
            "parent_id": "-root-",
            "description": "My first MCP upload"
        })
        print(result[0].text)

asyncio.run(upload_demo())
```

### 2. Create a Folder

```python
async def create_folder_demo():
    async with Client(mcp) as client:
        result = await client.call_tool("create_folder", {
            "folder_name": "My_MCP_Folder",
            "parent_id": "-root-",
            "description": "Created via MCP Server"
        })
        print(result[0].text)

asyncio.run(create_folder_demo())
```

### 3. Get Repository Information

```python
async def repo_info_demo():
    async with Client(mcp) as client:
        # Get repository info
        info = await client.read_resource("alfresco://repository/info")
        print("Repository Info:")
        print(info[0].text)

asyncio.run(repo_info_demo())
```

## 🌐 Transport Options

The server supports multiple transport protocols:

```bash
# STDIO (default)
python -m alfresco_mcp_server.fastmcp_server

# HTTP
python -m alfresco_mcp_server.fastmcp_server --transport http --port 8001

# Server-Sent Events
python -m alfresco_mcp_server.fastmcp_server --transport sse --port 8002
```

## 🔧 Common Configuration

### Custom Alfresco URL
```bash
export ALFRESCO_URL="https://my-alfresco.company.com"
```

### Authentication Token
```bash
export ALFRESCO_TOKEN="your-auth-token"
```

### Debug Mode
```bash
export ALFRESCO_DEBUG="true"
```

## ⚡ Quick Examples

### Complete Document Workflow
```python
import asyncio
import base64
from fastmcp import Client
from alfresco_mcp_server.fastmcp_server import mcp

async def complete_workflow():
    async with Client(mcp) as client:
        # 1. Create folder
        folder_result = await client.call_tool("create_folder", {
            "folder_name": "Quick_Start_Demo",
            "parent_id": "-root-",
            "description": "Demo folder from quick start"
        })
        print("✅ Folder created")
        
        # 2. Upload document
        content = "This is a demo document created during quick start."
        content_b64 = base64.b64encode(content.encode()).decode()
        
        upload_result = await client.call_tool("upload_document", {
            "filename": "demo_document.txt",
            "content_base64": content_b64,
            "parent_id": "-root-",
            "description": "Demo document"
        })
        print("✅ Document uploaded")
        
        # 3. Search for our content
        search_result = await client.call_tool("search_content", {
            "query": "Quick_Start_Demo",
            "max_results": 10
        })
        print("✅ Search completed")
        print("Search Results:", search_result[0].text)

# Run the complete workflow
asyncio.run(complete_workflow())
```

## 🆘 Troubleshooting

### Connection Issues
```bash
# Test Alfresco connectivity
curl -u admin:admin http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-
```

### Common Errors

**Error: Connection refused**
- ✅ Check if Alfresco server is running
- ✅ Verify ALFRESCO_URL is correct
- ✅ Check firewall settings

**Error: Authentication failed**
- ✅ Verify username/password
- ✅ Check user permissions in Alfresco

**Error: Module not found**
- ✅ Run `pip install -e .` again
- ✅ Check Python virtual environment

### Getting Help

- 📖 Read the [troubleshooting guide](troubleshooting.md)
- 💬 Check GitHub Issues for common questions
- 🐛 Report issues on GitHub

## 📖 What's Next?

Explore more advanced features:

- 📄 **[Document Lifecycle](../examples/document_lifecycle.py)** - Complete document management
- 🔍 **[Search Examples](../examples/search_examples.py)** - Advanced search patterns
- ⚡ **[Batch Operations](../examples/batch_operations.py)** - Bulk processing
- 🌐 **[Transport Examples](../examples/transport_examples.py)** - Different connection methods

## 🎯 Key Concepts

- **MCP Tools**: 15 tools for document management (search, upload, download, checkout/checkin workflow, etc.)
- **Transport Protocols**: STDIO, HTTP, SSE for different use cases
- **Resources**: Repository information and health status
- **Prompts**: AI-powered analysis and insights

---

**🚀 Complete!** You've successfully set up the Alfresco MCP Server. You're now ready to build document management integrations! 
```

--------------------------------------------------------------------------------
/alfresco_mcp_server/tools/search/search_content.py:
--------------------------------------------------------------------------------

```python
"""
Content search tool for Alfresco MCP Server.
Each tool is self-contained with its own validation, business logic, and env handling.
"""
import logging
from typing import Optional
from fastmcp import Context

from ...utils.connection import ensure_connection
from ...utils.json_utils import safe_format_output

logger = logging.getLogger(__name__)


async def search_content_impl(
    search_query: str,
    max_results: int = 25,
    node_type: str = "cm:content",
    ctx: Optional[Context] = None
) -> str:
    """Search for content in Alfresco repository.
    
    Args:
        search_query: Search query string
        max_results: Maximum number of results to return (default: 25)
        node_type: Type of nodes to search for (default: "cm:content" - searches documents)
        ctx: MCP context for progress reporting
    
    Returns:
        Formatted search results
    """
    # Parameter validation and extraction
    try:
        # Extract parameters with fallback handling
        if hasattr(search_query, 'value'):
            actual_query = str(search_query.value)
        else:
            actual_query = str(search_query)
            
        if hasattr(max_results, 'value'):
            actual_max_results = int(max_results.value)
        else:
            actual_max_results = int(max_results)
            
        if hasattr(node_type, 'value'):
            actual_node_type = str(node_type.value)
        else:
            actual_node_type = str(node_type)
        
        # Default to cm:content if empty
        if not actual_node_type.strip():
            actual_node_type = "cm:content"
        
        # Clean and normalize for display (prevent Unicode encoding issues)
        safe_query_display = safe_format_output(str(actual_query))
        safe_node_type_display = safe_format_output(str(actual_node_type))
        
    except Exception as e:
        logger.error(f"Parameter extraction error: {e}")
        return safe_format_output(f"ERROR: Parameter error: {str(e)}")
    
    if not actual_query.strip():
        return """Content Search Tool

Usage: Provide a search query to search Alfresco repository content.

Example searches:
- admin (finds items with 'admin' in name or content)
- name:test* (finds items with names starting with 'test')
- modified:[2024-01-01 TO 2024-12-31] (finds items modified in 2024)
- TYPE:"cm:content" (finds all documents)
- TYPE:"cm:folder" (finds all folders)

Search uses AFTS (Alfresco Full Text Search) syntax for flexible content discovery.
By default, searches for documents (cm:content) unless a different type is specified.
"""
    
    if ctx:
        await ctx.info(safe_format_output(f"Content search for: '{safe_query_display}'"))
        await ctx.report_progress(0.0)
    
    try:
        # Get all clients that ensure_connection() already created
        master_client = await ensure_connection()
        
        # Import search_utils
        from python_alfresco_api.utils import search_utils
        
        # Access the search client that was already created
        search_client = master_client.search
        
        logger.info(f"Content search for: '{safe_query_display}', type: '{safe_node_type_display}'")
        
        if ctx:
            await ctx.report_progress(0.3)
        
        # Build search query to include node_type filter
        final_query = actual_query
        
        # Add node_type filter if not already in query
        has_type_in_query = "TYPE:" in final_query.upper()
        if not has_type_in_query:
            if final_query == "*":
                final_query = f'TYPE:"{actual_node_type}"'
            else:
                final_query = f'({final_query}) AND TYPE:"{actual_node_type}"'
        
        # Use the correct working pattern: search_utils.simple_search with existing search_client
        try:
            search_results = search_utils.simple_search(search_client, final_query, max_items=actual_max_results)
            
            if search_results and hasattr(search_results, 'list_'):
                entries_list = search_results.list_.entries if search_results.list_  else []
                logger.info(f"Found {len(entries_list)} content search results")
                
                if ctx:
                    await ctx.report_progress(1.0)
                
                if not entries_list:
                    return "0"
                
                result_text = f"Found {len(entries_list)} item(s) matching the search query:\n\n"
                
                for i, entry in enumerate(entries_list, 1):
                    # Debug: Log the entry structure
                    logger.debug(f"Entry {i} type: {type(entry)}, content: {entry}")
                    
                    # Handle different possible entry structures
                    node = None
                    if isinstance(entry, dict):
                        if 'entry' in entry:
                            node = entry['entry']
                        elif 'name' in entry:  # Direct node structure
                            node = entry
                        else:
                            logger.warning(f"Unknown entry structure: {entry}")
                            continue
                    elif hasattr(entry, 'entry'):  # ResultSetRowEntry object
                        node = entry.entry
                    else:
                        logger.warning(f"Entry is not a dict or ResultSetRowEntry: {type(entry)}")
                        continue
                    
                    if node:
                        # Handle both dict and ResultNode objects
                        if isinstance(node, dict):
                            name = str(node.get('name', 'Unknown'))
                            node_id = str(node.get('id', 'Unknown'))
                            node_type_actual = str(node.get('nodeType', 'Unknown'))
                            created_at = str(node.get('createdAt', 'Unknown'))
                        else:
                            # ResultNode object - access attributes directly
                            name = str(getattr(node, 'name', 'Unknown'))
                            node_id = str(getattr(node, 'id', 'Unknown'))
                            node_type_actual = str(getattr(node, 'node_type', 'Unknown'))
                            created_at = str(getattr(node, 'created_at', 'Unknown'))
                        
                        # Clean JSON-friendly formatting (no markdown syntax)
                        # Apply safe formatting to individual fields to prevent emoji encoding issues
                        safe_name = safe_format_output(name)
                        safe_node_id = safe_format_output(node_id)
                        safe_node_type = safe_format_output(node_type_actual)
                        safe_created_at = safe_format_output(created_at)
                        
                        result_text += f"{i}. {safe_name}\n"
                        result_text += f"   - ID: {safe_node_id}\n"
                        result_text += f"   - Type: {safe_node_type}\n"
                        result_text += f"   - Created: {safe_created_at}\n\n"
                
                return safe_format_output(result_text)
            else:
                return safe_format_output(f"ERROR: Content search failed - invalid response from Alfresco")
                
        except Exception as e:
            logger.error(f"Content search failed: {e}")
            return safe_format_output(f"ERROR: Content search failed: {str(e)}")
        
    except Exception as e:
        # Preserve Unicode characters in error messages
        error_msg = f"ERROR: Content search failed: {str(e)}"
        if ctx:
            await ctx.error(safe_format_output(error_msg))
        return safe_format_output(error_msg)

    if ctx:
        await ctx.info(safe_format_output("Content search completed!")) 
```

--------------------------------------------------------------------------------
/docs/mcp_inspector_setup.md:
--------------------------------------------------------------------------------

```markdown
# MCP Inspector Setup Guide

MCP Inspector is a debugging and testing tool for Model Context Protocol servers. This guide covers setup and connection for the Python Alfresco MCP Server.

## 🔍 MCP Inspector Overview

MCP Inspector provides:
- **Tool Testing**: Interactive testing of all 15 MCP tools
- **Resource Access**: View repository information and other resources
- **Protocol Debugging**: Monitor MCP protocol messages
- **Real-time Testing**: Live interaction with Alfresco server

## 🚀 Installation & Setup

### Prerequisites

1. **Node.js**: Required for running MCP Inspector
2. **Running MCP Server**: Your Python Alfresco MCP Server must be running
3. **Alfresco Server**: Live Alfresco instance for testing

### Method 1: Using Config File (Recommended)

This method uses the included configuration files and avoids proxy connection errors.

#### Step 1: Start MCP Server with HTTP Transport

**With UV (Recommended):**
```bash
uv run python-alfresco-mcp-server --transport http --port 8003
```

**Traditional Python:**
```bash
python -m alfresco_mcp_server.fastmcp_server --transport http --port 8003
```

#### Step 2: Start MCP Inspector

Use the included configuration file:
```bash
npx @modelcontextprotocol/inspector --config mcp-inspector-http-config.json --server python-alfresco-mcp-server
```

#### Step 3: Open Browser

MCP Inspector will provide a URL with pre-filled authentication token:
```
🔗 Open inspector with token pre-filled:
   http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=d7a62ab6e032eefe5d85e807c50e13b9fffcd12badbf8bbc3377659c0be4fa8d
```

### Method 2: Manual Connection

#### Step 1: Start MCP Inspector
```bash
npx @modelcontextprotocol/inspector
```

#### Step 2: Connect to Server
1. Open browser to `http://localhost:6274`
2. Click **"Add Server"** or use server connection field
3. Enter server URL: `http://localhost:8003`
4. Select transport: **HTTP**
5. Click **Connect**

## 📋 Configuration Files

The project includes pre-configured files for easy setup:

### HTTP Transport Configuration
**File**: `mcp-inspector-http-config.json`
```json
{
  "servers": {
    "python-alfresco-mcp-server": {
      "command": "uv",
      "args": ["run", "python-alfresco-mcp-server", "--transport", "http", "--port", "8003"],
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin"
      }
    }
  }
}
```

### STDIO Transport Configuration  
**File**: `mcp-inspector-stdio-config.json`
```json
{
  "servers": {
    "python-alfresco-mcp-server": {
      "command": "uv",
      "args": ["run", "python-alfresco-mcp-server"],
      "env": {
        "ALFRESCO_URL": "http://localhost:8080",
        "ALFRESCO_USERNAME": "admin",
        "ALFRESCO_PASSWORD": "admin"
      }
    }
  }
}
```

## 🧪 Testing Tools and Features

### Available Tools (15 Total)

Once connected, you can test all tools:

#### Search Tools (4)
- **search_content**: Full text search
- **advanced_search**: AFTS query language  
- **search_by_metadata**: Property-based queries
- **cmis_search**: CMIS SQL queries

#### Core Tools (11)
- **browse_repository**: Browse folders
- **repository_info**: Get system information
- **upload_document**: Upload files
- **download_document**: Download content
- **create_folder**: Create directories
- **get_node_properties**: View metadata
- **update_node_properties**: Modify metadata
- **delete_node**: Remove content
- **checkout_document**: Lock for editing
- **checkin_document**: Save changes
- **cancel_checkout**: Cancel editing

### Resources
- **repository_info**: Repository status and configuration

### Prompts
- **search_and_analyze**: Interactive search form

## 🔧 Usage Examples

### Basic Testing Workflow

1. **Start with Repository Info**:
   ```json
   Tool: repository_info
   Parameters: {} 
   ```

2. **Search for Content**:
   ```json
   Tool: search_content
   Parameters: {
     "query": "test",
     "max_results": 10
   }
   ```

3. **Browse Repository**:
   ```json
   Tool: browse_repository
   Parameters: {
     "node_id": "-root-"
   }
   ```

4. **Upload Test Document**:
   ```json
   Tool: upload_document
   Parameters: {
     "filename": "test.txt",
     "content_base64": "VGVzdCBjb250ZW50",
     "parent_id": "-root-",
     "description": "Test upload"
   }
   ```

### Advanced Testing

**CMIS SQL Search**:
```json
Tool: cmis_search
Parameters: {
  "cmis_query": "SELECT * FROM cmis:document WHERE cmis:name LIKE '%test%'",
  "max_results": 5
}
```

**Metadata Search**:
```json
Tool: search_by_metadata
Parameters: {
  "property_name": "cm:name",
  "property_value": "test",
  "comparison": "contains"
}
```

## 🛠️ Troubleshooting

### Common Issues

#### 1. "Cannot connect to server"
- **Check**: MCP server is running on correct port
- **Verify**: `curl http://localhost:8003/health` returns response
- **Solution**: Restart MCP server with HTTP transport

#### 2. "Proxy connection failed"
- **Use**: Config file method instead of manual connection
- **Check**: No other service using port 8003
- **Alternative**: Try different port: `--port 8004`

#### 3. "Authentication failed"
- **Check**: Environment variables are set correctly
- **Verify**: Alfresco server is accessible
- **Test**: `curl -u admin:admin http://localhost:8080/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-`

#### 4. "Port 6274 already in use"
- **Auto-resolution**: MCP Inspector finds next available port
- **Manual**: Check what port is actually used in startup message
- **Check**: `netstat -an | findstr :6274`

### Network Issues

**Check ports**:
```bash
# Windows
netstat -an | findstr ":6274\|:8003\|:8080"

# Linux/macOS  
netstat -an | grep ":6274\|:8003\|:8080"
```

**Test connectivity**:
```bash
# Test MCP server
curl http://localhost:8003

# Test Alfresco server
curl http://localhost:8080/alfresco
```

### Tool-Specific Issues

#### Search Returns No Results
- **Check**: Alfresco has content indexed
- **Verify**: Search service is running
- **Test**: Simple query like `*` to return all content

#### Upload Fails
- **Check**: Base64 encoding is correct
- **Verify**: Parent folder exists and is writable
- **Test**: Upload to Company Home (-root-)

#### Authentication Errors
- **Verify**: Username/password in environment variables
- **Check**: User has required permissions
- **Test**: Basic authentication with curl

## 📊 Expected Behavior

### Successful Connection
When properly connected, you should see:
- ✅ Green connection status
- 📊 List of 15 available tools
- 🔍 Repository resource available
- 🎯 Search and analyze prompt available

### Typical Response Times
- **Search operations**: < 1 second
- **Repository browsing**: < 500ms
- **Document upload**: 1-3 seconds
- **Download operations**: 1-2 seconds

## 🔗 Alternative Testing Methods

### Command Line Testing
Use the examples in [`examples/`](../examples/) for programmatic testing:
```bash
python examples/quick_start.py
python examples/document_lifecycle.py
```

### Integration Tests
Run the automated test suite:
```bash
pytest tests/ -m integration
```

### Manual Testing with Claude Desktop
See [`prompts-for-claude.md`](../prompts-for-claude.md) for 14 manual test scenarios.

## 📚 Additional Resources

- **[MCP Inspector Documentation](https://github.com/modelcontextprotocol/inspector)**: Official documentation
- **[API Reference](./api_reference.md)**: Complete tool documentation
- **[Troubleshooting Guide](./troubleshooting.md)**: Problem diagnosis
- **[Configuration Guide](./configuration_guide.md)**: Advanced setup options

## 🎯 Quick Start Summary

1. **Start MCP Server**: `uv run python-alfresco-mcp-server --transport http --port 8003`
2. **Start Inspector**: `npx @modelcontextprotocol/inspector --config mcp-inspector-http-config.json --server python-alfresco-mcp-server`
3. **Open URL**: Use the provided URL with pre-filled token
4. **Test Tools**: Start with `repository_info` then explore other tools

This provides a comprehensive testing environment for validating all Alfresco MCP server functionality! 
```
Page 1/3FirstPrevNextLast