#
tokens: 48087/50000 55/85 files (page 1/4)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 1 of 4. Use http://codebase.md/stevereiner/python-alfresco-mcp-server?lines=true&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:
--------------------------------------------------------------------------------

```
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 | 
```

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

```
  1 | # Byte-compiled / optimized / DLL files
  2 | __pycache__/
  3 | *.py[cod]
  4 | *$py.class
  5 | 
  6 | # C extensions
  7 | *.so
  8 | 
  9 | # Distribution / packaging
 10 | .Python
 11 | build/
 12 | develop-eggs/
 13 | dist/
 14 | downloads/
 15 | eggs/
 16 | .eggs/
 17 | lib/
 18 | lib64/
 19 | parts/
 20 | sdist/
 21 | var/
 22 | wheels/
 23 | share/python-wheels/
 24 | *.egg-info/
 25 | .installed.cfg
 26 | *.egg
 27 | MANIFEST
 28 | 
 29 | # PyInstaller
 30 | #  Usually these files are written by a python script from a template
 31 | #  before PyInstaller builds the exe, so as to inject date/other infos into it.
 32 | *.manifest
 33 | *.spec
 34 | 
 35 | # Installer logs
 36 | pip-log.txt
 37 | pip-delete-this-directory.txt
 38 | 
 39 | # Unit test / coverage reports
 40 | htmlcov/
 41 | .tox/
 42 | .nox/
 43 | .coverage
 44 | .coverage.*
 45 | .cache
 46 | nosetests.xml
 47 | coverage.xml
 48 | *.cover
 49 | *.py,cover
 50 | .hypothesis/
 51 | .pytest_cache/
 52 | cover/
 53 | 
 54 | # Additional testing framework artifacts
 55 | .pytest_cache/
 56 | pytest.ini.bak
 57 | test-results/
 58 | test-reports/
 59 | .testmondata
 60 | junit.xml
 61 | report.xml
 62 | 
 63 | # Translations
 64 | *.mo
 65 | *.pot
 66 | 
 67 | # Django stuff:
 68 | *.log
 69 | local_settings.py
 70 | db.sqlite3
 71 | db.sqlite3-journal
 72 | 
 73 | # Flask stuff:
 74 | instance/
 75 | .webassets-cache
 76 | 
 77 | # Scrapy stuff:
 78 | .scrapy
 79 | 
 80 | # Sphinx documentation
 81 | docs/_build/
 82 | 
 83 | # PyBuilder
 84 | .pybuilder/
 85 | target/
 86 | 
 87 | # Jupyter Notebook
 88 | .ipynb_checkpoints
 89 | 
 90 | # IPython
 91 | profile_default/
 92 | ipython_config.py
 93 | 
 94 | # pyenv
 95 | #   For a library or package, you might want to ignore these files since the code is
 96 | #   intended to run in multiple environments; otherwise, check them in:
 97 | # .python-version
 98 | 
 99 | # pipenv
100 | #   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
101 | #   However, in case of collaboration, if having platform-specific dependencies or dependencies
102 | #   having no cross-platform support, pipenv may install dependencies that don't work, or not
103 | #   install all needed dependencies.
104 | #Pipfile.lock
105 | 
106 | # UV
107 | #   Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
108 | #   This is especially recommended for binary packages to ensure reproducibility, and is more
109 | #   commonly ignored for libraries.
110 | #uv.lock
111 | 
112 | # poetry
113 | #   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
114 | #   This is especially recommended for binary packages to ensure reproducibility, and is more
115 | #   commonly ignored for libraries.
116 | #   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
117 | #poetry.lock
118 | 
119 | # pdm
120 | #   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
121 | #pdm.lock
122 | #   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
123 | #   in version control.
124 | #   https://pdm.fming.dev/latest/usage/project/#working-with-version-control
125 | .pdm.toml
126 | .pdm-python
127 | .pdm-build/
128 | 
129 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
130 | __pypackages__/
131 | 
132 | # Celery stuff
133 | celerybeat-schedule
134 | celerybeat.pid
135 | 
136 | # SageMath parsed files
137 | *.sage.py
138 | 
139 | # Environments
140 | .env
141 | .venv
142 | env/
143 | venv/
144 | ENV/
145 | env.bak/
146 | venv.bak/
147 | 
148 | # Spyder project settings
149 | .spyderproject
150 | .spyproject
151 | 
152 | # Rope project settings
153 | .ropeproject
154 | 
155 | # mkdocs documentation
156 | /site
157 | 
158 | # mypy
159 | .mypy_cache/
160 | .dmypy.json
161 | dmypy.json
162 | 
163 | # Pyre type checker
164 | .pyre/
165 | 
166 | # pytype static type analyzer
167 | .pytype/
168 | 
169 | # Cython debug symbols
170 | cython_debug/
171 | 
172 | # PyCharm
173 | #  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
174 | #  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
175 | #  and can be added to the global gitignore or merged into this file.  For a more nuclear
176 | #  option (not recommended) you can uncomment the following to ignore the entire idea folder.
177 | #.idea/
178 | 
179 | # VS Code
180 | .vscode/settings.json
181 | .vscode/launch.json
182 | .vscode/extensions.json
183 | .vscode/tasks.json
184 | .vscode/.ropeproject
185 | # Keep workspace settings but ignore personal settings
186 | !.vscode/settings.json.template
187 | !.vscode/mcp.json
188 | 
189 | # Ruff stuff:
190 | .ruff_cache/
191 | 
192 | # PyPI configuration file
193 | .pypirc
194 | 
195 | # Cursor  
196 | #  Cursor is an AI-powered code editor.`.cursorignore` specifies files/directories to 
197 | #  exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
198 | #  refer to https://docs.cursor.com/context/ignore-files
199 | .cursorignore
200 | .cursorindexingignore
201 | *.cursormemory
202 | .cursormemory
203 | memory-bank/
204 | 
205 | # FastMCP 2.0 and MCP Development
206 | # MCP client/server communication logs
207 | *.mcp.log
208 | mcp_*.log
209 | fastmcp_*.log
210 | 
211 | # MCP development artifacts
212 | .mcp_cache/
213 | mcp_debug/
214 | .fastmcp/
215 | 
216 | # Alfresco MCP Server specific
217 | alfresco_mcp_server.log*
218 | alfresco_*.log
219 | connection_test.py
220 | debug_*.py
221 | 
222 | # Development and testing artifacts
223 | .benchmarks/
224 | performance_results/
225 | integration_test_results/
226 | mock_data/
227 | test_uploads/
228 | test_downloads/
229 | 
230 | # Configuration files with secrets (keep templates)
231 | config.local.yaml
232 | config.dev.yaml
233 | config.prod.yaml
234 | .env.local
235 | .env.dev
236 | .env.prod
237 | # Keep sample config files
238 | !sample-*.txt
239 | !config.sample.*
240 | !.env.sample
241 | 
242 | # Experimental/scratch files
243 | o.txt
244 | *.tmp
245 | test_*.py
246 | *_test.py
247 | scratch*
248 | temp*
249 | debug_output.txt
250 | output.txt
```

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

```markdown
 1 | # Debug Test Files
 2 | 
 3 | This directory contains one-off debug and development test files that are not part of the main testing suite.
 4 | 
 5 | These files were moved from the root directory to clean up the project structure:
 6 | 
 7 | - `test_additional_clients.py` - Testing additional client imports
 8 | - `test_fixed_auth.py` - Authentication testing
 9 | - `test_mcp_direct.py` - Direct MCP testing
10 | - `test_mcp_tools.py` - MCP tools testing  
11 | - `test_sse_endpoint.py` - SSE endpoint testing
12 | - `test_update_debug.py` - Update debugging
13 | - `test_update_direct.py` - Direct update testing
14 | 
15 | These are development/debugging tools and not included in the main test suite in `/tests/`. 
```

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

```markdown
 1 | # Testing Structure
 2 | 
 3 | This directory contains tests organized by their purpose.
 4 | 
 5 | ## MCP Server Tests
 6 | - `test_*.py` files in the root test the MCP server functionality
 7 | - `mcp_specific/` contains files specifically for testing MCP server deployment
 8 | 
 9 | ### Main Test Files (MCP Server Functionality)
10 | - `test_unit_tools.py` - Unit tests for MCP tools
11 | - `test_integration.py` - Integration tests with Alfresco
12 | - `test_fastmcp_2_0.py` - FastMCP 2.0 specific tests
13 | - `test_coverage.py` - Test coverage validation
14 | - `test_authentication.py` - Authentication tests
15 | - `test_search_debug.py` - Search functionality debugging
16 | - `test_simple_search.py` - Simple search tests
17 | - `test_response_structure.py` - Response structure validation
18 | - `test_comprehensive_scenarios.py` - Comprehensive scenario testing
19 | 
20 | ### MCP Specific (Deployment & Inspector)
21 | - `mcp_specific/test_with_mcp_client.py` - MCP client testing
22 | - `mcp_specific/test_http_server.ps1` - HTTP server testing script
23 | - `mcp_specific/mcp_testing_guide.md` - Testing guide
24 | - `mcp_specific/test_with_mcp_inspector.md` - MCP Inspector testing
25 | - `mcp_specific/test_server_status.py` - Server status check
26 | 
27 | ## Running Tests
28 | 
29 | ### Unit Tests
30 | ```bash
31 | python -m pytest tests/test_unit_tools.py -v
32 | ```
33 | 
34 | ### Integration Tests (requires live Alfresco)
35 | ```bash
36 | python -m pytest tests/test_integration.py -v
37 | ```
38 | 
39 | ### MCP Server Status
40 | ```bash
41 | python tests/mcp_specific/test_server_status.py
42 | ```
43 | 
44 | ## Testing with MCP Inspector
45 | 1. Start HTTP server: `fastmcp run alfresco_mcp_server.fastmcp_server --host localhost --port 8003`
46 | 2. Use MCP Inspector with URL: `http://localhost:8003`
47 | 3. Note: MCP Inspector shows warning about risky auth and includes token in URL
48 | 
49 | ## Testing with Claude Desktop
50 | 1. Update `claude-desktop-config.json` with current paths
51 | 2. Restart Claude Desktop application
52 | 3. Test MCP tools directly in Claude conversation 
```

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

```markdown
 1 | # Alfresco MCP Server Documentation
 2 | 
 3 | 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.
 4 | 
 5 | ## 📚 Documentation Index
 6 | 
 7 | ### 🚀 Getting Started
 8 | - [`quick_start_guide.md`](quick_start_guide.md) - Get up and running in 5 minutes
 9 | - [`configuration_guide.md`](configuration_guide.md) - Configuration options and setup
10 | 
11 | ### 🔧 Technical Guides
12 | - [`api_reference.md`](api_reference.md) - Complete API reference for all 15 tools
13 | 
14 | ### 🏗️ Development & Testing
15 | - [`testing_guide.md`](testing_guide.md) - Running tests and validation
16 | 
17 | ### 🆘 Support & Troubleshooting
18 | - [`troubleshooting.md`](troubleshooting.md) - Common issues and solutions
19 | 
20 | ## 🎯 Quick Navigation
21 | 
22 | | I want to... | Read this |
23 | |--------------|-----------|
24 | | **Get started quickly** | [`quick_start_guide.md`](quick_start_guide.md) |
25 | | **Configure my setup** | [`configuration_guide.md`](configuration_guide.md) |
26 | | **Learn about all tools** | [`api_reference.md`](api_reference.md) |
27 | | **Run tests** | [`testing_guide.md`](testing_guide.md) |
28 | | **Troubleshoot issues** | [`troubleshooting.md`](troubleshooting.md) |
29 | 
30 | ## 📖 Documentation Standards
31 | 
32 | All documentation in this directory follows these standards:
33 | 
34 | - ✅ **Clear Examples**: Every feature includes working code examples
35 | - ✅ **Step-by-Step**: Complex procedures broken into manageable steps
36 | - ✅ **Cross-Referenced**: Related topics linked throughout
37 | - ✅ **Tested**: All code examples are tested and verified
38 | - ✅ **Version Aware**: Marked with applicable version information
39 | 
40 | ## 🔄 Recent Updates
41 | 
42 | - **v1.1.0**: Updated tool count and improved documentation
43 | - **v1.1.0**: Corrected tool count to 15 (4 search + 11 core tools)
44 | - **v1.1.0**: Removed marketing language for professional tone
45 | - **v1.0.0**: Initial production release with FastMCP 2.0
46 | 
47 | ## 🤝 Contributing to Documentation
48 | 
49 | Found an error or want to improve the docs? Create an issue or pull request on GitHub.
50 | 
51 | ## 📞 Need Help?
52 | 
53 | - 🔍 Browse [`troubleshooting.md`](troubleshooting.md) for solutions
54 | - 🐛 Report issues via GitHub Issues
55 | 
56 | ---
57 | 
58 | **📌 Pro Tip**: Bookmark this page and use it as your central hub for all Alfresco MCP Server documentation needs! 
```

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

```markdown
 1 | # Alfresco MCP Server Examples
 2 | 
 3 | 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.
 4 | 
 5 | ## 📋 Available Examples
 6 | 
 7 | ### 🚀 Quick Start Examples
 8 | - [`quick_start.py`](quick_start.py) - Basic server setup and first tool call
 9 | 
10 | ### 🔧 Transport Examples  
11 | - [`transport_examples.py`](transport_examples.py) - All transport protocols (STDIO, HTTP, SSE)
12 | 
13 | ### 🛠️ Tool Usage Examples
14 | - [`document_lifecycle.py`](document_lifecycle.py) - Complete document management workflow
15 | - [`batch_operations.py`](batch_operations.py) - Bulk document processing
16 | 
17 | ### 📊 Additional Examples
18 | - [`error_handling.py`](error_handling.py) - Error handling patterns
19 | 
20 | ### 📖 Documentation Summary
21 | - [`examples_summary.md`](examples_summary.md) - Overview of all examples and documentation
22 | 
23 | ## 🎯 Getting Started
24 | 
25 | 1. **Install Dependencies**: Ensure you have the server installed
26 | 2. **Set Up Alfresco**: Configure your Alfresco connection
27 | 3. **Run Examples**: Each example is self-contained and well-documented
28 | 
29 | ## 🔧 Prerequisites
30 | 
31 | ```bash
32 | # Install the package
33 | pip install -e .
34 | 
35 | # Set environment variables
36 | export ALFRESCO_URL="http://localhost:8080"
37 | export ALFRESCO_USERNAME="admin"  
38 | export ALFRESCO_PASSWORD="admin"
39 | ```
40 | 
41 | ## 📖 Example Structure
42 | 
43 | Each example includes:
44 | - ✅ **Clear documentation** of what it demonstrates
45 | - ✅ **Step-by-step comments** explaining each operation
46 | - ✅ **Error handling** best practices
47 | - ✅ **Expected output** descriptions
48 | - ✅ **Practical use cases** and scenarios
49 | 
50 | ## 🚀 Running Examples
51 | 
52 | ```bash
53 | # Quick start example
54 | python examples/quick_start.py
55 | 
56 | # Document lifecycle workflow
57 | python examples/document_lifecycle.py
58 | 
59 | # Transport protocols demonstration
60 | python examples/transport_examples.py
61 | 
62 | # Batch operations and performance
63 | python examples/batch_operations.py
64 | 
65 | # Error handling patterns
66 | python examples/error_handling.py
67 | ```
68 | 
69 | ## 💡 Tips
70 | 
71 | - Start with `quick_start.py` for your first experience
72 | - Check `error_handling.py` for production-ready patterns
73 | - Use `batch_operations.py` for performance optimization insights
74 | - Explore `transport_examples.py` for different connection methods
75 | - Review `examples_summary.md` for documentation overview 
```

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

```markdown
  1 | # Python Alfresco MCP Server v1.1 🚀
  2 | 
  3 | [![PyPI version](https://img.shields.io/pypi/v/python-alfresco-mcp-server)](https://pypi.org/project/python-alfresco-mcp-server/)
  4 | [![PyPI downloads](https://pepy.tech/badge/python-alfresco-mcp-server)](https://pepy.tech/project/python-alfresco-mcp-server)
  5 | [![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://pypi.org/project/python-alfresco-mcp-server/)
  6 | [![License](https://img.shields.io/github/license/stevereiner/python-alfresco-mcp-server)](https://github.com/stevereiner/python-alfresco-mcp-server/blob/main/LICENSE)
  7 | 
  8 | **Model Context Protocol Server for Alfresco Content Services**
  9 | 
 10 | 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,
 11 | 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.
 12 | Built with [FastMCP 2.0](https://github.com/jlowin/FastMCP). 
 13 | Features complete documentation, examples, and 
 14 | config for various MCP clients (Claude Desktop, MCP Inspector, references to configuring others).
 15 | 
 16 | ## 🌟 What's New in v1.1
 17 | 
 18 | ### **Modular Architecture & Enhanced Testing**
 19 | - **FastMCP**: v1.0 had FastMCP 2.0 implementation that had all tools implementations in the fastmcp_server.py file
 20 | - **Code Modularization in v1.1**: Split monolithic single file into organized modular structure with separate files
 21 | - **Directory Organization**: Organized into `tools/search/`, `tools/core/`, `resources/`, `prompts/`, `utils/` directories
 22 | - **Enhanced Testing**: Complete test suite transformation - 143 tests with 100% pass rate
 23 | - **Client Configuration Files**: Added dedicated Claude Desktop and MCP Inspector configuration files
 24 | - **Live Integration Testing**: 21 Alfresco server validation tests for real-world functionality
 25 | - **Python-Alfresco-API**:  python-alfresco-mcp-server v1.1  requires the v1.1.1 python-alfresco-api package
 26 | 
 27 | ## 📚 Complete Documentation
 28 | 
 29 | ### **Documentation & Examples**
 30 | - **📚 Complete Documentation**: 10 guides covering setup to deployment
 31 | - **💡 Examples**: 6 practical examples from quick start to implementation patterns  
 32 | - **🔧 Configuration Management**: Environment variables, .env files, and command-line configuration
 33 | - **🏗️ Setup instruction for use with MCP client
 34 | 
 35 | ### **Learning Resources**
 36 | - **🚀 [Quick Start Guide](./docs/quick_start_guide.md)**: 5-minute setup and first operations
 37 | - **🤖 [Claude Desktop Setup](./docs/claude_desktop_setup.md)**: Complete Claude Desktop configuration for users and developers
 38 | - **🔧 [Client Configurations](./docs/client_configurations.md)**: Setup guide for Cursor, Claude Code, and other MCP clients
 39 | - **📖 [Examples Library](./examples/README.md)**: Implementation patterns and examples
 40 | 
 41 | ### 📖 Guides covering setup, deployment, and usage:
 42 | 
 43 | - **[📚 Documentation Hub](./docs/README.md)** - Complete navigation and overview
 44 | - **[🚀 Quick Start Guide](./docs/quick_start_guide.md)** - 5-minute setup and first operations
 45 | - **[📦 Installation with pip and pipx](./docs/install_with_pip_pipx.md)** - Traditional Python package installation methods
 46 | - **[🤖 Claude Desktop Setup](./docs/claude_desktop_setup.md)** - Complete Claude Desktop configuration for users and developers
 47 | - **[🔧 Client Configurations](./docs/client_configurations.md)** - Setup guide for Cursor, Claude Code, and other MCP clients
 48 | - **[🔍 MCP Inspector Setup](./docs/mcp_inspector_setup.md)** - Development and testing with MCP Inspector
 49 | - **[🔍 API Reference](./docs/api_reference.md)** - Complete tool and resource documentation
 50 | - **[⚙️ Configuration Guide](./docs/configuration_guide.md)** - Development to deployment
 51 | - **[🧪 Testing Guide](./docs/testing_guide.md)** - Quality assurance and test development
 52 | - **[🛠️ Troubleshooting Guide](./docs/troubleshooting.md)** - Problem diagnosis and resolution
 53 | 
 54 | ## 🚀 Features
 55 | 
 56 | ### Content Management and Search Tools
 57 | - **Search Tools**: 
 58 |   - **Full Text Search**: Basic content search with wildcard support (search_content)
 59 |   - **Advanced Search**: AFTS query language with date filters, sorting, and field targeting
 60 |   - **Metadata Search**: Property-based queries with operators (equals, contains, date ranges)
 61 |   - **CMIS Search**: SQL like queries for complex content discovery
 62 | - **Document Lifecycle**: Upload, download, check-in, checkout, cancel checkout
 63 | - **Version Management**: Create major/minor versions with comments
 64 | - **Folder Operations**: Create folders, delete folder nodes
 65 | - **Property Management**: Get and set document/folder properties and names
 66 | - **Node Operations**: Delete nodes (documents and folders) (trash or permanent)
 67 | - **Repository Info**: (Tool and Resource) Returns repository status, version and whether Community or Enterprise, and module configuration
 68 | 
 69 | ### MCP Architecture
 70 | - **FastMCP 2.0 Framework**: Modern, high-performance MCP server implementation
 71 | - **Multiple Transports**: 
 72 |   - **STDIO** (direct MCP protocol) - Default and fastest
 73 |   - **HTTP** (RESTful API) - Web services and testing
 74 |   - **SSE** (Server-Sent Events) - Real-time streaming updates
 75 | - **Enterprise Security**: OAuth 2.1  (optional)
 76 | - **Type Safety**: Full Pydantic v2 models
 77 | - **In-Memory Testing**: Client testing with faster execution
 78 | - **Configuration**: Environment variables, .env files
 79 | 
 80 | ### Alfresco Integration 
 81 | Works with Alfresco Community (tested) and Enterprise editions
 82 | 
 83 | 
 84 | ## 📋 Requirements
 85 | 
 86 | - Python 3.10+
 87 | - Alfresco Content Services (Community or Enterprise)
 88 | 
 89 | > **Note**: The `python-alfresco-api >= 1.1.1` dependency is automatically installed with `python-alfresco-mcp-server`
 90 | 
 91 | ## 🛠️ Installation
 92 | 
 93 | ### Install Python
 94 | 
 95 | You need to have Python 3.10+ installed for the sections below. If not, download the latest 3.13.x version from:
 96 | 
 97 | [Python.org Downloads](https://www.python.org/downloads/)
 98 | 
 99 | ### UV/UVX Setup (Recommended)
100 | 
101 | **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.
102 | 
103 | ```bash
104 | # Install UV (provides both uv and uvx commands)
105 | # Windows
106 | powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
107 | 
108 | # macOS/Linux  
109 | curl -LsSf https://astral.sh/uv/install.sh | sh
110 | 
111 | # Or via pip if you prefer
112 | pip install uv
113 | 
114 | # Verify installation (both commands should work)
115 | uv --version
116 | uvx --version
117 | ```
118 | 
119 | **UV Reference Links:**
120 | - **[UV Installation Guide](https://docs.astral.sh/uv/getting-started/installation/)** - Official installation instructions and platform-specific options
121 | - **[UV Documentation](https://docs.astral.sh/uv/)** - Complete UV documentation, guides, and advanced usage
122 | 
123 | ### Option A: UVX - Modern Tool Runner (Recommended for Users)
124 | 
125 | **UVX** is UV's tool runner - similar to pipx but faster and more modern. Automatically handles isolation and global availability:
126 | 
127 | ```bash
128 | # Install python-alfresco-mcp-server with uvx (after UV/UVX setup above)
129 | uvx python-alfresco-mcp-server --help
130 | 
131 | # This tests that installation worked - UVX automatically installs packages on first use!
132 | ```
133 | 
134 | **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.
135 | 
136 | ### Option B: UV - Modern Package Manager (Recommended for Development)
137 | 
138 | **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.
139 | 
140 | ```bash
141 | # Install and run from PyPI (fastest for users)
142 | uv tool install python-alfresco-mcp-server
143 | uv tool run python-alfresco-mcp-server --help  # Tests that installation worked
144 | 
145 | # Or install from source (for development)
146 | git clone https://github.com/stevereiner/python-alfresco-mcp-server.git
147 | cd python-alfresco-mcp-server
148 | uv run python-alfresco-mcp-server --help  # Tests that installation worked
149 | ```
150 | 
151 | ### Option C: Traditional Methods (pip and pipx)
152 | 
153 | For traditional Python package management approaches, see the **[Installation with pip and pipx](./docs/install_with_pip_pipx.md)**.
154 | 
155 | **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.
156 | 
157 | ### Source Installation (For Development)
158 | 
159 | For development or access to latest features:
160 | 
161 | ```bash
162 | # 1. Clone the repository
163 | git clone https://github.com/stevereiner/python-alfresco-mcp-server.git
164 | cd python-alfresco-mcp-server
165 | 
166 | # 2. UV handles everything automatically - run immediately!
167 | uv run python-alfresco-mcp-server --help  # Tests that installation worked
168 | 
169 | # Or install dependencies explicitly for development:
170 | uv sync                    # Basic dependencies
171 | uv sync --extra dev        # With development tools  
172 | uv sync --extra test       # With testing tools
173 | uv sync --extra all        # Everything
174 | ```
175 | 
176 | ### 4. Configure Alfresco Connection
177 | 
178 | **Option 1: Environment Variables**
179 | ```bash
180 | # Linux/Mac
181 | export ALFRESCO_URL="http://localhost:8080"
182 | export ALFRESCO_USERNAME="admin"
183 | export ALFRESCO_PASSWORD="admin"
184 | export ALFRESCO_VERIFY_SSL="false"
185 | 
186 | # Windows PowerShell
187 | $env:ALFRESCO_URL="http://localhost:8080"
188 | $env:ALFRESCO_USERNAME="admin"
189 | $env:ALFRESCO_PASSWORD="admin"
190 | $env:ALFRESCO_VERIFY_SSL="false"
191 | 
192 | # Windows Command Prompt
193 | set ALFRESCO_URL=http://localhost:8080
194 | set ALFRESCO_USERNAME=admin
195 | set ALFRESCO_PASSWORD=admin
196 | set ALFRESCO_VERIFY_SSL=false
197 | ```
198 | 
199 | **Option 2: .env file** (recommended - cross-platform):
200 | ```bash
201 | # Copy sample-dot-env.txt to .env and customize
202 | # Linux/macOS
203 | cp sample-dot-env.txt .env
204 | 
205 | # Windows
206 | copy sample-dot-env.txt .env
207 | 
208 | # Edit .env file with your settings
209 | ALFRESCO_URL=http://localhost:8080
210 | ALFRESCO_USERNAME=admin
211 | ALFRESCO_PASSWORD=admin
212 | ALFRESCO_VERIFY_SSL=false
213 | ```
214 | > **Note**: The `.env` file is not checked into git for security. Use `sample-dot-env.txt` as a template.
215 | 
216 | 📖 **See [Configuration Guide](./docs/configuration_guide.md) for complete setup options**
217 | 
218 | ## Alfresco Installation 
219 | 
220 | If you don't have an Alfresco server installed you can get a docker for the 
221 | Community version from Github
222 | 
223 | ```bash
224 | git clone https://github.com/Alfresco/acs-deployment.git
225 | ```
226 | 
227 | **Move to Docker Compose directory**
228 | 
229 | ```bash
230 | cd acs-deployment/docker-compose
231 | ```
232 | 
233 | **Edit community-compose.yaml**
234 | - Note: you will likely need to comment out activemq ports other than 8161
235 | 
236 | ```bash   
237 |    ports:
238 |    - "8161:8161" # Web Console
239 |    #- "5672:5672" # AMQP
240 |    #- "61616:61616" # OpenWire
241 |    #- "61613:61613" # STOMP
242 | ```      
243 | 
244 | **Start Alfresco with Docker Compose**
245 | 
246 | ```bash
247 | docker-compose -f community-compose.yaml up
248 | ```
249 | 
250 | ## 🚀 Usage
251 | 
252 | ### MCP Server Startup
253 | 
254 | **With UVX (Recommended - Automatic isolation and global availability):**
255 | 
256 | ```bash
257 | # Run MCP server with STDIO transport (default)
258 | uvx python-alfresco-mcp-server
259 | 
260 | # HTTP transport for web services (matches MCP Inspector)
261 | uvx python-alfresco-mcp-server --transport http --host 127.0.0.1 --port 8003
262 | 
263 | # SSE transport for real-time streaming  
264 | uvx python-alfresco-mcp-server --transport sse --host 127.0.0.1 --port 8001
265 | ```
266 | 
267 | **With UV (For development or source installations):**
268 | 
269 | ```bash
270 | # Run MCP server with STDIO transport (default)
271 | uv run python-alfresco-mcp-server
272 | 
273 | # HTTP transport for web services (matches MCP Inspector)
274 | uv run python-alfresco-mcp-server --transport http --host 127.0.0.1 --port 8003
275 | 
276 | # SSE transport for real-time streaming  
277 | uv run python-alfresco-mcp-server --transport sse --host 127.0.0.1 --port 8001
278 | ```
279 | 
280 | **With Traditional Methods (pip/pipx):**
281 | 
282 | See the **[Installation with pip and pipx](./docs/install_with_pip_pipx.md)** for pip and pipx usage instructions.
283 | 
284 | ### MCP Client Setup and Use
285 | 
286 | 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.
287 | 
288 | #### 🤖 **Claude Desktop** for Windows (tested) and MacOS (not tested)
289 | 
290 | 📖 **Complete Setup Guide**: **[Claude Desktop Setup Guide](./docs/claude_desktop_setup.md)**
291 | 
292 | **📥 Download Claude Desktop (Free and Pro versions):**
293 | - **[Download Claude Desktop](https://claude.ai/download)** - Official Anthropic download page
294 | - Available for **Windows** and **macOS** only (no Linux version)
295 | - **Free tier** includes full MCP support and Claude Sonnet 4 access with limits, older Claude models
296 | (Claude Opus 4 only in Pro)
297 | 
298 | 
299 | **🔧 Claude Desktop Configuration by Installation Method:**
300 | 
301 | The Claude Desktop configuration differs based on how you installed the MCP server:
302 | 
303 | **1. UVX (Recommended - Modern tool runner):**
304 | ```json
305 | {
306 |   "command": "uvx",
307 |   "args": ["python-alfresco-mcp-server", "--transport", "stdio"]
308 | }
309 | ```
310 | - **Sample Config Files:**
311 |   - Windows: [`claude-desktop-config-uvx-windows.json`](./claude-desktop-config-uvx-windows.json)
312 |   - macOS: [`claude-desktop-config-uvx-macos.json`](./claude-desktop-config-uvx-macos.json)
313 | - UVX automatically handles isolation and global availability
314 | - Fastest and most modern approach
315 | 
316 | **2. UV (Development or source installations):**
317 | ```json
318 | {
319 |   "command": "uv",
320 |   "args": ["run", "python-alfresco-mcp-server", "--transport", "stdio"],
321 |   "cwd": "C:\\path\\to\\python-alfresco-mcp-server"
322 | }
323 | ```
324 | - **Sample Config Files:**
325 |   - Windows: [`claude-desktop-config-uv-windows.json`](./claude-desktop-config-uv-windows.json)
326 |   - macOS: [`claude-desktop-config-uv-macos.json`](./claude-desktop-config-uv-macos.json)
327 | - Uses `uv run` with `cwd` pointing to your **project directory**
328 | - UV automatically finds and uses the `.venv` from the project directory
329 | - Works for both source installations and after `uv tool install`
330 | 
331 | **3. Traditional Methods (pipx/pip):**
332 | 
333 | For traditional installation methods, see the **[Installation with pip and pipx](./docs/install_with_pip_pipx.md)** which covers:
334 | - **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)
335 | - **pip**: Manual venv path configuration
336 | 
337 | **🔐 Tool-by-Tool Permission System:**
338 | 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:
339 | - **"Allow once"** - Approve this single tool use only
340 | - **"Always allow"** - Approve all future uses of this specific tool automatically (recommended for regular use)
341 | 
342 | This tool-by-tool security feature ensures you maintain granular control over which external tools can be executed.
343 | 
344 | > **🛡️ 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.
345 | 
346 | **Using the Tools:**
347 | 
348 | - **Chat naturally** about what you want to do with documents and search
349 | - **Mention "Alfresco"** to ensure the MCP server is used (e.g., "In Alfresco...")
350 | - **Use tool-related keywords** - mention something close to the tool name 
351 | - **Follow-up prompts** will know the document from previous context
352 | 
353 | **Example 1: Document Management**
354 | 
355 | 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'"
356 | 2. Update properties: "Set the description property of this document to 'my desc'"
357 | 3. Check out the document
358 | 4. Cancel checkout
359 | 5. Check out again  
360 | 6. Check in as a major version
361 | 7. Download the document
362 | 8. Upload a second document from "C:\1 sample files\cmispress.pdf"
363 | 
364 | > **Note**: Claude will figure out to use base64 encoding for the first upload on a second try
365 | 
366 | **Example 2: Search Operations**
367 | 
368 | "With Alfresco please test all 3 search methods and CMIS query:"
369 | - Basic search for "txt" documents, return max 10
370 | - Advanced search for documents created after 2024-01-01, return max 25
371 | - Metadata search for documents where cm:title contains "test", limit to 50  
372 | - CMIS search to find all txt documents, limit to 50
373 | 
374 | **More Examples: Create Folder, Browse Folders, Get Repository Info**
375 | 
376 | - "Create a folder called '25 07 25 01 18 am' in shared folder"
377 | - "List docs and folders in shared folder" *(will use -shared-)*
378 | - "Can you show me what's in my Alfresco home directory?" *(will use browse_repository -my-)*
379 | - "Get info on Alfresco" *(will use repository_info tool)*
380 | 
381 | **Chat Box Buttons**
382 | 
383 | - 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
384 | 
385 | - Click the **+ Button** → "Add from alfresco" for quick access to resources and prompts
386 | 
387 | **Search and Analyze Prompt:**
388 | - Provides a form with query field for full-text search
389 | - Analysis types: **summary**, **detailed**, **trends**, or **compliance**
390 | - **Generates template text** to copy/paste into chat for editing
391 | 
392 | **Repository Info Resource (and Tool):**
393 | - Provides status information in text format for viewing or copying
394 | 
395 | **Examples:**
396 | - See [`prompts-for-claude.md`](./prompts-for-claude.md) for examples testing the tools
397 | 
398 | 
399 | #### 🔍 **MCP Inspector** (Development/Testing)
400 | 
401 | > 📖 **Setup Guide**: Complete MCP Inspector setup and connection instructions in [MCP Inspector Setup Guide](./docs/mcp_inspector_setup.md)
402 | 
403 | **📥 Install MCP Inspector:**
404 | - **Prerequisites**: Requires **Node.js 18+** - Download from **[nodejs.org](https://nodejs.org/)**
405 | - **Install Command**: `npm install -g @modelcontextprotocol/inspector`
406 | - **Or run directly**: `npx @modelcontextprotocol/inspector` (no global install needed)
407 | - **Purpose**: Web-based tool for testing MCP servers and individual tools with custom parameters
408 | 
409 | **Working Method (Recommended):**
410 | 
411 | **1. Start MCP Server with HTTP transport:**
412 | 
413 |    ```bash
414 |    # With UVX (recommended)
415 |    uvx python-alfresco-mcp-server --transport http --port 8003
416 | 
417 |    # With UV (development)
418 |    uv run python-alfresco-mcp-server --transport http --port 8003
419 | 
420 |    # Traditional methods - see Traditional Installation Guide
421 |    ```
422 | 
423 | **2. Start MCP Inspector with config:**
424 | 
425 |    **UVX Installation (Recommended):**
426 |    ```bash
427 |    # Start with stdio transport
428 |    npx @modelcontextprotocol/inspector --config mcp-inspector-stdio-uvx-config.json --server python-alfresco-mcp-server
429 | 
430 |    # Start with http transport  
431 |    npx @modelcontextprotocol/inspector --config mcp-inspector-http-uvx-config.json --server python-alfresco-mcp-server
432 |    ```
433 | 
434 |    **UV Installation (Development):**
435 |    ```bash
436 |    # From project directory where config files exist
437 |    npx @modelcontextprotocol/inspector --config mcp-inspector-stdio-uv-config.json --server python-alfresco-mcp-server  # stdio transport
438 |    npx @modelcontextprotocol/inspector --config mcp-inspector-http-uv-config.json --server python-alfresco-mcp-server   # http transport
439 |    ```
440 | 
441 |    **Traditional Methods (pipx/pip):**
442 | 
443 |    See the **[Installation with pip and pipx](./docs/install_with_pip_pipx.md)** for pipx and pip configuration options.
444 | 
445 | **3. Open browser with pre-filled token:**
446 | 
447 |    - Use the URL provided in the output (includes authentication token)
448 |    - Example: `http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=<token>`
449 |    - This step applies to **all installation methods** (uv, uvx, pip, pipx)
450 | 
451 | This approach avoids proxy connection errors and provides direct authentication.
452 | 
453 | 
454 | #### 🔧 **Other MCP Clients**
455 | 
456 | For Cursor, Claude Code, and other MCP clients:
457 | 
458 | 📖 **Complete Setup Guide**: **[Client Configuration Guide](./docs/client_configurations.md)**
459 | 
460 | 
461 | ## 🛠️ Available Tools (15 Total)
462 | 
463 | ### 🔍 Search Tools (4)
464 | | Tool | Description | Parameters |
465 | |------|-------------|------------|
466 | | `search_content` | Search documents and folders | `query` (str), `max_results` (int), `node_type` (str) |
467 | | `advanced_search` | Advanced search with filters | `query` (str), `content_type` (str), `created_after` (str), etc. |
468 | | `search_by_metadata` | Search by metadata properties | `property_name` (str), `property_value` (str), `comparison` (str) |
469 | | `cmis_search` | CMIS SQL queries | `cmis_query` (str), `preset` (str), `max_results` (int) |
470 | 
471 | ### 🛠️ Core Tools (11)
472 | | Tool | Description | Parameters |
473 | |------|-------------|------------|
474 | | `browse_repository` | Browse repository folders | `node_id` (str) |
475 | | `repository_info` | Get repository information | None |
476 | | `upload_document` | Upload new document | `filename` (str), `content_base64` (str), `parent_id` (str), `description` (str) |
477 | | `download_document` | Download document content | `node_id` (str), `save_to_disk` (bool) |
478 | | `create_folder` | Create new folder | `folder_name` (str), `parent_id` (str), `description` (str) |
479 | | `get_node_properties` | Get node metadata | `node_id` (str) |
480 | | `update_node_properties` | Update node metadata | `node_id` (str), `name` (str), `title` (str), `description` (str), `author` (str) |
481 | | `delete_node` | Delete document/folder | `node_id` (str), `permanent` (bool) |
482 | | `checkout_document` | Check out for editing | `node_id` (str), `download_for_editing` (bool) |
483 | | `checkin_document` | Check in after editing | `node_id` (str), `comment` (str), `major_version` (bool), `file_path` (str) |
484 | | `cancel_checkout` | Cancel checkout/unlock | `node_id` (str) |
485 | 
486 | 📖 **See [API Reference](./docs/api_reference.md) for detailed tool documentation**
487 | 
488 | ## 📊 Available Resources
489 | 
490 | ### Repository Information
491 | | Resource | Description | Access Method |
492 | |----------|-------------|---------------|
493 | | `repository_info` | Get comprehensive repository information including version, edition, license details, installed modules, and system status | Available as both MCP resource and tool |
494 | 
495 | The `repository_info` resource provides:
496 | - **Repository Details**: ID, edition (Community/Enterprise), version information
497 | - **License Information**: Issued/expires dates, remaining days, license holder, entitlements
498 | - **System Status**: Read-only mode, audit enabled, quick share, thumbnail generation
499 | - **Installed Modules**: Up to 10 modules with ID, title, version, and installation state
500 | 
501 | 📖 **See [API Reference](./docs/api_reference.md) for detailed resource documentation**
502 | 
503 | ## 🎯 Available Prompts
504 | 
505 | ### Search and Analyze Prompt
506 | | Prompt | Description | Parameters |
507 | |--------|-------------|------------|
508 | | `search_and_analyze` | Interactive form for guided content search and analysis | `query` (search terms), `analysis_type` (summary/detailed/trends/compliance) |
509 | 
510 | The Search and Analyze Prompt provides:
511 | - **Interactive Form**: User-friendly interface with query input field
512 | - **Analysis Options**: Choose from summary, detailed analysis, trends, or compliance reporting
513 | - **Template Generation**: Creates copyable template text for chat conversations
514 | - **Query Assistance**: Helps users structure effective search queries
515 | - **Multiple Search Types**: Integrates with all 4 search tools (content, advanced, metadata, CMIS)
516 | 
517 | 📖 **See [API Reference](./docs/api_reference.md) for detailed prompt documentation**
518 | 
519 | ## 🔧 Configuration Options
520 | 
521 | | Environment Variable | Default | Description |
522 | |---------------------|---------|-------------|
523 | | `ALFRESCO_URL` | `http://localhost:8080` | Alfresco server URL |
524 | | `ALFRESCO_USERNAME` | `admin` | Username for authentication |
525 | | `ALFRESCO_PASSWORD` | `admin` | Password for authentication |
526 | | `ALFRESCO_VERIFY_SSL` | `false` | Verify SSL certificates |
527 | | `ALFRESCO_TIMEOUT` | `30` | Request timeout (seconds) |
528 | | `FASTAPI_HOST` | `localhost` | FastAPI host |
529 | | `FASTAPI_PORT` | `8000` | FastAPI port |
530 | | `LOG_LEVEL` | `INFO` | Logging level |
531 | | `MAX_FILE_SIZE` | `100000000` | Max upload size (bytes) |
532 | 
533 | ⚙️ **See [Configuration Guide](./docs/configuration_guide.md) for deployment options**
534 | 
535 | ## 🏗️ Architecture
536 | 
537 | ```
538 | ┌─────────────────────────────────────────────────────┐
539 | │                   MCP Clients                       │
540 | │  Claude Desktop │ MCP Inspector │ Cursor │ Claude   │
541 | │     Code │ n8n │ LangFlow │ Custom MCP Client App   │
542 | └─────────────────┬───────────────────────────────────┘
543 |                   │ stdio/HTTP/SSE
544 | ┌─────────────────▼───────────────────────────────────┐
545 | │             FastMCP 2.0 MCP Server                  │
546 | │  ┌─────────────┬─────────────┬─────────────────┐    │
547 | │  │ MCP Tools   │ MCP         │ HTTP/SSE API    │    │
548 | │  │ (15 total)  │ Resources   │                 │    │
549 | │  │             │ MCP Prompts │                 │    │
550 | │  └─────────────┴─────────────┴─────────────────┘    │
551 | └─────────────────┬───────────────────────────────────┘
552 |                   │ python-alfresco-api
553 | ┌─────────────────▼───────────────────────────────────┐
554 | │            Alfresco Content Services                │
555 | │         (Community/Enterprise Edition)              │
556 | └─────────────────────────────────────────────────────┘
557 | ```
558 | 
559 | ## 🧪 Testing & Quality
560 | 
561 | ### Test Suite Overview
562 | - **143 Total Tests**: **100% passed** - Coverage of all functionality
563 | - **122 Unit Tests**: **100% passed** - Core functionality validated with mocking (FastMCP 2.0, tools, coverage)
564 | - **21 Integration Tests**: **100% passed** - Live server testing (search, upload, download, document lifecycle)
565 | - **Integration Tests**: Automated end-to-end testing covering all core document lifecycle scenarios
566 | - **Performance Validated**: Search <1s, concurrent operations, resource access
567 | 
568 | ### Coverage Report (Post-Cleanup)
569 | - **Overall Coverage**: 51% (1,829 statements tested)
570 | - **FastMCP 2.0 Core**: Well tested with comprehensive unit coverage
571 | - **Configuration Module**: 93% coverage - Fully tested
572 | - **Package Initialization**: 100% coverage (5/5 lines) - Complete
573 | - **Overall Project**: 51% coverage of comprehensive codebase
574 | 
575 | ### Run Tests
576 | 
577 | ```bash
578 | # Run full test suite
579 | pytest
580 | 
581 | # Run with coverage report
582 | pytest --cov=alfresco_mcp_server --cov-report=term-missing
583 | 
584 | # Run specific test categories
585 | pytest -m "unit"           # Unit tests only
586 | pytest -m "fastmcp"        # FastMCP 2.0 tests
587 | pytest -m "integration"    # Integration tests (requires Alfresco)
588 | ```
589 | 
590 | 🧪 **See [Testing Guide](./docs/testing_guide.md) for detailed testing strategies**
591 | 
592 | ### 🧪 Test Categories and Execution
593 | 
594 | The project includes **4 levels of testing**:
595 | 
596 | 1. **📋 Unit Tests** (122 tests) - Fast, mocked, isolated component testing
597 | 2. **🔗 Integration Tests** (21 tests) - Live Alfresco server testing  
598 | 3. **📝 Comprehensive Tests** - Automated core document lifecycle scenarios
599 | 4. **📊 Coverage Tests** - Edge cases and error path coverage
600 | 
601 | 
602 | 
603 | ## 🧪 Development
604 | 
605 | ### Setup Development Environment
606 | 
607 | ```bash
608 | git clone <repository>
609 | cd python-alfresco-mcp-server
610 | 
611 | # UV handles everything automatically - no manual venv setup needed!
612 | uv sync --extra dev        # Install with development tools
613 | uv sync --extra test       # With testing tools
614 | uv sync --extra all        # Everything
615 | 
616 | # Run immediately to test that installation worked
617 | uv run python-alfresco-mcp-server --help
618 | 
619 | # Install python-alfresco-api for local development (if needed)
620 | uv add --editable ../python-alfresco-api
621 | ```
622 | 
623 | **Traditional Development Setup:**
624 | 
625 | See the **[Installation with pip and pipx](./docs/install_with_pip_pipx.md)** for pip-based development setup.
626 | 
627 | ## 💡 Examples
628 | 
629 | ### Real-world implementation patterns from beginner to enterprise:
630 | 
631 | - **[💡 Examples Library](./examples/README.md)** - Complete navigation and learning paths
632 | - **[🏃 Quick Start](./examples/quick_start.py)** - 5-minute introduction and basic operations
633 | - **[📋 Document Lifecycle](./examples/document_lifecycle.py)** - Complete process demonstration
634 | - **[🚀 Transport Examples](./examples/transport_examples.py)** - STDIO, HTTP, and SSE protocols
635 | - **[⚡ Batch Operations](./examples/batch_operations.py)** - High-performance bulk processing
636 | - **[🛡️ Error Handling](./examples/error_handling.py)** - Resilience patterns
637 | - **[📊 Examples Summary](./examples/examples_summary.md)** - Overview and statistics
638 | 
639 | ## 🤝 Contributing
640 | 
641 | 1. Fork the repository
642 | 2. Create a feature branch (`git checkout -b feature/new-feature`)
643 | 3. Commit your changes (`git commit -m 'Add new feature'`)
644 | 4. Push to the branch (`git push origin feature/new-feature`)
645 | 5. Open a Pull Request
646 | 
647 | ## 📄 License
648 | 
649 | This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details.
650 | 
651 | ## 🔗 Related Projects and References
652 | 
653 | - **[Hyland Alfresco](https://www.hyland.com/en/solutions/products/alfresco-platform)** - Content management platform (Enterprise and Community editions)
654 | - **[python-alfresco-api](https://github.com/stevereiner/python-alfresco-api)** - The underlying Alfresco API library
655 | - **[FastMCP 2.0](https://github.com/jlowin/FastMCP)** - Modern framework for building MCP servers
656 | - **[FastMCP Documentation](https://gofastmcp.com/)** - Complete FastMCP framework documentation and guides
657 | - **[Model Context Protocol](https://modelcontextprotocol.io)** - Official MCP specification and documentation
658 | - **[Playbooks.com MCP List](https://playbooks.com/mcp/stevereiner-alfresco-content-services)** - Python Alfresco MCP Server listing
659 | - **[PulseMCP.com MCP List](https://www.pulsemcp.com/servers/stevereiner-alfresco-content-services)** - Python Alfresco MCP Server listing
660 | - **[Glama.ai MCP List](https://glama.ai/mcp/servers?query=alfresco)** - Glama Alfresco list including Python Alfresco MCP Server listing
661 | - **[MCPMarket.com MCP List](https://mcpmarket.com/server/alfresco)** - Python Alfresco MCP Server listing
662 | 
663 | ## 🙋‍♂️ Support
664 | 
665 | - 📚 **Documentation**: Complete guides in [`./docs/`](./docs/README.md)
666 | - 💡 **Examples**: Implementation patterns in [`./examples/`](./examples/README.md)
667 | - 🧪 **Testing**: Quality assurance in [`./docs/testing_guide.md`](./docs/testing_guide.md)
668 | - 🔍 **MCP Inspector**: Development testing in [`./docs/mcp_inspector_setup.md`](./docs/mcp_inspector_setup.md)
669 | - 🛠️ **Troubleshooting**: Problem solving in [`./docs/troubleshooting.md`](./docs/troubleshooting.md)
670 | - 🐛 **Issues**: [GitHub Issues](https://github.com/stevereiner/python-alfresco-mcp-server/issues)
671 | 
672 | ---
673 | 
674 | **🚀 MCP server built with [python-alfresco-api](https://github.com/stevereiner/python-alfresco-api) and [FastMCP 2.0](https://github.com/paulinephelan/FastMCP)**
675 | 
```

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

```python
1 | 
```

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

```json
1 | {
2 |     "svn.ignoreMissingSvnWarning": true
3 | }
```

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

```python
1 | # Prompts module
2 | 
3 | from . import search_and_analyze
4 | 
5 | __all__ = [
6 |     "search_and_analyze",
7 | ] 
```

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

```python
1 | # Resources module
2 | 
3 | from . import repository_resources
4 | 
5 | __all__ = [
6 |     "repository_resources",
7 | ] 
```

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

```python
 1 | """
 2 | Tools module for Alfresco MCP Server.
 3 | Contains core and search tools organized hierarchically.
 4 | """
 5 | 
 6 | # Import subdirectories
 7 | from . import core
 8 | from . import search
 9 | 
10 | __all__ = [
11 |     "core",
12 |     "search",
13 | ] 
```

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

```python
 1 | # Search tools module
 2 | 
 3 | from . import (
 4 |     advanced_search,
 5 |     cmis_search,
 6 |     search_by_metadata,
 7 |     search_content,
 8 | )
 9 | 
10 | __all__ = [
11 |     "advanced_search",
12 |     "cmis_search", 
13 |     "search_by_metadata",
14 |     "search_content",
15 | ] 
```

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

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "python-alfresco-mcp-server": {
 4 |       "command": "python-alfresco-mcp-server",
 5 |       "args": ["--transport", "stdio"],
 6 |       "env": {
 7 |         "ALFRESCO_URL": "http://localhost:8080",
 8 |         "ALFRESCO_USERNAME": "admin", 
 9 |         "ALFRESCO_PASSWORD": "admin"
10 |       }
11 |     }
12 |   }
13 | } 
```

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

```yaml
 1 | # Alfresco MCP Server Configuration
 2 | alfresco:
 3 |   base_url: "http://localhost:8080"  # Base URL only - API paths added by individual clients
 4 |   username: "admin"
 5 |   password: "admin"
 6 |   verify_ssl: false
 7 |   timeout: 30
 8 | 
 9 | # MCP Server Configuration  
10 | server:
11 |   name: "python-alfresco-mcp-server"
12 |   version: "1.1.0"
```

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

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "python-alfresco-mcp-server": {
 4 |       "command": "python-alfresco-mcp-server", 
 5 |       "args": ["--transport", "http", "--port", "8003"],
 6 |       "env": {
 7 |         "ALFRESCO_URL": "http://localhost:8080",
 8 |         "ALFRESCO_USERNAME": "admin",
 9 |         "ALFRESCO_PASSWORD": "admin"
10 |       }
11 |     }
12 |   }
13 | } 
```

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

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "python-alfresco-mcp-server": {
 4 |       "command": "python-alfresco-mcp-server",
 5 |       "args": [
 6 |         "--transport", 
 7 |         "stdio"
 8 |       ],
 9 |       "env": {
10 |         "ALFRESCO_URL": "http://localhost:8080",
11 |         "ALFRESCO_USERNAME": "admin",
12 |         "ALFRESCO_PASSWORD": "admin"
13 |       }
14 |     }
15 |   }
16 | } 
```

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

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "python-alfresco-mcp-server": {
 4 |       "command": "uvx",
 5 |       "args": [
 6 |         "python-alfresco-mcp-server",
 7 |         "--transport", 
 8 |         "stdio"
 9 |       ],
10 |       "env": {
11 |         "ALFRESCO_URL": "http://localhost:8080",
12 |         "ALFRESCO_USERNAME": "admin",
13 |         "ALFRESCO_PASSWORD": "admin"
14 |       }
15 |     }
16 |   }
17 | } 
```

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

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "python-alfresco-mcp-server": {
 4 |       "command": "uvx",
 5 |       "args": [
 6 |         "python-alfresco-mcp-server",
 7 |         "--transport", 
 8 |         "stdio"
 9 |       ],
10 |       "env": {
11 |         "ALFRESCO_URL": "http://localhost:8080",
12 |         "ALFRESCO_USERNAME": "admin",
13 |         "ALFRESCO_PASSWORD": "admin"
14 |       }
15 |     }
16 |   }
17 | } 
```

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

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "python-alfresco-mcp-server": {
 4 |       "command": "python-alfresco-mcp-server",
 5 |       "args": [
 6 |         "--transport", 
 7 |         "stdio"
 8 |       ],
 9 |       "env": {
10 |         "ALFRESCO_URL": "http://localhost:8080",
11 |         "ALFRESCO_USERNAME": "admin",
12 |         "ALFRESCO_PASSWORD": "admin",
13 |         "PYTHONIOENCODING": "utf-8",
14 |         "PYTHONLEGACYWINDOWSSTDIO": "1"
15 |       }
16 |     }
17 |   }
18 | } 
```

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

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "python-alfresco-mcp-server": {
 4 |       "command": "uv",
 5 |       "args": [
 6 |         "run", 
 7 |         "python-alfresco-mcp-server",
 8 |         "--transport", 
 9 |         "stdio"
10 |       ],
11 |       "cwd": "/path/to/python-alfresco-mcp-server",
12 |       "env": {
13 |         "ALFRESCO_URL": "http://localhost:8080",
14 |         "ALFRESCO_USERNAME": "admin",
15 |         "ALFRESCO_PASSWORD": "admin"
16 |       }
17 |     }
18 |   }
19 | } 
```

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

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "python-alfresco-mcp-server": {
 4 |       "command": "uvx",
 5 |       "args": [
 6 |         "python-alfresco-mcp-server",
 7 |         "--transport", 
 8 |         "http",
 9 |         "--host",
10 |         "127.0.0.1",
11 |         "--port",
12 |         "8003"
13 |       ],
14 |       "env": {
15 |         "ALFRESCO_URL": "http://localhost:8080",
16 |         "ALFRESCO_USERNAME": "admin",
17 |         "ALFRESCO_PASSWORD": "admin"
18 |       }
19 |     }
20 |   }
21 | } 
```

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

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "python-alfresco-mcp-server": {
 4 |       "command": "uvx",
 5 |       "args": [
 6 |         "python-alfresco-mcp-server",
 7 |         "--transport", 
 8 |         "stdio"
 9 |       ],
10 |       "env": {
11 |         "ALFRESCO_URL": "http://localhost:8080",
12 |         "ALFRESCO_USERNAME": "admin",
13 |         "ALFRESCO_PASSWORD": "admin",
14 |         "PYTHONIOENCODING": "utf-8",
15 |         "PYTHONLEGACYWINDOWSSTDIO": "1"
16 |       }
17 |     }
18 |   }
19 | } 
```

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

```json
 1 | "servers": {
 2 |   "my-mcp-server": {
 3 |     "type": "http",
 4 |     "command": "python",
 5 |     "args": ["${workspaceFolder}/run_server.py", "--transport", "http", "--port", "8003", "--host", "127.0.0.1"],
 6 |     "dev": {
 7 |       "watch": "${workspaceFolder}/alfresco_mcp_server/**/*.py",
 8 |       "debug": { "type": "python" }
 9 |     },
10 |     "env": {
11 |         "ALFRESCO_URL": "http://localhost:8080",
12 |         "ALFRESCO_USERNAME": "admin",
13 |         "ALFRESCO_PASSWORD": "admin"
14 |       }
15 |   }
16 | }
17 | 
```

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

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "python-alfresco-mcp-server": {
 4 |       "command": "uv",
 5 |       "args": [
 6 |         "run", 
 7 |         "python-alfresco-mcp-server",
 8 |         "--transport", 
 9 |         "stdio"
10 |       ],
11 |       "cwd": "C:\\newdev3\\python-alfresco-mcp-server",
12 |       "env": {
13 |         "ALFRESCO_URL": "http://localhost:8080",
14 |         "ALFRESCO_USERNAME": "admin",
15 |         "ALFRESCO_PASSWORD": "admin",
16 |         "PYTHONIOENCODING": "utf-8",
17 |         "PYTHONLEGACYWINDOWSSTDIO": "1"
18 |       }
19 |     }
20 |   }
21 | } 
```

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

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "python-alfresco-mcp-server": {
 4 |       "command": "uv",
 5 |       "args": [
 6 |         "run", 
 7 |         "python-alfresco-mcp-server",
 8 |         "--transport", 
 9 |         "stdio"
10 |       ],
11 |       "cwd": "C:\\path\\to\\python-alfresco-mcp-server",
12 |       "env": {
13 |         "ALFRESCO_URL": "http://localhost:8080",
14 |         "ALFRESCO_USERNAME": "admin",
15 |         "ALFRESCO_PASSWORD": "admin",
16 |         "PYTHONIOENCODING": "utf-8",
17 |         "PYTHONLEGACYWINDOWSSTDIO": "1"
18 |       }
19 |     }
20 |   }
21 | } 
```

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

```python
 1 | # Core tools module
 2 | 
 3 | from . import (
 4 |     browse_repository,
 5 |     cancel_checkout,
 6 |     checkin_document,
 7 |     checkout_document,
 8 |     create_folder,
 9 |     delete_node,
10 |     download_document,
11 |     get_node_properties,
12 |     update_node_properties,
13 |     upload_document,
14 | )
15 | 
16 | __all__ = [
17 |     "browse_repository",
18 |     "cancel_checkout",
19 |     "checkin_document", 
20 |     "checkout_document",
21 |     "create_folder",
22 |     "delete_node",
23 |     "download_document",
24 |     "get_node_properties",
25 |     "update_node_properties",
26 |     "upload_document",
27 | ] 
```

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

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "python-alfresco-mcp-server": {
 4 |       "command": "uv",
 5 |       "args": [
 6 |         "run", 
 7 |         "python-alfresco-mcp-server",
 8 |         "--transport", 
 9 |         "http",
10 |         "--host",
11 |         "127.0.0.1",
12 |         "--port",
13 |         "8003"
14 |       ],
15 |       "cwd": "C:\\newdev3\\python-alfresco-mcp-server",
16 |       "env": {
17 |         "ALFRESCO_URL": "http://localhost:8080",
18 |         "ALFRESCO_USERNAME": "admin",
19 |         "ALFRESCO_PASSWORD": "admin",
20 |         "PYTHONIOENCODING": "utf-8",
21 |         "PYTHONLEGACYWINDOWSSTDIO": "1"
22 |       }
23 |     }
24 |   }
25 | } 
```

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

```python
 1 | """
 2 | MCP Server for Alfresco
 3 | 
 4 | Model Context Protocol server for Alfresco Content Services.
 5 | Provides AI-native access to Alfresco content management and search operations.
 6 | """
 7 | 
 8 | __version__ = "1.1.0"
 9 | __title__ = "MCP Server for Alfresco"
10 | __description__ = "Model Context Protocol server for Alfresco Content Services"
11 | 
12 | from .config import AlfrescoConfig, load_config
13 | 
14 | # Import subpackages to make them available
15 | from . import tools
16 | from . import resources  
17 | from . import prompts
18 | from . import utils
19 | 
20 | __all__ = [
21 |     "AlfrescoConfig", 
22 |     "load_config",
23 |     "tools",
24 |     "resources", 
25 |     "prompts",
26 |     "utils",
27 | ] 
```

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

```python
 1 | # Utilities module for Alfresco MCP Server
 2 | 
 3 | from .connection import (
 4 |     get_alfresco_config,
 5 |     get_connection,
 6 |     get_search_utils,
 7 |     get_node_utils,
 8 | )
 9 | 
10 | from .file_type_analysis import (
11 |     detect_file_extension_from_content,
12 |     analyze_content_type,
13 | )
14 | 
15 | from .json_utils import (
16 |     make_json_safe,
17 |     safe_format_output,
18 |     escape_unicode_for_json,
19 | )
20 | 
21 | __all__ = [
22 |     # Connection utilities
23 |     "get_alfresco_config",
24 |     "get_connection", 
25 |     "get_search_utils",
26 |     "get_node_utils",
27 |     # File type analysis
28 |     "detect_file_extension_from_content",
29 |     "analyze_content_type",
30 |     # JSON utilities
31 |     "make_json_safe",
32 |     "safe_format_output",
33 |     "escape_unicode_for_json",
34 | ] 
```

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

```
 1 | # Alfresco MCP Server Configuration
 2 | # Copy this file to .env and customize for your environment
 3 | # The .env file will be ignored by git for security
 4 | 
 5 | # === REQUIRED: Alfresco Connection ===
 6 | ALFRESCO_URL=http://localhost:8080
 7 | ALFRESCO_USERNAME=admin
 8 | ALFRESCO_PASSWORD=admin
 9 | 
10 | # === OPTIONAL: Connection Settings ===
11 | ALFRESCO_VERIFY_SSL=false
12 | ALFRESCO_TIMEOUT=30
13 | 
14 | # === OPTIONAL: Server Settings ===
15 | LOG_LEVEL=INFO
16 | MAX_FILE_SIZE=100000000
17 | 
18 | # === OPTIONAL: HTTP Transport Settings ===
19 | FASTAPI_HOST=localhost
20 | FASTAPI_PORT=8000
21 | 
22 | # === NOTES ===
23 | # - Environment variables take precedence over defaults
24 | # - python-alfresco-api may have its own configuration (check its docs)
25 | # - For production, use environment variables or secure secret management
26 | # - Boolean values: true/false (case insensitive)
27 | # - File size in bytes (100000000 = 100MB) 
```

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

```markdown
 1 | # Starting MCP Server for Testing
 2 | 
 3 | ## For MCP Inspector Testing (HTTP)
 4 | 
 5 | ### Option 1: Using PowerShell
 6 | ```powershell
 7 | cd C:\newdev3\python-alfresco-mcp-server
 8 | .\venv_clean\Scripts\activate
 9 | fastmcp run alfresco_mcp_server.fastmcp_server --host localhost --port 8003
10 | ```
11 | 
12 | ### Option 2: Using Command Prompt
13 | ```cmd
14 | cd C:\newdev3\python-alfresco-mcp-server
15 | venv_clean\Scripts\activate.bat
16 | fastmcp run alfresco_mcp_server.fastmcp_server --host localhost --port 8003
17 | ```
18 | 
19 | ## For Claude Desktop Testing (STDIO)
20 | Claude Desktop uses the stdio transport via the config file:
21 | `claude-desktop-config.json`
22 | 
23 | ## Testing Server Status
24 | After starting the HTTP server, run:
25 | ```bash
26 | python tests/mcp_specific/test_server_status.py
27 | ```
28 | 
29 | ## MCP Inspector Setup
30 | 1. Start HTTP server on port 8003 (see above)
31 | 2. Open MCP Inspector in browser
32 | 3. Use URL: `http://localhost:8003`
33 | 4. **Note**: MCP Inspector will show warning about "risky auth" and include token in URL - this is normal for development testing
34 | 
35 | ## Important Notes
36 | - Make sure you're using `venv_clean` which has all the correct dependencies
37 | - The FastMCP server will show import success messages when starting correctly
38 | - If you see pydantic import errors, make sure you're using the correct virtual environment 
```

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

```python
 1 | #!/usr/bin/env python3
 2 | """
 3 | Simple wrapper to run the Alfresco MCP Server
 4 | """
 5 | import sys
 6 | import os
 7 | 
 8 | # Add the current directory to Python path
 9 | sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
10 | 
11 | # Import and run the server
12 | from alfresco_mcp_server.fastmcp_server import mcp
13 | 
14 | if __name__ == "__main__":
15 |     import argparse
16 |     
17 |     parser = argparse.ArgumentParser(description="Run Alfresco MCP Server")
18 |     parser.add_argument("--port", type=int, default=8003, help="Port to run server on")
19 |     parser.add_argument("--host", type=str, default="127.0.0.1", help="Host to bind to")
20 |     parser.add_argument("--transport", type=str, default="http", choices=["stdio", "http", "sse"], help="Transport method to use")
21 |     
22 |     args = parser.parse_args()
23 |     
24 |     if args.transport == "stdio":
25 |         print(">> Starting Alfresco MCP Server with stdio transport")
26 |         mcp.run(transport="stdio")
27 |     elif args.transport == "http":
28 |         print(f">> Starting Alfresco MCP Server with HTTP transport on {args.host}:{args.port}")
29 |         mcp.run(transport="http", host=args.host, port=args.port)
30 |     else:
31 |         print(f">> Starting Alfresco MCP Server with SSE transport on {args.host}:{args.port}")
32 |         mcp.run(transport="sse", host=args.host, port=args.port) 
```

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

```
 1 | [tool:pytest]
 2 | # Pytest configuration for Alfresco MCP Server
 3 | minversion = 6.0
 4 | testpaths = tests
 5 | python_files = test_*.py
 6 | python_classes = Test*
 7 | python_functions = test_*
 8 | 
 9 | # Add options
10 | addopts = 
11 |     --strict-markers
12 |     --strict-config
13 |     --verbose
14 |     --tb=short
15 |     --cov=alfresco_mcp_server
16 |     --cov-report=html:htmlcov
17 |     --cov-report=term-missing
18 |     --cov-report=xml
19 |     --cov-branch
20 |     --cov-fail-under=85
21 | 
22 | # Test markers
23 | markers =
24 |     unit: marks tests as unit tests (fast, mocked dependencies)
25 |     integration: marks tests as integration tests (requires live Alfresco)
26 |     slow: marks tests as slow running
27 |     performance: marks tests as performance benchmarks
28 | 
29 | # Async test configuration
30 | asyncio_mode = auto
31 | 
32 | # Filter warnings
33 | filterwarnings =
34 |     ignore::DeprecationWarning
35 |     ignore::PendingDeprecationWarning
36 |     ignore::pytest.PytestUnraisableExceptionWarning
37 | 
38 | # Coverage configuration
39 | [tool:coverage:run]
40 | source = alfresco_mcp_server
41 | omit = 
42 |     tests/*
43 |     venv/*
44 |     */site-packages/*
45 |     */test_*
46 |     setup.py
47 | 
48 | [tool:coverage:report]
49 | exclude_lines =
50 |     pragma: no cover
51 |     def __repr__
52 |     if self.debug:
53 |     if settings.DEBUG
54 |     raise AssertionError
55 |     raise NotImplementedError
56 |     if 0:
57 |     if __name__ == .__main__.:
58 |     class .*\bProtocol\):
59 |     @(abc\.)?abstractmethod
60 | 
61 | [tool:coverage:html]
62 | directory = htmlcov
63 | 
64 | [tool:coverage:xml]
65 | output = coverage.xml 
```

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

```python
 1 | """
 2 | Search and analyze prompt for Alfresco MCP Server.
 3 | Self-contained prompt for generating comprehensive search and analysis workflows.
 4 | """
 5 | 
 6 | 
 7 | async def search_and_analyze_impl(query: str, analysis_type: str = "summary") -> str:
 8 |     """Generate comprehensive search and analysis prompts for Alfresco documents.
 9 |     
10 |     Args:
11 |         query: Search query for documents
12 |         analysis_type: Type of analysis (summary, detailed, trends, compliance)
13 |     
14 |     Returns:
15 |         Formatted prompt for document analysis workflow
16 |     """
17 |     base_prompt = f"""**Alfresco Document Analysis Request**
18 | 
19 | Please search for documents matching "{query}" and provide a {analysis_type} analysis.
20 | 
21 | **Step 1: Search**
22 | Use the `search_content` tool to find relevant documents.
23 | 
24 | **Step 2: Analysis**
25 | Based on the search results, provide:
26 | """
27 |     
28 |     if analysis_type == "summary":
29 |         base_prompt += """
30 | - Document count and types
31 | - Key themes and topics
32 | - Most relevant documents
33 | - Quick insights
34 | """
35 |     elif analysis_type == "detailed":
36 |         base_prompt += """
37 | - Comprehensive document inventory
38 | - Metadata analysis (dates, authors, sizes)
39 | - Content categorization
40 | - Compliance status
41 | - Recommended actions
42 | - Related search suggestions
43 | """
44 |     elif analysis_type == "trends":
45 |         base_prompt += """
46 | - Temporal patterns (creation/modification dates)
47 | - Document lifecycle analysis
48 | - Usage and access patterns
49 | - Version history insights
50 | - Storage optimization recommendations
51 | """
52 |     elif analysis_type == "compliance":
53 |         base_prompt += """
54 | - Document retention analysis
55 | - Security classification review
56 | - Access permissions audit
57 | - Regulatory compliance status
58 | - Risk assessment
59 | - Remediation recommendations
60 | """
61 |     
62 |     base_prompt += f"""
63 | **Step 3: Recommendations**
64 | Provide actionable insights and next steps based on the {analysis_type} analysis.
65 | """
66 |     
67 |     return base_prompt 
```

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

```
 1 | @echo off
 2 | REM Windows batch script for running Alfresco MCP Server tests
 3 | 
 4 | echo ================================
 5 | echo  Alfresco MCP Server Test Suite
 6 | echo ================================
 7 | 
 8 | REM Check if Python is available
 9 | python --version >nul 2>&1
10 | if %errorlevel% neq 0 (
11 |     echo Error: Python is not installed or not in PATH
12 |     exit /b 1
13 | )
14 | 
15 | REM Set environment variables for Alfresco
16 | set ALFRESCO_URL=http://localhost:8080
17 | set ALFRESCO_USERNAME=admin
18 | set ALFRESCO_PASSWORD=admin
19 | set ALFRESCO_VERIFY_SSL=false
20 | 
21 | REM Parse command line arguments
22 | set MODE=unit
23 | set INSTALL_DEPS=false
24 | 
25 | :parse_args
26 | if "%1"=="--help" goto :show_help
27 | if "%1"=="--unit" set MODE=unit
28 | if "%1"=="--integration" set MODE=integration
29 | if "%1"=="--performance" set MODE=performance
30 | if "%1"=="--coverage" set MODE=coverage
31 | if "%1"=="--all" set MODE=all
32 | if "%1"=="--lint" set MODE=lint
33 | if "%1"=="--install-deps" set INSTALL_DEPS=true
34 | shift
35 | if not "%1"=="" goto :parse_args
36 | 
37 | REM Install dependencies if requested
38 | if "%INSTALL_DEPS%"=="true" (
39 |     echo Installing test dependencies...
40 |     python -m pip install pytest pytest-asyncio pytest-cov pytest-mock coverage httpx
41 |     if %errorlevel% neq 0 (
42 |         echo Error: Failed to install dependencies
43 |         exit /b 1
44 |     )
45 | )
46 | 
47 | REM Run the test runner
48 | echo Running tests in %MODE% mode...
49 | python scripts/run_tests.py --mode %MODE%
50 | 
51 | if %errorlevel% neq 0 (
52 |     echo Tests failed!
53 |     exit /b 1
54 | )
55 | 
56 | echo.
57 | echo ================================
58 | echo  Tests completed successfully!
59 | echo ================================
60 | echo Coverage report: htmlcov/index.html
61 | exit /b 0
62 | 
63 | :show_help
64 | echo Usage: test.bat [OPTIONS]
65 | echo.
66 | echo Options:
67 | echo   --unit          Run unit tests (default)
68 | echo   --integration   Run integration tests (requires Alfresco)
69 | echo   --performance   Run performance tests
70 | echo   --coverage      Run coverage analysis
71 | echo   --all           Run all tests
72 | echo   --lint          Run code linting
73 | echo   --install-deps  Install test dependencies
74 | echo   --help          Show this help
75 | exit /b 0 
```

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

```markdown
 1 | # Connecting MCP Inspector to Your Running Server
 2 | 
 3 | ## 🔍 Current Setup
 4 | - **MCP Inspector**: Running on port 6274 ✅
 5 | - **Your MCP Server**: Running on port 8003 ✅
 6 | - **URL Pattern**: `http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=...#tools`
 7 | 
 8 | ## 🔗 How to Connect
 9 | 
10 | ### Step 1: MCP Inspector Server Connection
11 | In your MCP Inspector interface:
12 | 
13 | 1. **Look for "Add Server" or "Connect Server"** button
14 | 2. **Enter your server URL**: `http://localhost:8003`
15 | 3. **Transport**: Select "HTTP" 
16 | 4. **No additional auth needed** - it's local
17 | 
18 | ### Step 2: Expected URL Structure
19 | Once connected, your URL will be:
20 | ```
21 | http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=<token>&server=http://localhost:8003#tools
22 | ```
23 | 
24 | ### Step 3: What You Should See
25 | - ✅ **Server**: "Alfresco Document Management Server"
26 | - ✅ **15 Tools Available**:
27 |   - search_content
28 |   - search_by_metadata
29 |   - advanced_search
30 |   - cmis_search
31 |   - browse_repository
32 |   - repository_info
33 |   - upload_document
34 |   - download_document
35 |   - checkout_document
36 |   - checkin_document
37 |   - cancel_checkout
38 |   - delete_node
39 |   - get_node_properties
40 |   - update_node_properties
41 |   - create_folder
42 | 
43 | ## 🚨 If Connection Fails
44 | 
45 | ### Check Server Status
46 | ```bash
47 | python tests/mcp_specific/test_server_status.py
48 | ```
49 | 
50 | ### Check Both Ports
51 | ```bash
52 | netstat -an | findstr ":6274\|:8003"
53 | ```
54 | 
55 | ### Restart If Needed
56 | ```bash
57 | # Stop your MCP server
58 | taskkill /PID 57420 /F
59 | 
60 | # Restart with explicit logging
61 | fastmcp run alfresco_mcp_server/fastmcp_server.py --transport http --host localhost --port 8003 --log-level DEBUG
62 | ```
63 | 
64 | ## 🎯 Quick Test
65 | 1. **Open your MCP Inspector**: `http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=4b4a857ba6eecd2ac7029bf0c9daaf97e311b7f5cee35dec3a25ddf739ffd66b#tools`
66 | 2. **Add server**: `http://localhost:8003`
67 | 3. **Test a tool**: Try "browse_repository" or "search_content"
68 | 
69 | ## 🔒 The "Risky Auth" Token
70 | The `MCP_PROXY_AUTH_TOKEN=4b4a857ba6eecd2ac7029bf0c9daaf97e311b7f5cee35dec3a25ddf739ffd66b` is:
71 | - ✅ **Normal**: Generated by MCP Inspector for security
72 | - ✅ **Local only**: Not transmitted anywhere
73 | - ✅ **Safe**: Standard MCP development practice 
```

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

```markdown
  1 | # How to Start MCP Inspector
  2 | 
  3 | ## 🚀 Quick Start Methods
  4 | 
  5 | ### Method 1: Using Config File (Recommended - Avoids Proxy Errors)
  6 | ```bash
  7 | # 1. Start your MCP server first
  8 | python -m alfresco_mcp_server.fastmcp_server --transport http --port 8003
  9 | 
 10 | # 2. Start MCP Inspector with pre-configured server
 11 | npx @modelcontextprotocol/inspector --config mcp-inspector-http-config.json --server python-alfresco-mcp-server
 12 | ```
 13 | 
 14 | **Expected Output:**
 15 | ```
 16 | Starting MCP inspector...
 17 | ⚙️ Proxy server listening on 127.0.0.1:6277
 18 | 🔑 Session token: d7a62ab6e032eefe5d85e807c50e13b9fffcd12badbf8bbc3377659c0be4fa8d
 19 | Use this token to authenticate requests or set DANGEROUSLY_OMIT_AUTH=true to disable auth
 20 | 
 21 | 🔗 Open inspector with token pre-filled:
 22 |    http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=d7a62ab6e032eefe5d85e807c50e13b9fffcd12badbf8bbc3377659c0be4fa8d
 23 | ```
 24 | 
 25 | ### Method 2: Using npx (Basic)
 26 | ```bash
 27 | npx @modelcontextprotocol/inspector
 28 | ```
 29 | 
 30 | ### Method 2: Using npm (if globally installed)
 31 | ```bash
 32 | npm run dev
 33 | # or
 34 | npm start
 35 | ```
 36 | 
 37 | ### Method 3: Direct GitHub (if you have it locally)
 38 | ```bash
 39 | # If you have the repo cloned
 40 | cd path/to/mcp-inspector
 41 | npm run dev
 42 | ```
 43 | 
 44 | ### Method 4: Using the pre-built package
 45 | ```bash
 46 | npx @modelcontextprotocol/inspector@latest
 47 | ```
 48 | 
 49 | ## 📍 Expected Output
 50 | When MCP Inspector starts, you should see:
 51 | ```
 52 | > Local:   http://localhost:6274
 53 | > Network: http://192.168.x.x:6274
 54 | ```
 55 | 
 56 | ## 🔗 After Starting
 57 | 
 58 | ### 1. Open Browser
 59 | Navigate to: `http://localhost:6274`
 60 | 
 61 | ### 2. Connect to Your Server
 62 | - **Click "Add Server"** or server connection field
 63 | - **Enter**: `http://localhost:8003`
 64 | - **Transport**: HTTP
 65 | - **Connect**
 66 | 
 67 | ### 3. Expected URL
 68 | ```
 69 | http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=<new-token>&server=http://localhost:8003#tools
 70 | ```
 71 | 
 72 | ## 🛠️ Troubleshooting
 73 | 
 74 | ### If Port 6274 is Busy
 75 | ```bash
 76 | # MCP Inspector will auto-find next available port
 77 | # Check what port it actually uses in the startup message
 78 | ```
 79 | 
 80 | ### If npx Fails
 81 | ```bash
 82 | # Update npm/npx
 83 | npm install -g npm@latest
 84 | 
 85 | # Try with explicit version
 86 | npx @modelcontextprotocol/inspector@latest
 87 | ```
 88 | 
 89 | ### Check if Already Running
 90 | ```bash
 91 | netstat -an | findstr :6274
 92 | # or check other common ports
 93 | netstat -an | findstr ":3000\|:5173\|:6274"
 94 | ```
 95 | 
 96 | ## 🎯 Quick Test Command
 97 | ```bash
 98 | # This should start MCP Inspector on port 6274
 99 | npx @modelcontextprotocol/inspector
100 | ```
101 | 
102 | Then open `http://localhost:6274` and connect to your server at `http://localhost:8003`! 
```

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

```markdown
 1 | # MCP Server Testing Instructions
 2 | 
 3 | ## 🟢 Server Status: RUNNING
 4 | - **Process**: Python PID 57420 ✅
 5 | - **Port**: 8003 LISTENING ✅  
 6 | - **URL**: `http://localhost:8003/mcp/` ✅
 7 | 
 8 | ## 🌐 MCP Inspector Testing
 9 | 
10 | ### Why Browser Shows Error
11 | The error `"Not Acceptable: Client must accept text/event-stream"` is **NORMAL**:
12 | - MCP servers use Server-Sent Events (SSE) format
13 | - Regular browsers can't handle MCP protocol
14 | - You need the MCP Inspector tool
15 | 
16 | ### Setup MCP Inspector
17 | 1. **Download**: [MCP Inspector](https://github.com/modelcontextprotocol/inspector)
18 | 2. **Install**: Follow their setup instructions
19 | 3. **Connect to**: `http://localhost:8003`
20 | 4. **No token required** - it's local development
21 | 
22 | ### Expected in MCP Inspector
23 | - ✅ Server connection successful
24 | - ✅ 10 tools available (search_content, browse_repository, etc.)
25 | - ✅ "Risky auth" warning - NORMAL for development
26 | - ✅ Can test individual tools
27 | 
28 | ## 💬 Claude Desktop Testing (Easier)
29 | 
30 | ### Setup
31 | 1. **Config ready**: `claude-desktop-config.json` ✅
32 | 2. **Restart**: Claude Desktop application
33 | 3. **Ready**: MCP tools appear in conversation
34 | 
35 | ### Your Config File
36 | ```json
37 | {
38 |   "mcpServers": {
39 |     "alfresco": {
40 |       "command": "C:\\newdev3\\python-alfresco-mcp-server\\venv_clean\\Scripts\\python.exe",
41 |       "args": [
42 |         "C:\\newdev3\\python-alfresco-mcp-server\\alfresco_mcp_server\\fastmcp_server.py"
43 |       ],
44 |       "env": {
45 |         "ALFRESCO_URL": "http://localhost:8080",
46 |         "ALFRESCO_USERNAME": "admin", 
47 |         "ALFRESCO_PASSWORD": "admin"
48 |       }
49 |     }
50 |   }
51 | }
52 | ```
53 | 
54 | ### Testing in Claude
55 | Ask Claude to:
56 | - Search for content: "Search for documents about 'project'"
57 | - Browse repository: "Show me the repository structure"
58 | - Get node info: "Get details about the root folder"
59 | 
60 | ## 🔒 Security Notes
61 | 
62 | ### LOCAL ONLY - No Central Registration
63 | - ✅ Server runs on `localhost:8003` only
64 | - ✅ Not accessible from internet
65 | - ✅ Not registered in any central directory
66 | - ✅ Private development server
67 | 
68 | ### If You're Concerned About Name
69 | The server name "Alfresco Document Management Server" is just a display name:
70 | - ✅ Not registered anywhere
71 | - ✅ Only visible to connected clients
72 | - ✅ Can be changed in code if desired
73 | 
74 | ## 🧪 Quick Test Commands
75 | 
76 | ### Test Server Status
77 | ```bash
78 | python tests/mcp_specific/test_server_status.py
79 | ```
80 | 
81 | ### Test Server Tools
82 | ```bash
83 | python tests/mcp_specific/test_server_fixed.py
84 | ```
85 | 
86 | ### Stop Server
87 | ```bash
88 | # Find and kill process 57420
89 | taskkill /PID 57420 /F
90 | ```
91 | 
92 | ## 🎯 Recommended Testing Order
93 | 1. **Start with Claude Desktop** (easiest to test)
94 | 2. **Then try MCP Inspector** (if you want detailed tool testing)
95 | 3. **Both use the same server** - no conflicts 
```

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

```
 1 | # Test script for Python Alfresco MCP Server HTTP transport
 2 | Write-Host "🌐 Testing Python Alfresco MCP Server - HTTP Transport" -ForegroundColor Green
 3 | Write-Host "=" * 60
 4 | 
 5 | $baseUrl = "http://127.0.0.1:8001"
 6 | 
 7 | # Wait for server to start
 8 | Write-Host "⏳ Waiting for server to start..." -ForegroundColor Yellow
 9 | Start-Sleep -Seconds 3
10 | 
11 | try {
12 |     # Test 1: Server health/info
13 |     Write-Host "`n🏥 Testing Server Health..." -ForegroundColor Cyan
14 |     try {
15 |         $response = Invoke-WebRequest -Uri "$baseUrl/" -Method GET -TimeoutSec 10
16 |         Write-Host "✅ Server is responding" -ForegroundColor Green
17 |         Write-Host "   Status: $($response.StatusCode)" -ForegroundColor Gray
18 |     }
19 |     catch {
20 |         Write-Host "ℹ️  Root endpoint: $($_.Exception.Message)" -ForegroundColor Yellow
21 |     }
22 | 
23 |     # Test 2: List tools (if available)
24 |     Write-Host "`n🔧 Testing Tools Endpoint..." -ForegroundColor Cyan
25 |     try {
26 |         $response = Invoke-WebRequest -Uri "$baseUrl/tools" -Method GET -TimeoutSec 10
27 |         Write-Host "✅ Tools endpoint accessible" -ForegroundColor Green
28 |         $content = $response.Content | ConvertFrom-Json
29 |         Write-Host "   Tools found: $($content.tools.Count)" -ForegroundColor Gray
30 |     }
31 |     catch {
32 |         Write-Host "ℹ️  Tools endpoint: $($_.Exception.Message)" -ForegroundColor Yellow
33 |     }
34 | 
35 |     # Test 3: List resources (if available)
36 |     Write-Host "`n📦 Testing Resources Endpoint..." -ForegroundColor Cyan
37 |     try {
38 |         $response = Invoke-WebRequest -Uri "$baseUrl/resources" -Method GET -TimeoutSec 10
39 |         Write-Host "✅ Resources endpoint accessible" -ForegroundColor Green
40 |     }
41 |     catch {
42 |         Write-Host "ℹ️  Resources endpoint: $($_.Exception.Message)" -ForegroundColor Yellow
43 |     }
44 | 
45 |     # Test 4: Call a tool (if supported)
46 |     Write-Host "`n🔍 Testing Tool Call..." -ForegroundColor Cyan
47 |     try {
48 |         $body = @{
49 |             query = "test"
50 |             max_results = 5
51 |         } | ConvertTo-Json
52 | 
53 |         $response = Invoke-WebRequest -Uri "$baseUrl/tools/search_content" -Method POST -Body $body -ContentType "application/json" -TimeoutSec 10
54 |         Write-Host "✅ Tool call successful" -ForegroundColor Green
55 |         Write-Host "   Response length: $($response.Content.Length) chars" -ForegroundColor Gray
56 |     }
57 |     catch {
58 |         Write-Host "ℹ️  Tool call: $($_.Exception.Message)" -ForegroundColor Yellow
59 |     }
60 | 
61 |     Write-Host "`n🎉 HTTP testing completed!" -ForegroundColor Green
62 | 
63 | }
64 | catch {
65 |     Write-Host "❌ HTTP testing failed: $($_.Exception.Message)" -ForegroundColor Red
66 | }
67 | 
68 | Write-Host "`n📖 Next steps:" -ForegroundColor Cyan
69 | Write-Host "1. Try MCP Inspector: npx @modelcontextprotocol/inspector" -ForegroundColor Gray
70 | Write-Host "2. Test STDIO transport: python test_with_mcp_client.py" -ForegroundColor Gray
71 | Write-Host "3. Run existing test suite: python -m pytest tests/" -ForegroundColor Gray 
```

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

```python
 1 | """
 2 | Delete node tool for Alfresco MCP Server.
 3 | Self-contained tool for deleting documents or folders from Alfresco repository.
 4 | """
 5 | import logging
 6 | from typing import Optional
 7 | from fastmcp import Context
 8 | 
 9 | from ...utils.connection import ensure_connection
10 | from ...utils.json_utils import safe_format_output
11 | 
12 | logger = logging.getLogger(__name__)
13 | 
14 | 
15 | async def delete_node_impl(
16 |     node_id: str, 
17 |     permanent: bool = False,
18 |     ctx: Optional[Context] = None
19 | ) -> str:
20 |     """Delete a document or folder from Alfresco.
21 |     
22 |     Args:
23 |         node_id: Node ID to delete
24 |         permanent: Whether to permanently delete (bypass trash)
25 |         ctx: MCP context for progress reporting
26 |     
27 |     Returns:
28 |         Deletion confirmation
29 |     """
30 |     if ctx:
31 |         delete_type = "permanently delete" if permanent else "move to trash"
32 |         await ctx.info(f"Preparing to {delete_type}: {node_id}")
33 |         await ctx.info("Validating deletion request...")
34 |         await ctx.report_progress(0.1)
35 |     
36 |     if not node_id.strip():
37 |         return safe_format_output("❌ Error: node_id is required")
38 |     
39 |     try:
40 |         await ensure_connection()
41 |         from ...utils.connection import get_client_factory
42 |         
43 |         # Get client factory and create core client (working pattern from test)
44 |         client_factory = await get_client_factory()
45 |         core_client = client_factory.create_core_client()
46 |         
47 |         # Clean the node ID (remove any URL encoding or extra characters)
48 |         clean_node_id = node_id.strip()
49 |         if clean_node_id.startswith('alfresco://'):
50 |             # Extract node ID from URI format
51 |             clean_node_id = clean_node_id.split('/')[-1]
52 |         
53 |         logger.info(f"Attempting to delete node: {clean_node_id}")
54 |         
55 |         if ctx:
56 |             await ctx.report_progress(0.7)
57 |         
58 |         # Get node information first to validate it exists (working pattern from test)
59 |         node_response = core_client.nodes.get(clean_node_id)
60 |         
61 |         if not hasattr(node_response, 'entry'):
62 |             return safe_format_output(f"❌ Failed to get node information for: {clean_node_id}")
63 |         
64 |         node_info = node_response.entry
65 |         filename = getattr(node_info, 'name', f"document_{clean_node_id}")
66 |         
67 |         # Use the working high-level API pattern from test script
68 |         core_client.nodes.delete(clean_node_id)
69 |         
70 |         status = "permanently deleted" if permanent else "moved to trash"
71 |         logger.info(f"✅ Node {status}: {filename}")
72 |         
73 |         if ctx:
74 |             await ctx.report_progress(1.0)
75 |         return safe_format_output(f"""✅ **Deletion Complete**
76 | 
77 | 📄 **Node**: {node_info.name}
78 | 🗑️ **Status**: {status.title()}
79 | {"⚠️ **WARNING**: This action cannot be undone" if permanent else "ℹ️ **INFO**: Can be restored from trash"}
80 | 
81 | 🆔 **Node ID**: {clean_node_id}""")
82 |         
83 |     except Exception as e:
84 |         error_msg = f"ERROR: Deletion failed: {str(e)}"
85 |         if ctx:
86 |             await ctx.error(error_msg)
87 |         logger.error(f"Deletion failed: {e}")
88 |         return error_msg 
```

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

```python
  1 | """
  2 | Configuration management for MCP Server for Alfresco.
  3 | """
  4 | 
  5 | import os
  6 | from typing import Optional
  7 | from pydantic import BaseModel, Field
  8 | 
  9 | 
 10 | class AlfrescoConfig(BaseModel):
 11 |     """Configuration for MCP Server for Alfresco."""
 12 |     
 13 |     # Alfresco server connection
 14 |     alfresco_url: str = Field(
 15 |         default_factory=lambda: os.getenv("ALFRESCO_URL", "http://localhost:8080"),
 16 |         description="Alfresco server URL"
 17 |     )
 18 |     
 19 |     # Authentication
 20 |     username: str = Field(
 21 |         default_factory=lambda: os.getenv("ALFRESCO_USERNAME", "admin"),
 22 |         description="Alfresco username"
 23 |     )
 24 |     
 25 |     password: str = Field(
 26 |         default_factory=lambda: os.getenv("ALFRESCO_PASSWORD", "admin"),
 27 |         description="Alfresco password"
 28 |     )
 29 |     
 30 |     # Connection settings
 31 |     verify_ssl: bool = Field(
 32 |         default_factory=lambda: os.getenv("ALFRESCO_VERIFY_SSL", "false").lower() == "true",
 33 |         description="Verify SSL certificates"
 34 |     )
 35 |     
 36 |     timeout: int = Field(
 37 |         default_factory=lambda: int(os.getenv("ALFRESCO_TIMEOUT", "30")),
 38 |         description="Request timeout in seconds"
 39 |     )
 40 |     
 41 |     # MCP Server settings
 42 |     server_name: str = Field(
 43 |         default="python-alfresco-mcp-server",
 44 |         description="MCP server name"
 45 |     )
 46 |     
 47 |     server_version: str = Field(
 48 |         default="1.0.0",
 49 |         description="MCP server version"
 50 |     )
 51 |     
 52 |     # FastAPI settings (for HTTP transport)
 53 |     fastapi_host: str = Field(
 54 |         default_factory=lambda: os.getenv("FASTAPI_HOST", "localhost"),
 55 |         description="FastAPI host"
 56 |     )
 57 |     
 58 |     fastapi_port: int = Field(
 59 |         default_factory=lambda: int(os.getenv("FASTAPI_PORT", "8000")),
 60 |         description="FastAPI port"
 61 |     )
 62 |     
 63 |     fastapi_prefix: str = Field(
 64 |         default_factory=lambda: os.getenv("FASTAPI_PREFIX", "/mcp"),
 65 |         description="FastAPI URL prefix"
 66 |     )
 67 |     
 68 |     # Logging
 69 |     log_level: str = Field(
 70 |         default_factory=lambda: os.getenv("LOG_LEVEL", "INFO"),
 71 |         description="Logging level"
 72 |     )
 73 |     
 74 |     # Content settings
 75 |     max_file_size: int = Field(
 76 |         default_factory=lambda: int(os.getenv("MAX_FILE_SIZE", "100000000")),  # 100MB
 77 |         description="Maximum file size for uploads in bytes"
 78 |     )
 79 |     
 80 |     allowed_extensions: list[str] = Field(
 81 |         default_factory=lambda: [
 82 |             ".txt", ".pdf", ".doc", ".docx", ".xls", ".xlsx", 
 83 |             ".ppt", ".pptx", ".jpg", ".jpeg", ".png", ".gif", 
 84 |             ".zip", ".xml", ".json", ".csv"
 85 |         ],
 86 |         description="Allowed file extensions for uploads"
 87 |     )
 88 |     
 89 |     class Config:
 90 |         env_prefix = "ALFRESCO_"
 91 |         case_sensitive = False
 92 |         
 93 |     def model_post_init(self, __context) -> None:
 94 |         """Normalize URLs after initialization."""
 95 |         if self.alfresco_url.endswith("/"):
 96 |             self.alfresco_url = self.alfresco_url.rstrip("/")
 97 | 
 98 | 
 99 | def load_config() -> AlfrescoConfig:
100 |     """Load configuration from environment variables and defaults."""
101 |     return AlfrescoConfig()
102 | 
103 | # Global config instance for import
104 | config = load_config() 
```

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

```python
  1 | """
  2 | Create folder tool for Alfresco MCP Server.
  3 | Self-contained tool for creating folders in Alfresco repository.
  4 | """
  5 | import logging
  6 | from typing import Optional
  7 | from fastmcp import Context
  8 | 
  9 | from ...utils.connection import ensure_connection
 10 | from ...utils.json_utils import safe_format_output
 11 | 
 12 | logger = logging.getLogger(__name__)
 13 | 
 14 | 
 15 | async def create_folder_impl(
 16 |     folder_name: str, 
 17 |     parent_id: str = "-shared-", 
 18 |     description: str = "",
 19 |     ctx: Optional[Context] = None
 20 | ) -> str:
 21 |     """Create a new folder in Alfresco.
 22 |     
 23 |     Args:
 24 |         folder_name: Name of the new folder
 25 |         parent_id: Parent folder ID (default: shared folder)
 26 |         description: Folder description
 27 |         ctx: MCP context for progress reporting
 28 |     
 29 |     Returns:
 30 |         Folder creation confirmation with details
 31 |     """
 32 |     if ctx:
 33 |         await ctx.info(f">> Creating folder '{folder_name}' in {parent_id}")
 34 |         await ctx.info("Validating folder parameters...")
 35 |         await ctx.report_progress(0.0)
 36 |     
 37 |     if not folder_name.strip():
 38 |         return safe_format_output("❌ Error: folder_name is required")
 39 |     
 40 |     try:
 41 |         # Ensure connection and get client factory (working pattern from test)
 42 |         await ensure_connection()
 43 |         from ...utils.connection import get_client_factory
 44 |         
 45 |         # Get client factory and create core client (working pattern from test)
 46 |         client_factory = await get_client_factory()
 47 |         core_client = client_factory.create_core_client()
 48 |         
 49 |         if ctx:
 50 |             await ctx.info("Creating folder in Alfresco...")
 51 |             await ctx.report_progress(0.5)
 52 |         
 53 |         logger.info(f"Creating folder '{folder_name}' in parent {parent_id}")
 54 |         
 55 |         # Prepare properties
 56 |         properties = {"cm:title": folder_name}
 57 |         if description:
 58 |             properties["cm:description"] = description
 59 |         
 60 |         logger.info(f"Using high-level API: core_client.nodes.create_folder()")
 61 |         
 62 |         # Use the working high-level API pattern from test script
 63 |         folder_response = core_client.nodes.create_folder(
 64 |             name=folder_name,
 65 |             parent_id=parent_id,
 66 |             properties=properties
 67 |         )
 68 |         
 69 |         if folder_response and hasattr(folder_response, 'entry'):
 70 |             entry = folder_response.entry
 71 |             logger.info("✅ Folder created successfully")
 72 |             
 73 |             # Extract folder details from response
 74 |             folder_id = getattr(entry, 'id', 'Unknown')
 75 |             folder_name_response = getattr(entry, 'name', folder_name)
 76 |             created_at = getattr(entry, 'createdAt', 'Unknown')
 77 |             node_type = getattr(entry, 'nodeType', 'cm:folder')
 78 |         else:
 79 |             raise Exception(f"Failed to create folder - invalid response from core client")
 80 |         
 81 |         if ctx:
 82 |             await ctx.info("Processing folder creation response...")
 83 |             await ctx.report_progress(0.9)
 84 |         
 85 |         if ctx:
 86 |             await ctx.info("Folder created!")
 87 |             await ctx.report_progress(1.0)
 88 |             await ctx.info(f"SUCCESS: Folder '{folder_name_response}' created successfully")
 89 |             
 90 |         # Clean JSON-friendly formatting (no markdown syntax)
 91 |         return safe_format_output(f"""✅ Folder Created Successfully!
 92 | 
 93 | 📁 Name: {folder_name_response}
 94 | 🆔 Folder ID: {folder_id}
 95 | 📍 Parent: {parent_id}
 96 | 📅 Created: {created_at}
 97 | 🏷️ Type: {node_type}
 98 | 📝 Description: {description or 'None'}""")
 99 |         
100 |     except Exception as e:
101 |         error_msg = f"❌ Folder creation failed: {str(e)}"
102 |         if ctx:
103 |             await ctx.error(error_msg)
104 |         logger.error(f"Folder creation failed: {e}")
105 |         return safe_format_output(error_msg) 
```

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

```markdown
  1 | # Testing with MCP Inspector
  2 | 
  3 | ## Step 1: Start MCP Inspector
  4 | 
  5 | ```bash
  6 | # Launch MCP Inspector
  7 | npx @modelcontextprotocol/inspector
  8 | ```
  9 | 
 10 | This will open a web interface (usually at http://localhost:3000) where you can interactively test your MCP server.
 11 | 
 12 | ## Step 2: Configure Server Connection
 13 | 
 14 | In the MCP Inspector interface:
 15 | 
 16 | 1. **Server Type**: Select "stdio"
 17 | 2. **Command**: Enter `python`
 18 | 3. **Arguments**: Add these as separate entries:
 19 |    - `-m`
 20 |    - `alfresco_mcp_server.fastmcp_server`
 21 | 4. **Environment Variables** (if needed):
 22 |    - `ALFRESCO_URL`: `http://localhost:8080`
 23 |    - `ALFRESCO_USERNAME`: `admin`
 24 |    - `ALFRESCO_PASSWORD`: `admin`
 25 |    - `ALFRESCO_VERIFY_SSL`: `false`
 26 | 
 27 | ## Step 3: Connect and Explore
 28 | 
 29 | ### Available Tools (15 total):
 30 | 1. **search_content** - Search documents and folders
 31 | 2. **search_by_metadata** - Search by metadata properties
 32 | 3. **advanced_search** - Advanced search with filters
 33 | 4. **cmis_search** - CMIS SQL-based search
 34 | 5. **upload_document** - Upload new documents
 35 | 6. **download_document** - Download document content
 36 | 7. **browse_repository** - Browse repository structure
 37 | 8. **repository_info** - Get repository information and status
 38 | 9. **checkout_document** - Check out for editing
 39 | 10. **checkin_document** - Check in after editing
 40 | 11. **cancel_checkout** - Cancel document checkout
 41 | 12. **delete_node** - Delete documents/folders
 42 | 13. **get_node_properties** - Get node metadata
 43 | 14. **update_node_properties** - Update node metadata
 44 | 15. **create_folder** - Create new folders
 45 | 
 46 | ### Available Resources (5 total):
 47 | 1. **alfresco://repository/info** - Repository information
 48 | 2. **alfresco://repository/health** - Health status
 49 | 3. **alfresco://repository/stats** - Usage statistics
 50 | 4. **alfresco://repository/config** - Configuration details
 51 | 5. **alfresco://repository/{section}** - Dynamic repository info
 52 | 
 53 | ### Available Prompts (1 total):
 54 | 1. **search_and_analyze** - AI-friendly search template
 55 | 
 56 | ## Step 4: Test Examples
 57 | 
 58 | ### Quick Tests (No Alfresco Required):
 59 | - List tools: Should show all 15 tools
 60 | - List resources: Should show all 5 resources
 61 | - List prompts: Should show search_and_analyze prompt
 62 | 
 63 | ### With Live Alfresco Server:
 64 | 1. **Test Search**: 
 65 |    - Tool: `search_content`
 66 |    - Parameters: `{"query": "test", "max_results": 5}`
 67 | 
 68 | 2. **Test Repository Info**:
 69 |    - Resource: `alfresco://repository/info`
 70 | 
 71 | 3. **Test Create Folder**:
 72 |    - Tool: `create_folder`
 73 |    - Parameters: `{"folder_name": "MCP Test Folder", "description": "Created via MCP Inspector"}`
 74 | 
 75 | ## Step 5: Advanced Testing
 76 | 
 77 | ### Error Handling:
 78 | - Try invalid parameters
 79 | - Test without Alfresco connection
 80 | - Test with wrong credentials
 81 | 
 82 | ### Performance:
 83 | - Large search queries
 84 | - Multiple concurrent operations
 85 | - File upload/download operations
 86 | 
 87 | ## Troubleshooting
 88 | 
 89 | ### Common Issues:
 90 | 1. **Inspector won't start**: Check Node.js version, try `npm install -g @modelcontextprotocol/inspector`
 91 | 2. **Server connection fails**: Verify Python path and module installation
 92 | 3. **Alfresco errors**: Check server status, credentials, and network connectivity
 93 | 4. **Tool execution fails**: Verify parameters match schema requirements
 94 | 
 95 | ### Environment Setup:
 96 | ```bash
 97 | # Windows PowerShell
 98 | $env:ALFRESCO_URL="http://localhost:8080"
 99 | $env:ALFRESCO_USERNAME="admin"
100 | $env:ALFRESCO_PASSWORD="admin"
101 | $env:ALFRESCO_VERIFY_SSL="false"
102 | 
103 | # Or use .env file (recommended)
104 | # Copy sample-dot-env.txt to .env and modify
105 | ```
106 | 
107 | ## Next Steps
108 | 
109 | 1. **Start simple**: Test tool/resource listing first
110 | 2. **Add credentials**: Set up environment variables for Alfresco
111 | 3. **Test incrementally**: One tool at a time
112 | 4. **Explore features**: Try different parameters and combinations
113 | 5. **Production testing**: Test with your actual Alfresco deployment 
```

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

```python
  1 | """
  2 | Connection utilities for Alfresco MCP Server.
  3 | Handles client creation and connection management.
  4 | """
  5 | import logging
  6 | import os
  7 | from typing import Optional
  8 | 
  9 | 
 10 | logger = logging.getLogger(__name__)
 11 | 
 12 | # Global connection cache
 13 | _master_client = None
 14 | _client_factory = None
 15 | 
 16 | def get_alfresco_config() -> dict:
 17 |     """Get Alfresco configuration from environment variables."""
 18 |     return {
 19 |         'alfresco_url': os.getenv('ALFRESCO_URL', 'http://localhost:8080'),
 20 |         'username': os.getenv('ALFRESCO_USERNAME', 'admin'),
 21 |         'password': os.getenv('ALFRESCO_PASSWORD', 'admin'),
 22 |         'verify_ssl': os.getenv('ALFRESCO_VERIFY_SSL', 'false').lower() == 'true',
 23 |         'timeout': int(os.getenv('ALFRESCO_TIMEOUT', '30'))
 24 |     }
 25 | 
 26 | 
 27 | async def ensure_connection():
 28 |     """Ensure we have a working connection to Alfresco using python-alfresco-api."""
 29 |     global _master_client, _client_factory
 30 |     
 31 |     if _master_client is None:
 32 |         try:
 33 |             # Import here to avoid circular imports
 34 |             from python_alfresco_api import ClientFactory
 35 |             
 36 |             config = get_alfresco_config()
 37 |             
 38 |             logger.info(">> Creating Alfresco clients...")
 39 |             
 40 |             # Use ClientFactory to create authenticated client (original Sunday pattern)
 41 |             factory = ClientFactory(
 42 |                 base_url=config['alfresco_url'],
 43 |                 username=config['username'],
 44 |                 password=config['password'],
 45 |                 verify_ssl=config['verify_ssl'],
 46 |                 timeout=config['timeout']
 47 |             )
 48 |             
 49 |             # Store the factory globally for other functions to use
 50 |             _client_factory = factory
 51 |             
 52 |             _master_client = factory.create_master_client()
 53 |             logger.info("Master client created successfully")
 54 |                         
 55 |             # Test connection - use method that initializes and gets
 56 |             try:
 57 |                 # Use ensure_httpx_client to initialize, then test simple call
 58 |                 _master_client.core.ensure_httpx_client()
 59 |                 logger.info("Connection test successful!")
 60 |             except Exception as conn_error:
 61 |                 logger.warning(f"Connection test failed: {conn_error}")
 62 |             
 63 |         except Exception as e:
 64 |             logger.error(f"ERROR: Failed to create clients: {str(e)}")
 65 |             raise e
 66 |     
 67 |     return _master_client
 68 | 
 69 | 
 70 | def get_connection():
 71 |     """Get the cached connection without async (for sync operations)."""
 72 |     return _master_client
 73 | 
 74 | 
 75 | async def get_search_client():
 76 |     """Get the search client for search operations (using master_client for auth compatibility)."""
 77 |     master_client = await ensure_connection()
 78 |     # Return master_client which has simple_search access and working authentication
 79 |     return master_client
 80 | 
 81 | 
 82 | async def get_core_client():
 83 |     """Get the core client for core operations (using master_client for auth compatibility)."""
 84 |     master_client = await ensure_connection()
 85 |     # Return the actual core client that has nodes, folders, etc.
 86 |     return master_client.core
 87 | 
 88 | 
 89 | async def get_client_factory():
 90 |     """Get the client factory for advanced operations."""
 91 |     await ensure_connection()
 92 |     if not _client_factory:
 93 |         raise RuntimeError("Connection not initialized. Call ensure_connection() first.")
 94 |     return _client_factory
 95 | 
 96 | 
 97 | def get_search_utils():
 98 |     """Get the search_utils module from python-alfresco-api."""
 99 |     try:
100 |         from python_alfresco_api.utils import search_utils
101 |         return search_utils
102 |     except ImportError as e:
103 |         logger.error(f"Failed to import search_utils: {e}")
104 |         raise
105 | 
106 | 
107 | def get_node_utils():
108 |     """Get the node_utils module from python-alfresco-api."""
109 |     try:
110 |         from python_alfresco_api.utils import node_utils
111 |         return node_utils
112 |     except ImportError as e:
113 |         logger.error(f"Failed to import node_utils: {e}")
114 |         raise
```

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

```python
  1 | """
  2 | JSON utilities for Alfresco MCP Server.
  3 | Handles proper Unicode emoji encoding for MCP protocol transport.
  4 | """
  5 | import json
  6 | import logging
  7 | 
  8 | 
  9 | logger = logging.getLogger(__name__)
 10 | 
 11 | 
 12 | def make_json_safe(text: str) -> str:
 13 |     """
 14 |     Make text JSON-safe for MCP protocol transport.
 15 |     Properly encodes Unicode emojis to prevent character map errors.
 16 |     
 17 |     Args:
 18 |         text: Input text that may contain Unicode emojis
 19 |         
 20 |     Returns:
 21 |         JSON-safe text with properly encoded Unicode characters
 22 |     """
 23 |     if not text:
 24 |         return text
 25 |         
 26 |     try:
 27 |         # Ensure proper Unicode normalization
 28 |         import unicodedata
 29 |         normalized = unicodedata.normalize('NFC', text)
 30 |         
 31 |         # Test if it can be safely JSON serialized
 32 |         json.dumps(normalized)
 33 |         return normalized
 34 |         
 35 |     except (UnicodeError, TypeError) as e:
 36 |         logger.warning(f"Unicode encoding issue, falling back to ASCII: {e}")
 37 |         # Fall back to ASCII-safe version with emoji descriptions
 38 |         return text.encode('ascii', errors='ignore').decode('ascii')
 39 | 
 40 | 
 41 | def safe_format_output(text: str) -> str:
 42 |     """
 43 |     Format output text to be safe for MCP JSON transport.
 44 |     Replaces emojis with text equivalents to prevent character map errors.
 45 |     
 46 |     Args:
 47 |         text: Text to format
 48 |         
 49 |     Returns:
 50 |         Safely formatted text with emojis replaced
 51 |     """
 52 |     if not text:
 53 |         return text
 54 |         
 55 |     try:
 56 |         # Define emoji replacements for common ones used in the tools
 57 |         emoji_replacements = {
 58 |             '🔗': '[LINK]',
 59 |             '🔓': '[UNLOCKED]', 
 60 |             '📄': '[DOCUMENT]',
 61 |             '🆔': '[ID]',
 62 |             '📏': '[SIZE]',
 63 |             '💾': '[SAVED]',
 64 |             '🔒': '[LOCKED]',
 65 |             '🕒': '[TIME]',
 66 |             '📥': '[DOWNLOAD]',
 67 |             'ℹ️': '[INFO]',
 68 |             '⚠️': '[WARNING]',
 69 |             '👤': '[USER]',
 70 |             '✅': '[SUCCESS]',
 71 |             '❌': '[ERROR]',
 72 |             '🏷️': '[TAG]',
 73 |             '🧩': '[MODULE]',
 74 |             '📁': '[FOLDER]',
 75 |             '📍': '[LOCATION]',
 76 |             '📅': '[DATE]',
 77 |             '📝': '[NOTE]',
 78 |             '🔢': '[VERSION]',
 79 |             '📊': '[SIZE]',
 80 |             '🗑️': '[DELETE]',
 81 |             '🔍': '[SEARCH]',
 82 |             '📤': '[UPLOAD]',
 83 |             '🧹': '[CLEANUP]',
 84 |             '🏢': '[REPOSITORY]',
 85 |             '🔧': '[TOOL]',
 86 |             '📦': '[PACKAGE]'
 87 |         }
 88 |         
 89 |         # Replace emojis with text equivalents
 90 |         safe_text = text
 91 |         for emoji, replacement in emoji_replacements.items():
 92 |             safe_text = safe_text.replace(emoji, replacement)
 93 |         
 94 |         # Test if the result is JSON-safe
 95 |         test_json = json.dumps(safe_text, ensure_ascii=True)
 96 |         json.loads(test_json)
 97 |         
 98 |         return safe_text
 99 |         
100 |     except Exception as e:
101 |         logger.warning(f"JSON formatting issue: {e}")
102 |         try:
103 |             # Ultimate fallback: remove all non-ASCII characters
104 |             ascii_text = text.encode('ascii', errors='ignore').decode('ascii')
105 |             return ascii_text
106 |         except Exception as fallback_error:
107 |             logger.error(f"ASCII fallback failed: {fallback_error}")
108 |             return "Error: Text encoding failed"
109 | 
110 | 
111 | def escape_unicode_for_json(text: str) -> str:
112 |     """
113 |     Alternative approach: explicitly escape Unicode characters for JSON.
114 |     Use this if the regular approach doesn't work.
115 |     
116 |     Args:
117 |         text: Input text with Unicode characters
118 |         
119 |     Returns:
120 |         Text with Unicode characters escaped for JSON
121 |     """
122 |     if not text:
123 |         return text
124 |         
125 |     try:
126 |         # Use json.dumps to properly escape Unicode, then remove the quotes
127 |         escaped = json.dumps(text, ensure_ascii=False)
128 |         # Remove the surrounding quotes added by json.dumps
129 |         if escaped.startswith('"') and escaped.endswith('"'):
130 |             escaped = escaped[1:-1]
131 |         return escaped
132 |         
133 |     except Exception as e:
134 |         logger.warning(f"Unicode escaping failed: {e}")
135 |         return text 
```

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

```markdown
  1 | # Installation with pip and pipx
  2 | 
  3 | 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).
  4 | 
  5 | ## 🛠️ Installation Options
  6 | 
  7 | ### Option 1: pipx (Recommended for Traditional Methods)
  8 | 
  9 | pipx automatically creates isolated environments for each tool while making commands globally available - eliminates dependency conflicts while providing system-wide access.
 10 | 
 11 | ```bash
 12 | # First install pipx if you don't have it (one-time setup)
 13 | pip install pipx
 14 | 
 15 | # Install python-alfresco-mcp-server in isolated environment
 16 | pipx install python-alfresco-mcp-server
 17 | 
 18 | # Test that installation worked
 19 | python-alfresco-mcp-server --help
 20 | ```
 21 | 
 22 | **Why pipx?** pipx automatically creates isolated environments for each tool while making commands globally available - eliminates dependency conflicts while providing system-wide access.
 23 | 
 24 | ### Option 2: pip (Traditional Package Manager)
 25 | 
 26 | ```bash
 27 | # Recommended: Create virtual environment first
 28 | python -m venv venv
 29 | source venv/bin/activate     # Linux/macOS
 30 | # venv\Scripts\activate      # Windows
 31 | 
 32 | # Install python-alfresco-mcp-server
 33 | pip install python-alfresco-mcp-server
 34 | 
 35 | # Test that installation worked
 36 | python-alfresco-mcp-server --help
 37 | ```
 38 | 
 39 | ## 🚀 Usage
 40 | 
 41 | ### MCP Server Startup
 42 | 
 43 | **With pipx (Global installation - no venv needed):**
 44 | 
 45 | ```bash
 46 | # Run MCP server with STDIO transport (default)
 47 | python-alfresco-mcp-server
 48 | 
 49 | # HTTP transport for web services (matches MCP Inspector)
 50 | python-alfresco-mcp-server --transport http --host 127.0.0.1 --port 8003
 51 | 
 52 | # SSE transport for real-time streaming  
 53 | python-alfresco-mcp-server --transport sse --host 127.0.0.1 --port 8001
 54 | ```
 55 | 
 56 | **With pip (Activate venv first if installed in one):**
 57 | 
 58 | ```bash
 59 | # Activate virtual environment first (if used during installation)
 60 | source venv/bin/activate     # Linux/macOS
 61 | # venv\Scripts\activate      # Windows
 62 | 
 63 | # Run MCP server with STDIO transport (default)
 64 | python-alfresco-mcp-server
 65 | 
 66 | # HTTP transport for web services (matches MCP Inspector)
 67 | python-alfresco-mcp-server --transport http --host 127.0.0.1 --port 8003
 68 | 
 69 | # SSE transport for real-time streaming  
 70 | python-alfresco-mcp-server --transport sse --host 127.0.0.1 --port 8001
 71 | ```
 72 | 
 73 | ## 🔧 Claude Desktop Configuration
 74 | 
 75 | The Claude Desktop configuration differs based on how you installed the MCP server:
 76 | 
 77 | ### pipx Installation (Global Tool)
 78 | 
 79 | ```json
 80 | {
 81 |   "command": "python-alfresco-mcp-server",
 82 |   "args": ["--transport", "stdio"]
 83 | }
 84 | ```
 85 | 
 86 | - Uses the **global command name** directly (no path needed)
 87 | - pipx makes tools globally available in your PATH
 88 | - Simplest configuration
 89 | 
 90 | **Sample Config Files:**
 91 | - Windows: [`claude-desktop-config-pipx-windows.json`](../claude-desktop-config-pipx-windows.json)
 92 | - macOS: [`claude-desktop-config-pipx-macos.json`](../claude-desktop-config-pipx-macos.json)
 93 | 
 94 | ### pip Installation (Manual venv)
 95 | 
 96 | ```json
 97 | {
 98 |   "command": "C:\\path\\to\\venv\\Scripts\\python-alfresco-mcp-server.exe",
 99 |   "args": ["--transport", "stdio"]
100 | }
101 | ```
102 | 
103 | - Uses **direct path to executable** in your virtual environment
104 | - Path points to `Scripts/` directory in your venv (Windows) or `bin/` (Linux/macOS)
105 | - Replace `C:\\path\\to\\venv` with your actual venv location
106 | 
107 | ## 🔍 MCP Inspector Configuration
108 | 
109 | For development and testing with MCP Inspector:
110 | 
111 | ### pipx Installation
112 | 
113 | Use the sample config files:
114 | - **stdio transport**: [`mcp-inspector-stdio-pipx-config.json`](../mcp-inspector-stdio-pipx-config.json)
115 | - **http transport**: [`mcp-inspector-http-pipx-config.json`](../mcp-inspector-http-pipx-config.json)
116 | 
117 | ```bash
118 | # Start with stdio transport
119 | npx @modelcontextprotocol/inspector --config mcp-inspector-stdio-pipx-config.json --server python-alfresco-mcp-server
120 | 
121 | # Start with http transport  
122 | npx @modelcontextprotocol/inspector --config mcp-inspector-http-pipx-config.json --server python-alfresco-mcp-server
123 | ```
124 | 
125 | ### pip Installation
126 | 
127 | Copy one of the pipx sample files above and modify the `"command"` field to point to your venv executable:
128 | - Change `"python-alfresco-mcp-server"` to `"C:\\path\\to\\venv\\Scripts\\python-alfresco-mcp-server.exe"` (Windows)
129 | - Or `"/path/to/venv/bin/python-alfresco-mcp-server"` (Linux/macOS) 
```

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

```toml
  1 | [build-system]
  2 | requires = ["setuptools>=61.0", "wheel"]
  3 | build-backend = "setuptools.build_meta"
  4 | 
  5 | [project]
  6 | name = "python-alfresco-mcp-server"
  7 | version = "1.1.0"
  8 | description = "FastMCP 2.0 server for Alfresco Content Services integration"
  9 | authors = [{name = "Steve Reiner", email = "[email protected]"}]
 10 | license = {text = "Apache-2.0"}
 11 | readme = "README.md"
 12 | requires-python = ">=3.10"
 13 | keywords = ["alfresco", "mcp", "content-management", "fastmcp", "ai"]
 14 | classifiers = [
 15 |     "Development Status :: 4 - Beta",
 16 |     "Intended Audience :: Developers",
 17 |     "License :: OSI Approved :: Apache Software License",
 18 |     "Programming Language :: Python :: 3",
 19 |     "Programming Language :: Python :: 3.10",
 20 |     "Programming Language :: Python :: 3.11",
 21 |     "Programming Language :: Python :: 3.12",
 22 |     "Programming Language :: Python :: 3.13",
 23 |     "Topic :: Software Development :: Libraries :: Python Modules",
 24 |     "Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Content Management System",
 25 |     "Topic :: Internet :: WWW/HTTP :: Indexing/Search",
 26 |     "Topic :: Database :: Database Engines/Servers",
 27 |     "Topic :: Scientific/Engineering :: Artificial Intelligence",
 28 | ]
 29 | 
 30 | dependencies = [
 31 |     # FastMCP 2.0 - Modern MCP framework with enhanced features
 32 |     "fastmcp>=2.9.0",
 33 |     
 34 |     # Alfresco integration
 35 |     "python-alfresco-api>=1.1.1",
 36 |     
 37 |     # Configuration and utilities
 38 |     "pydantic>=2.0.0",
 39 |     "pydantic-settings>=2.0.0",
 40 |     "PyYAML>=6.0",
 41 |     
 42 |     # HTTP client
 43 |     "httpx>=0.24.0",
 44 |     
 45 |     # File handling
 46 |     "python-multipart>=0.0.6",
 47 | ]
 48 | 
 49 | [project.urls]
 50 | Homepage = "https://github.com/stevereiner/python-alfresco-mcp-server"
 51 | Repository = "https://github.com/stevereiner/python-alfresco-mcp-server"
 52 | Issues = "https://github.com/stevereiner/python-alfresco-mcp-server/issues"
 53 | Documentation = "https://github.com/stevereiner/python-alfresco-mcp-server#readme"
 54 | 
 55 | [project.optional-dependencies]
 56 | dev = [
 57 |     "black>=23.0.0",
 58 |     "ruff>=0.1.0",
 59 |     "mypy>=1.0.0",
 60 | ]
 61 | test = [
 62 |     "pytest>=7.0.0",
 63 |     "pytest-asyncio>=0.21.0",
 64 |     "pytest-cov>=4.0.0",
 65 |     "pytest-xdist>=3.0.0",
 66 |     "pytest-mock>=3.10.0",
 67 |     "coverage[toml]>=7.0.0",
 68 |     "httpx>=0.24.0",
 69 | ]
 70 | all = [
 71 |     "python-alfresco-mcp-server[dev]",
 72 |     "python-alfresco-mcp-server[test]",
 73 | ]
 74 | 
 75 | [project.scripts]
 76 | python-alfresco-mcp-server = "alfresco_mcp_server.fastmcp_server:main"
 77 | 
 78 | [tool.setuptools]
 79 | include-package-data = true
 80 | 
 81 | [tool.setuptools.packages.find]
 82 | where = ["."]
 83 | include = ["alfresco_mcp_server*"]
 84 | exclude = ["tests*", "tests-debug*", "venv*", "*.egg-info*"]
 85 | 
 86 | [tool.setuptools.package-data]
 87 | alfresco_mcp_server = [
 88 |     "*.py",
 89 |     "*.yaml", 
 90 |     "*.yml",
 91 |     "*.json",
 92 |     "*.md",
 93 |     "tools/**/*.py",
 94 |     "resources/**/*.py", 
 95 |     "prompts/**/*.py",
 96 |     "utils/**/*.py",
 97 | ]
 98 | 
 99 | [tool.pytest.ini_options]
100 | minversion = "7.0"
101 | addopts = [
102 |     "-ra",
103 |     "-v",
104 |     "--tb=short",
105 |     "--strict-markers",
106 |     "--disable-warnings",
107 | ]
108 | testpaths = ["tests"]
109 | filterwarnings = [
110 |     "ignore::DeprecationWarning",
111 |     "ignore::PendingDeprecationWarning",
112 | ]
113 | markers = [
114 |     "asyncio: marks tests as async",
115 |     "integration: marks tests as integration tests requiring live Alfresco server",
116 |     "unit: marks tests as unit tests",
117 |     "fastmcp: marks tests as FastMCP 2.0 specific tests",
118 | ]
119 | asyncio_mode = "auto"
120 | 
121 | [tool.coverage.run]
122 | source = ["alfresco_mcp_server"]
123 | omit = [
124 |     "*/tests/*",
125 |     "*/venv/*",
126 |     "*/__pycache__/*",
127 | ]
128 | 
129 | [tool.coverage.report]
130 | exclude_lines = [
131 |     "pragma: no cover",
132 |     "def __repr__",
133 |     "if self.debug:",
134 |     "if settings.DEBUG",
135 |     "raise AssertionError",
136 |     "raise NotImplementedError",
137 |     "if 0:",
138 |     "if __name__ == .__main__.:",
139 |     "class .*\\bProtocol\\):",
140 |     "@(abc\\.)?abstractmethod",
141 | ]
142 | 
143 | [tool.black]
144 | line-length = 88
145 | target-version = ['py310']
146 | include = '\.pyi?$'
147 | extend-exclude = '''
148 | /(
149 |   # directories
150 |   \.eggs
151 |   | \.git
152 |   | \.hg
153 |   | \.mypy_cache
154 |   | \.tox
155 |   | \.venv
156 |   | venv
157 |   | build
158 |   | dist
159 | )/
160 | '''
161 | 
162 | [tool.ruff]
163 | target-version = "py310"
164 | line-length = 88
165 | select = [
166 |     "E",  # pycodestyle errors
167 |     "W",  # pycodestyle warnings
168 |     "F",  # pyflakes
169 |     "I",  # isort
170 |     "B",  # flake8-bugbear
171 |     "C4", # flake8-comprehensions
172 |     "UP", # pyupgrade
173 | ]
174 | ignore = [
175 |     "E501",  # line too long, handled by black
176 |     "B008",  # do not perform function calls in argument defaults
177 |     "C901",  # too complex
178 | ]
179 | 
180 | [tool.ruff.per-file-ignores]
181 | "__init__.py" = ["F401"]
182 | "tests/**/*" = ["B018", "B019"]
183 | 
184 | [tool.mypy]
185 | python_version = "3.10"
186 | check_untyped_defs = true
187 | disallow_any_generics = true
188 | disallow_incomplete_defs = true
189 | disallow_untyped_defs = true
190 | no_implicit_optional = true
191 | warn_redundant_casts = true
192 | warn_unused_ignores = true
193 | warn_return_any = true
194 | strict_equality = true
195 | 
196 | [dependency-groups]
197 | dev = [
198 |     "build>=1.2.2.post1",
199 |     "twine>=6.1.0",
200 | ]
201 | 
```

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

```python
  1 | #!/usr/bin/env python3
  2 | """
  3 | Quick Start Example for Alfresco MCP Server
  4 | 
  5 | This example demonstrates:
  6 | - Basic server connection
  7 | - Making your first tool call
  8 | - Handling responses
  9 | - Simple error handling
 10 | 
 11 | Prerequisites:
 12 | - Alfresco MCP Server running
 13 | - Environment variables set (ALFRESCO_URL, ALFRESCO_USERNAME, ALFRESCO_PASSWORD)
 14 | """
 15 | 
 16 | import asyncio
 17 | import os
 18 | from fastmcp import Client
 19 | from alfresco_mcp_server.fastmcp_server import mcp
 20 | 
 21 | 
 22 | async def quick_start_example():
 23 |     """Quick start example showing basic MCP server usage."""
 24 |     
 25 |     print("🚀 Alfresco MCP Server - Quick Start Example")
 26 |     print("=" * 50)
 27 |     
 28 |     # Check environment setup
 29 |     print("\n📋 Checking Environment Setup...")
 30 |     required_vars = ["ALFRESCO_URL", "ALFRESCO_USERNAME", "ALFRESCO_PASSWORD"]
 31 |     for var in required_vars:
 32 |         value = os.getenv(var)
 33 |         if value:
 34 |             print(f"✅ {var}: {value}")
 35 |         else:
 36 |             print(f"❌ {var}: Not set (using defaults)")
 37 |     
 38 |     try:
 39 |         # Connect to the MCP server
 40 |         print("\n🔌 Connecting to MCP Server...")
 41 |         async with Client(mcp) as client:
 42 |             print("✅ Connected successfully!")
 43 |             
 44 |             # List available tools
 45 |             print("\n🛠️ Available Tools:")
 46 |             tools = await client.list_tools()
 47 |             for i, tool in enumerate(tools, 1):
 48 |                 print(f"  {i:2d}. {tool.name} - {tool.description}")
 49 |             
 50 |             # List available resources
 51 |             print("\n📚 Available Resources:")
 52 |             resources = await client.list_resources()
 53 |             for i, resource in enumerate(resources, 1):
 54 |                 print(f"  {i:2d}. {resource.uri}")
 55 |             
 56 |             # List available prompts
 57 |             print("\n💭 Available Prompts:")
 58 |             prompts = await client.list_prompts()
 59 |             for i, prompt in enumerate(prompts, 1):
 60 |                 print(f"  {i:2d}. {prompt.name} - {prompt.description}")
 61 |             
 62 |             # Example 1: Simple search
 63 |             print("\n🔍 Example 1: Simple Document Search")
 64 |             print("-" * 40)
 65 |             search_result = await client.call_tool("search_content", {
 66 |                 "query": "*",  # Search for all documents
 67 |                 "max_results": 5
 68 |             })
 69 |             
 70 |             if search_result:
 71 |                 print("Search Result:")
 72 |                 print(search_result[0].text)
 73 |             
 74 |             # Example 2: Get repository info
 75 |             print("\n📊 Example 2: Repository Information")
 76 |             print("-" * 40)
 77 |             repo_info = await client.read_resource("alfresco://repository/info")
 78 |             if repo_info:
 79 |                 print("Repository Info:")
 80 |                 print(repo_info[0].text)
 81 |             
 82 |             # Example 3: Create a test folder
 83 |             print("\n📁 Example 3: Create Test Folder")
 84 |             print("-" * 40)
 85 |             folder_result = await client.call_tool("create_folder", {
 86 |                 "folder_name": f"MCP_Test_Folder_{asyncio.current_task().get_name()}",
 87 |                 "parent_id": "-root-",
 88 |                 "description": "Test folder created by MCP Quick Start example"
 89 |             })
 90 |             
 91 |             if folder_result:
 92 |                 print("Folder Creation Result:")
 93 |                 print(folder_result[0].text)
 94 |             
 95 |             # Example 4: Get analysis prompt
 96 |             print("\n💡 Example 4: Analysis Prompt")
 97 |             print("-" * 40)
 98 |             prompt_result = await client.get_prompt("search_and_analyze", {
 99 |                 "query": "financial reports",
100 |                 "analysis_type": "summary"
101 |             })
102 |             
103 |             if prompt_result.messages:
104 |                 print("Generated Prompt:")
105 |                 print(prompt_result.messages[0].content.text[:300] + "...")
106 |             
107 |             print("\n✅ Quick Start Complete!")
108 |             print("Next steps:")
109 |             print("- Explore other examples in this directory")
110 |             print("- Check the documentation in ../docs/")
111 |             print("- Try the document lifecycle example")
112 |             
113 |     except Exception as e:
114 |         print(f"\n❌ Error: {e}")
115 |         print("\nTroubleshooting:")
116 |         print("1. Ensure Alfresco server is running")
117 |         print("2. Check environment variables")
118 |         print("3. Verify network connectivity")
119 |         return False
120 |     
121 |     return True
122 | 
123 | 
124 | def main():
125 |     """Main function to run the quick start example."""
126 |     print("Starting Alfresco MCP Server Quick Start Example...")
127 |     
128 |     # Run the async example
129 |     success = asyncio.run(quick_start_example())
130 |     
131 |     if success:
132 |         print("\n🎉 Example completed successfully!")
133 |     else:
134 |         print("\n💥 Example failed. Please check the error messages above.")
135 | 
136 | 
137 | if __name__ == "__main__":
138 |     main() 
```

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

```python
  1 | """
  2 | Update node properties tool for Alfresco MCP Server.
  3 | Self-contained tool for updating document/folder metadata and properties.
  4 | """
  5 | import logging
  6 | import os
  7 | from typing import Optional
  8 | from fastmcp import Context
  9 | 
 10 | from ...utils.connection import ensure_connection, get_core_client
 11 | 
 12 | logger = logging.getLogger(__name__)
 13 | 
 14 | 
 15 | async def update_node_properties_impl(
 16 |     node_id: str,
 17 |     name: str = "",
 18 |     title: str = "",
 19 |     description: str = "",
 20 |     author: str = "",
 21 |     ctx: Optional[Context] = None
 22 | ) -> str:
 23 |     """Update metadata and properties of a document or folder.
 24 |     
 25 |     Args:
 26 |         node_id: Node ID to update (required)
 27 |         name: New name for the node (optional)
 28 |         title: Document title (cm:title) (optional)
 29 |         description: Document description (cm:description) (optional)
 30 |         author: Document author (cm:author) (optional)
 31 |         ctx: MCP context for progress reporting
 32 |     
 33 |     Returns:
 34 |         Update confirmation with changes made
 35 |     """
 36 |     if ctx:
 37 |         await ctx.info(f"Updating properties for: {node_id}")
 38 |         await ctx.report_progress(0.1)
 39 |     
 40 |     if not node_id.strip():
 41 |         return "ERROR: node_id is required"
 42 |     
 43 |     if not any([name, title, description, author]):
 44 |         return "ERROR: At least one property (name, title, description, or author) must be provided"
 45 |     
 46 |     try:
 47 |         # Ensure connection and get core client
 48 |         await ensure_connection()
 49 |         core_client = await get_core_client()
 50 |         
 51 |         # Clean the node ID (remove any URL encoding or extra characters)
 52 |         clean_node_id = node_id.strip()
 53 |         if clean_node_id.startswith('alfresco://'):
 54 |             # Extract node ID from URI format
 55 |             clean_node_id = clean_node_id.split('/')[-1]
 56 |         
 57 |         logger.info(f"Updating properties for node: {clean_node_id}")
 58 |         
 59 |         if ctx:
 60 |             await ctx.report_progress(0.3)
 61 |         
 62 |         # Get node information first to validate it exists
 63 |         try:
 64 |             node_response = core_client.nodes.get(node_id=clean_node_id)
 65 |             if not hasattr(node_response, 'entry'):
 66 |                 return f"ERROR: Failed to get node information for: {clean_node_id}"
 67 |             
 68 |             node_info = node_response.entry
 69 |             current_name = getattr(node_info, 'name', f"document_{clean_node_id}")
 70 |             
 71 |         except Exception as get_error:
 72 |             return f"ERROR: Failed to validate node {clean_node_id}: {str(get_error)}"
 73 |         
 74 |         if ctx:
 75 |             await ctx.report_progress(0.5)
 76 |         
 77 |         # Prepare updates for actual API call
 78 |         properties_updates = {}
 79 |         if title and title.strip():
 80 |             properties_updates['cm:title'] = title.strip()
 81 |         if description and description.strip():
 82 |             properties_updates['cm:description'] = description.strip()
 83 |         if author and author.strip():
 84 |             properties_updates['cm:author'] = author.strip()
 85 |         
 86 |         # Import the UpdateNodeRequest model
 87 |         try:
 88 |             from python_alfresco_api.clients.core.nodes.models import UpdateNodeRequest
 89 |         except ImportError:
 90 |             return "ERROR: Failed to import UpdateNodeRequest model"
 91 |         
 92 |         # Prepare update request
 93 |         update_request = UpdateNodeRequest()
 94 |         
 95 |         if name and name.strip():
 96 |             update_request.name = name.strip()
 97 |         if properties_updates:
 98 |             update_request.properties = properties_updates
 99 |             
100 |         if ctx:
101 |             await ctx.report_progress(0.7)
102 |         
103 |         # Use the core client's update method
104 |         try:
105 |             updated_node = core_client.nodes.update(
106 |                 node_id=clean_node_id,
107 |                 request=update_request
108 |             )
109 |             logger.info("Node properties updated successfully")
110 |             
111 |         except Exception as update_error:
112 |             logger.error(f"Update failed: {update_error}")
113 |             return f"ERROR: Update failed: {str(update_error)}"
114 | 
115 |         if ctx:
116 |             await ctx.report_progress(1.0)
117 |         
118 |         changes = []
119 |         if name:
120 |             changes.append(f"- Name: {name}")
121 |         if title:
122 |             changes.append(f"- Title: {title}")
123 |         if description:
124 |             changes.append(f"- Description: {description}")
125 |         if author:
126 |             changes.append(f"- Author: {author}")
127 |         
128 |         updated_properties = "\n".join(changes)
129 |         
130 |         # Clean JSON-friendly formatting (no markdown syntax)
131 |         return f"Node Updated Successfully\n\nNode ID: {clean_node_id}\nUpdated Properties:\n{updated_properties}\nUpdate completed successfully"
132 |         
133 |     except Exception as e:
134 |         error_msg = f"ERROR: Update failed: {str(e)}"
135 |         if ctx:
136 |             await ctx.error(error_msg)
137 |         logger.error(f"Update failed: {e}")
138 |         return error_msg 
```

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

```markdown
  1 | # MCP Client Configuration Guide
  2 | 
  3 | 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).
  4 | 
  5 | ## 🔷 Cursor Configuration
  6 | 
  7 | Cursor is a VS Code fork with AI capabilities and MCP support.
  8 | 
  9 | > 📖 **Complete Setup Guide**: Detailed Cursor configuration instructions available at [playbooks.com](https://playbooks.com/mcp/stevereiner-alfresco-content-services#cursor-setup)
 10 | 
 11 | ### For Users (PyPI Installation)
 12 | 
 13 | If you installed the package via PyPI with pipx:
 14 | 
 15 | ```json
 16 | {
 17 |     "mcpServers": {
 18 |         "python-alfresco-mcp-server": {
 19 |             "command": "python-alfresco-mcp-server",
 20 |             "args": ["--transport", "stdio"],
 21 |             "env": {
 22 |                 "ALFRESCO_URL": "http://localhost:8080",
 23 |                 "ALFRESCO_USERNAME": "admin",
 24 |                 "ALFRESCO_PASSWORD": "admin"
 25 |             }
 26 |         }
 27 |     }
 28 | }
 29 | ```
 30 | 
 31 | ### For Developers (Source Installation)
 32 | 
 33 | If you're using the source code with UV:
 34 | 
 35 | ```json
 36 | {
 37 |     "mcpServers": {
 38 |         "python-alfresco-mcp-server": {
 39 |             "command": "uv",
 40 |             "args": ["run", "python-alfresco-mcp-server"],
 41 |             "cwd": "/path/to/python-alfresco-mcp-server",
 42 |             "env": {
 43 |                 "ALFRESCO_URL": "http://localhost:8080",
 44 |                 "ALFRESCO_USERNAME": "admin",
 45 |                 "ALFRESCO_PASSWORD": "admin"
 46 |             }
 47 |         }
 48 |     }
 49 | }
 50 | ```
 51 | 
 52 | ## ⚡ Claude Code Configuration
 53 | 
 54 | Claude Code is Anthropic's VS Code extension with MCP support.
 55 | 
 56 | > 📖 **Complete Setup Guide**: Claude Code configuration instructions at [playbooks.com](https://playbooks.com/mcp/stevereiner-alfresco-content-services#claude-code-setup)
 57 | 
 58 | ### For Users (PyPI Installation)
 59 | 
 60 | ```bash
 61 | claude mcp add-json "python-alfresco-mcp-server" '{
 62 |   "command": "python-alfresco-mcp-server",
 63 |   "args": ["--transport", "stdio"],
 64 |   "env": {
 65 |     "ALFRESCO_URL": "http://localhost:8080",
 66 |     "ALFRESCO_USERNAME": "admin",
 67 |     "ALFRESCO_PASSWORD": "admin"
 68 |   }
 69 | }'
 70 | ```
 71 | 
 72 | ### For Developers (Source Installation)
 73 | 
 74 | ```bash
 75 | claude mcp add-json "python-alfresco-mcp-server" '{
 76 |   "command": "uv",
 77 |   "args": ["run", "python-alfresco-mcp-server"],
 78 |   "cwd": "/path/to/python-alfresco-mcp-server",
 79 |   "env": {
 80 |     "ALFRESCO_URL": "http://localhost:8080",
 81 |     "ALFRESCO_USERNAME": "admin",
 82 |     "ALFRESCO_PASSWORD": "admin"
 83 |   }
 84 | }'
 85 | ```
 86 | 
 87 | ## 🔧 Other MCP Clients
 88 | 
 89 | For any MCP-compatible client, use these connection parameters based on your installation method:
 90 | 
 91 | ### PyPI Installation (Users)
 92 | 
 93 | - **Command**: `python-alfresco-mcp-server` (assumes pipx installation)
 94 | - **Args**: `["--transport", "stdio"]`
 95 | - **Transport Options**: 
 96 |   - STDIO (default) - Direct MCP protocol
 97 |   - HTTP (add `--port 8001`) - RESTful API  
 98 |   - SSE (add `--port 8003`) - Server-Sent Events
 99 | 
100 | ### Source Installation (Developers)
101 | 
102 | - **Command**: `uv`
103 | - **Args**: `["run", "python-alfresco-mcp-server"]`
104 | - **Working Directory**: Path to cloned repository
105 | - **Transport Options**: Same as above
106 | 
107 | ### Traditional Python Installation
108 | 
109 | If using traditional pip in a virtual environment:
110 | 
111 | - **Command**: `/path/to/venv/bin/python-alfresco-mcp-server` (full path to executable)
112 | - **Args**: `["--transport", "stdio"]`
113 | - **Transport Options**: Same as above
114 | 
115 | ## 🔧 Environment Variables
116 | 
117 | All clients need these environment variables configured:
118 | 
119 | | Variable | Default | Description |
120 | |----------|---------|-------------|
121 | | `ALFRESCO_URL` | `http://localhost:8080` | Alfresco server URL |
122 | | `ALFRESCO_USERNAME` | `admin` | Username for authentication |
123 | | `ALFRESCO_PASSWORD` | `admin` | Password for authentication |
124 | | `ALFRESCO_VERIFY_SSL` | `false` | Verify SSL certificates |
125 | | `ALFRESCO_TIMEOUT` | `30` | Request timeout (seconds) |
126 | 
127 | ### Windows-Specific Variables (if needed)
128 | 
129 | For Windows systems experiencing character encoding issues:
130 | 
131 | ```json
132 | "env": {
133 |   "ALFRESCO_URL": "http://localhost:8080",
134 |   "ALFRESCO_USERNAME": "admin",
135 |   "ALFRESCO_PASSWORD": "admin",
136 |   "PYTHONIOENCODING": "utf-8",
137 |   "PYTHONLEGACYWINDOWSSTDIO": "1"
138 | }
139 | ```
140 | 
141 | ## 🚀 Transport Options
142 | 
143 | The MCP server supports three transport protocols:
144 | 
145 | ### STDIO (Default)
146 | - **Fastest** and most efficient
147 | - Direct MCP protocol communication
148 | - Recommended for most use cases
149 | - Args: `["--transport", "stdio"]` (optional, it's the default)
150 | 
151 | ### HTTP 
152 | - RESTful API interface
153 | - Useful for web services and testing
154 | - Args: `["--transport", "http", "--port", "8001"]`
155 | 
156 | ### SSE (Server-Sent Events)
157 | - Real-time streaming updates
158 | - Good for live monitoring
159 | - Args: `["--transport", "sse", "--port", "8003"]`
160 | 
161 | ## 🧪 Testing Your Configuration
162 | 
163 | After setting up your MCP client:
164 | 
165 | 1. **Start Your Client**: Launch your MCP-enabled application
166 | 2. **Check Connection**: Look for "python-alfresco-mcp-server" in connected servers
167 | 3. **Test Basic Functionality**:
168 |    - Try the `repository_info` tool to verify connection
169 |    - Run a simple `search_content` query
170 |    - Check that all 15 tools are available
171 | 
172 | ## 🛠️ Troubleshooting
173 | 
174 | ### Common Issues
175 | 
176 | 1. **Command Not Found**
177 |    - Ensure the package is installed correctly
178 |    - For pipx: Run `pipx list` to verify installation
179 |    - For source: Ensure UV is installed and working directory is correct
180 | 
181 | 2. **Connection Failures**
182 |    - Check Alfresco server is running
183 |    - Verify environment variables are set correctly
184 |    - Test connection with `curl http://localhost:8080/alfresco`
185 | 
186 | 3. **Permission Errors**
187 |    - Verify Alfresco username/password
188 |    - Check that user has appropriate permissions
189 |    - Try with admin credentials first
190 | 
191 | 4. **Character Encoding (Windows)**
192 |    - Add Windows-specific environment variables
193 |    - Ensure UTF-8 encoding is configured
194 | 
195 | ### Getting Help
196 | 
197 | - 📚 **Documentation**: Complete guides in [`../docs/`](./README.md)
198 | - 🛠️ **Troubleshooting**: [Troubleshooting Guide](./troubleshooting.md)
199 | - 🐛 **Issues**: [GitHub Issues](https://github.com/stevereiner/python-alfresco-mcp-server/issues)
200 | 
201 | ## ⚠️ Security Notes
202 | 
203 | - **Never commit configuration files** with real credentials to version control
204 | - **Use environment variables** for production deployments
205 | - **Consider using .env files** for local development (they're ignored by git)
206 | - **Use strong passwords** for production Alfresco servers 
```

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

```python
  1 | """
  2 | Pytest configuration and shared fixtures for Alfresco MCP Server tests.
  3 | Provides configuration for unit tests, integration tests, and code coverage.
  4 | """
  5 | import pytest
  6 | import pytest_asyncio
  7 | import asyncio
  8 | import os
  9 | import httpx
 10 | import tempfile
 11 | import shutil
 12 | from typing import Generator, AsyncGenerator
 13 | from unittest.mock import MagicMock, AsyncMock, patch
 14 | 
 15 | # Test markers
 16 | pytest_plugins = ["pytest_asyncio"]
 17 | 
 18 | def pytest_configure(config):
 19 |     """Configure pytest markers."""
 20 |     config.addinivalue_line(
 21 |         "markers", "unit: marks tests as unit tests (fast, mocked)"
 22 |     )
 23 |     config.addinivalue_line(
 24 |         "markers", "integration: marks tests as integration tests (requires live Alfresco)"
 25 |     )
 26 |     config.addinivalue_line(
 27 |         "markers", "slow: marks tests as slow running"
 28 |     )
 29 |     config.addinivalue_line(
 30 |         "markers", "performance: marks tests as performance benchmarks"
 31 |     )
 32 | 
 33 | 
 34 | @pytest.fixture(scope="session")
 35 | def event_loop():
 36 |     """Create an instance of the default event loop for the test session."""
 37 |     loop = asyncio.get_event_loop_policy().new_event_loop()
 38 |     yield loop
 39 |     loop.close()
 40 | 
 41 | 
 42 | @pytest.fixture
 43 | def temp_dir() -> Generator[str, None, None]:
 44 |     """Create a temporary directory for test files."""
 45 |     with tempfile.TemporaryDirectory() as temp_dir:
 46 |         yield temp_dir
 47 | 
 48 | 
 49 | @pytest.fixture
 50 | def mock_alfresco_factory():
 51 |     """Mock Alfresco client factory for unit tests."""
 52 |     factory = MagicMock()
 53 |     
 54 |     # Mock search client
 55 |     search_client = AsyncMock()
 56 |     search_client.search = AsyncMock()
 57 |     factory.create_search_client.return_value = search_client
 58 |     
 59 |     # Mock core client  
 60 |     core_client = AsyncMock()
 61 |     core_client.get_node = AsyncMock()
 62 |     core_client.create_node = AsyncMock()
 63 |     core_client.update_node = AsyncMock()
 64 |     core_client.delete_node = AsyncMock()
 65 |     core_client.get_node_content = AsyncMock()
 66 |     factory.create_core_client.return_value = core_client
 67 |     
 68 |     return factory
 69 | 
 70 | 
 71 | @pytest.fixture
 72 | def mock_auth_util():
 73 |     """Mock authentication utility for unit tests."""
 74 |     auth_util = AsyncMock()
 75 |     auth_util.ensure_authenticated = AsyncMock()
 76 |     auth_util.get_auth_headers = AsyncMock(return_value={"Authorization": "Bearer mock-token"})
 77 |     auth_util.is_authenticated.return_value = True
 78 |     return auth_util
 79 | 
 80 | 
 81 | @pytest_asyncio.fixture
 82 | async def fastmcp_client():
 83 |     """FastMCP in-memory client for testing."""
 84 |     from fastmcp import Client
 85 |     from alfresco_mcp_server.fastmcp_server import mcp
 86 |     
 87 |     async with Client(mcp) as client:
 88 |         yield client
 89 | 
 90 | 
 91 | @pytest.fixture
 92 | async def http_client() -> AsyncGenerator[httpx.AsyncClient, None]:
 93 |     """HTTP client for integration tests."""
 94 |     async with httpx.AsyncClient(timeout=30.0) as client:
 95 |         yield client
 96 | 
 97 | 
 98 | @pytest.fixture
 99 | def alfresco_config():
100 |     """Alfresco configuration for tests."""
101 |     return {
102 |         "url": os.getenv("ALFRESCO_URL", "http://localhost:8080"),
103 |         "username": os.getenv("ALFRESCO_USERNAME", "admin"),
104 |         "password": os.getenv("ALFRESCO_PASSWORD", "admin"),
105 |         "verify_ssl": os.getenv("ALFRESCO_VERIFY_SSL", "false").lower() == "true"
106 |     }
107 | 
108 | 
109 | @pytest.fixture
110 | def sample_documents():
111 |     """Sample document data for testing."""
112 |     import base64
113 |     
114 |     return {
115 |         "text_doc": {
116 |             "filename": "test_document.txt",
117 |             "content": "This is a test document for MCP server testing.",
118 |             "content_base64": base64.b64encode(
119 |                 "This is a test document for MCP server testing.".encode()
120 |             ).decode(),
121 |             "mime_type": "text/plain"
122 |         },
123 |         "json_doc": {
124 |             "filename": "test_data.json",
125 |             "content": '{"test": "data", "numbers": [1, 2, 3]}',
126 |             "content_base64": base64.b64encode(
127 |                 '{"test": "data", "numbers": [1, 2, 3]}'.encode()
128 |             ).decode(),
129 |             "mime_type": "application/json"
130 |         }
131 |     }
132 | 
133 | 
134 | @pytest.fixture
135 | def mock_search_results():
136 |     """Mock search results for testing."""
137 |     from types import SimpleNamespace
138 |     
139 |     def create_mock_entry(name, node_id, is_folder=False):
140 |         entry = SimpleNamespace()
141 |         entry.entry = SimpleNamespace()
142 |         entry.entry.name = name
143 |         entry.entry.id = node_id
144 |         entry.entry.isFolder = is_folder
145 |         entry.entry.modifiedAt = "2024-01-15T10:30:00Z"
146 |         entry.entry.createdByUser = {"displayName": "Test User"}
147 |         entry.entry.content = {"sizeInBytes": 1024} if not is_folder else None
148 |         entry.entry.path = {"name": "/Shared/Test"}
149 |         return entry
150 |     
151 |     results = SimpleNamespace()
152 |     results.list = SimpleNamespace()
153 |     results.list.entries = [
154 |         create_mock_entry("Test Document 1.pdf", "doc-123", False),
155 |         create_mock_entry("Test Folder", "folder-456", True),
156 |         create_mock_entry("Test Document 2.txt", "doc-789", False)
157 |     ]
158 |     
159 |     return results
160 | 
161 | 
162 | def pytest_collection_modifyitems(config, items):
163 |     """Modify test collection to handle markers."""
164 |     if config.getoption("--integration"):
165 |         # Only run integration tests
166 |         skip_unit = pytest.mark.skip(reason="Integration test run - skipping unit tests")
167 |         for item in items:
168 |             if "integration" not in item.keywords:
169 |                 item.add_marker(skip_unit)
170 |     else:
171 |         # Skip integration tests by default
172 |         skip_integration = pytest.mark.skip(reason="Integration tests require --integration flag")
173 |         for item in items:
174 |             if "integration" in item.keywords:
175 |                 item.add_marker(skip_integration)
176 | 
177 | 
178 | def pytest_addoption(parser):
179 |     """Add custom command line options."""
180 |     parser.addoption(
181 |         "--integration",
182 |         action="store_true",
183 |         default=False,
184 |         help="Run integration tests (requires live Alfresco server)"
185 |     )
186 |     parser.addoption(
187 |         "--performance",
188 |         action="store_true", 
189 |         default=False,
190 |         help="Run performance benchmarks"
191 |     )
192 | 
193 | 
194 | @pytest.fixture
195 | def check_alfresco_available(alfresco_config):
196 |     """Check if Alfresco server is available for integration tests."""
197 |     def _check():
198 |         try:
199 |             response = httpx.get(
200 |                 f"{alfresco_config['url']}/alfresco/api/-default-/public/alfresco/versions/1/probes/-ready-",
201 |                 timeout=5.0
202 |             )
203 |             return response.status_code == 200
204 |         except Exception:
205 |             return False
206 |     
207 |     return _check 
```

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

```python
  1 | """
  2 | Cancel checkout tool implementation for Alfresco MCP Server.
  3 | Handles canceling document checkout with cleanup and unlock management.
  4 | """
  5 | import logging
  6 | import pathlib
  7 | import json
  8 | from datetime import datetime
  9 | from fastmcp import Context
 10 | 
 11 | from ...utils.connection import get_core_client
 12 | from ...utils.json_utils import safe_format_output
 13 | 
 14 | logger = logging.getLogger(__name__)
 15 | 
 16 | 
 17 | async def cancel_checkout_impl(
 18 |     node_id: str,
 19 |     ctx: Context = None
 20 | ) -> str:
 21 |     """Cancel checkout of a document, discarding any working copy.
 22 |     
 23 |     Args:
 24 |         node_id: Original node ID that was checked out
 25 |         ctx: MCP context for progress reporting
 26 |     
 27 |     Returns:
 28 |         Cancellation confirmation and cleanup status
 29 |     """
 30 |     if ctx:
 31 |         await ctx.info(f"Cancelling checkout for: {node_id}")
 32 |         await ctx.report_progress(0.1)
 33 |     
 34 |     if not node_id.strip():
 35 |         return safe_format_output("❌ Error: node_id is required")
 36 |     
 37 |     try:
 38 |         logger.info(f"Starting cancel checkout: node {node_id}")
 39 |         core_client = await get_core_client()
 40 |         
 41 |         # Clean the node ID
 42 |         clean_node_id = node_id.strip()
 43 |         if clean_node_id.startswith('alfresco://'):
 44 |             clean_node_id = clean_node_id.split('/')[-1]
 45 |         
 46 |         if ctx:
 47 |             await ctx.info("Checking node status...")
 48 |             await ctx.report_progress(0.3)
 49 |         
 50 |         # Get node information to validate using high-level core client
 51 |         node_response = core_client.nodes.get(node_id=clean_node_id)
 52 |         
 53 |         if not hasattr(node_response, 'entry'):
 54 |             return f"ERROR: Failed to get node information for: {clean_node_id}"
 55 |         
 56 |         node_info = node_response.entry
 57 |         filename = getattr(node_info, 'name', f"document_{clean_node_id}")
 58 |         
 59 |         if ctx:
 60 |             await ctx.info(">> Performing Alfresco unlock using high-level client...")
 61 |             await ctx.report_progress(0.5)
 62 |         
 63 |         # Use high-level core client unlock method
 64 |         try:
 65 |             logger.info(f"Attempting to unlock document: {clean_node_id}")
 66 |             unlock_response = core_client.versions.cancel_checkout(node_id=clean_node_id)
 67 |             if unlock_response and hasattr(unlock_response, 'entry'):
 68 |                 api_status = "✅ Document unlocked in Alfresco"
 69 |             else:
 70 |                 api_status = "✅ Document unlocked in Alfresco"
 71 |             logger.info(f"Document unlocked successfully: {clean_node_id}")
 72 |         except Exception as unlock_error:
 73 |             error_str = str(unlock_error)
 74 |             if "404" in error_str:
 75 |                 # Document might not be locked
 76 |                 api_status = "ℹ️ Document was not locked in Alfresco"
 77 |                 logger.info(f"Document was not locked: {clean_node_id}")
 78 |             elif "405" in error_str:
 79 |                 # Server doesn't support lock/unlock APIs
 80 |                 api_status = "WARNING: Server doesn't support lock/unlock APIs (treating as unlocked)"
 81 |                 logger.warning(f"Server doesn't support unlock API for {clean_node_id}")
 82 |             else:
 83 |                 api_status = f"WARNING: Alfresco unlock failed: {error_str}"
 84 |                 logger.error(f"Failed to unlock document {clean_node_id}: {error_str}")
 85 |         
 86 |         if ctx:
 87 |             await ctx.info("Cleaning up local files...")
 88 |             await ctx.report_progress(0.7)
 89 |         
 90 |         # Clean up local checkout tracking
 91 |         downloads_dir = pathlib.Path.home() / "Downloads"
 92 |         checkout_dir = downloads_dir / "checkout"
 93 |         checkout_manifest_path = checkout_dir / ".checkout_manifest.json"
 94 |         
 95 |         checkout_data = {}
 96 |         cleanup_status = [api_status]
 97 |         
 98 |         if checkout_manifest_path.exists():
 99 |             try:
100 |                 with open(checkout_manifest_path, 'r') as f:
101 |                     checkout_data = json.load(f)
102 |             except:
103 |                 checkout_data = {}
104 |         
105 |         # Check if this node is tracked in local checkouts
106 |         if 'checkouts' in checkout_data and clean_node_id in checkout_data['checkouts']:
107 |             checkout_info = checkout_data['checkouts'][clean_node_id]
108 |             checkout_filename = checkout_info['local_file']
109 |             checkout_file_path = checkout_dir / checkout_filename
110 |             
111 |             # Remove local checkout file
112 |             try:
113 |                 if checkout_file_path.exists():
114 |                     checkout_file_path.unlink()
115 |                     cleanup_status.append("🗑️ Local checkout file removed")
116 |                     logger.info(f"Removed local checkout file: {checkout_file_path}")
117 |                 else:
118 |                     cleanup_status.append("ℹ️ Local checkout file already removed")
119 |             except Exception as e:
120 |                 cleanup_status.append(f"WARNING: Could not remove local file: {e}")
121 |                 logger.warning(f"Failed to remove local file {checkout_file_path}: {e}")
122 |             
123 |             # Remove from tracking
124 |             del checkout_data['checkouts'][clean_node_id]
125 |             
126 |             # Update manifest
127 |             try:
128 |                 with open(checkout_manifest_path, 'w') as f:
129 |                     json.dump(checkout_data, f, indent=2)
130 |                 cleanup_status.append(">> Checkout tracking updated")
131 |             except Exception as e:
132 |                 cleanup_status.append(f"WARNING: Could not update tracking: {e}")
133 |         else:
134 |             cleanup_status.append("ℹ️ No local checkout tracking found")
135 |         
136 |         if ctx:
137 |             await ctx.info("Document unlocked!")
138 |             await ctx.report_progress(1.0)
139 |         
140 |         # Clean JSON-friendly formatting (no markdown syntax)
141 |         result = f"🔓 Document Unlocked\n\n"
142 |         result += f">> Document: {filename}\n"
143 |         result += f"ID: Node ID: {clean_node_id}\n"
144 |         result += f"🕒 Unlocked: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
145 |         result += f"🧹 Cleanup Status:\n"
146 |         
147 |         for status in cleanup_status:
148 |             result += f"   {status}\n"
149 |         
150 |         result += f"\nINFO: Note: Document is now available for others to edit."
151 |         result += f"\nWARNING: Important: Any unsaved changes in the local file have been discarded."
152 |         
153 |         return safe_format_output(result)
154 |         
155 |     except Exception as e:
156 |         error_msg = f"❌ Cancel checkout failed: {str(e)}"
157 |         if ctx:
158 |             await ctx.error(error_msg)
159 |         logger.error(f"Cancel checkout failed: {e}")
160 |         return safe_format_output(error_msg) 
```

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

```markdown
  1 | # MCP Testing Guide - Base64 Examples and Utilities
  2 | 
  3 | ## Base64 Example Strings
  4 | 
  5 | ### Simple Examples
  6 | ```
  7 | "SGVsbG8gV29ybGQ="           # "Hello World"
  8 | "VGVzdCBkb2N1bWVudA=="       # "Test document"
  9 | "U2FtcGxlIGNvbnRlbnQ="       # "Sample content"
 10 | ```
 11 | 
 12 | ### Document Content Examples
 13 | ```python
 14 | # Text document
 15 | text_content = "This is a test document for MCP server testing."
 16 | text_base64 = "VGhpcyBpcyBhIHRlc3QgZG9jdW1lbnQgZm9yIE1DUCBzZXJ2ZXIgdGVzdGluZy4="
 17 | 
 18 | # JSON document  
 19 | json_content = '{"test": "data", "numbers": [1, 2, 3]}'
 20 | json_base64 = "eyJ0ZXN0IjogImRhdGEiLCAibnVtYmVycyI6IFsxLCAyLCAzXX0="
 21 | ```
 22 | 
 23 | ## Base64 Generation Functions
 24 | 
 25 | ### Simple Generator Function
 26 | ```python
 27 | import base64
 28 | 
 29 | def generate_base64(text_content):
 30 |     """Convert text to base64 string."""
 31 |     return base64.b64encode(text_content.encode('utf-8')).decode('utf-8')
 32 | 
 33 | def decode_base64(base64_string):
 34 |     """Decode base64 string to text."""
 35 |     return base64.b64decode(base64_string).decode('utf-8')
 36 | 
 37 | # Examples
 38 | text = "Hello World"
 39 | encoded = generate_base64(text)  # "SGVsbG8gV29ybGQ="
 40 | decoded = decode_base64(encoded)  # "Hello World"
 41 | ```
 42 | 
 43 | ### Document Generator for MCP Testing
 44 | ```python
 45 | import base64
 46 | import json
 47 | import time
 48 | import uuid
 49 | 
 50 | def generate_test_document(filename, content_type="text"):
 51 |     """Generate a test document with base64 encoding for MCP upload."""
 52 |     
 53 |     if content_type == "text":
 54 |         content = f"""Test Document - {filename}
 55 |         
 56 | Created: {time.strftime('%Y-%m-%d %H:%M:%S')}
 57 | Document ID: {uuid.uuid4()}
 58 | Type: MCP Test Document
 59 | 
 60 | This is a sample document created for testing the Alfresco MCP server.
 61 | It contains structured content for validation purposes.
 62 | 
 63 | Sections:
 64 | 1. Header information
 65 | 2. Timestamp data
 66 | 3. Unique identifier
 67 | 4. Content body
 68 | 5. Footer notes
 69 | 
 70 | End of document.
 71 | """
 72 |     elif content_type == "json":
 73 |         content = json.dumps({
 74 |             "document_id": str(uuid.uuid4()),
 75 |             "filename": filename,
 76 |             "created_at": time.strftime('%Y-%m-%d %H:%M:%S'),
 77 |             "content_type": "test_data",
 78 |             "test_data": {
 79 |                 "numbers": [1, 2, 3, 4, 5],
 80 |                 "strings": ["hello", "world", "test"],
 81 |                 "nested": {
 82 |                     "level1": {
 83 |                         "level2": "deep_value"
 84 |                     }
 85 |                 }
 86 |             }
 87 |         }, indent=2)
 88 |     
 89 |     content_base64 = base64.b64encode(content.encode('utf-8')).decode('utf-8')
 90 |     
 91 |     return {
 92 |         "filename": filename,
 93 |         "content": content,
 94 |         "content_base64": content_base64,
 95 |         "size": len(content),
 96 |         "encoded_size": len(content_base64)
 97 |     }
 98 | 
 99 | # Usage examples
100 | text_doc = generate_test_document("test_file.txt", "text")
101 | json_doc = generate_test_document("test_data.json", "json")
102 | ```
103 | 
104 | ### Batch Document Generator
105 | ```python
106 | def generate_batch_documents(count=5, prefix="batch_doc"):
107 |     """Generate multiple test documents for batch operations."""
108 |     
109 |     documents = []
110 |     session_id = str(uuid.uuid4())[:8]
111 |     
112 |     for i in range(count):
113 |         content = f"""Batch Document {i+1}
114 |         
115 | Session ID: {session_id}
116 | Document Index: {i+1} of {count}
117 | Created: {time.strftime('%Y-%m-%d %H:%M:%S')}
118 | Unique ID: {uuid.uuid4()}
119 | 
120 | This is batch document number {i+1} created for testing 
121 | concurrent operations and bulk uploads in the MCP server.
122 | 
123 | Content sections:
124 | - Document metadata
125 | - Sequential numbering
126 | - Timestamp information
127 | - Unique identification
128 | 
129 | Processing notes:
130 | - Part of batch operation
131 | - Sequential creation
132 | - Automated generation
133 | """
134 |         
135 |         doc = {
136 |             "filename": f"{prefix}_{session_id}_{i+1:03d}.txt",
137 |             "content": content,
138 |             "content_base64": base64.b64encode(content.encode('utf-8')).decode('utf-8'),
139 |             "description": f"Batch document {i+1} from session {session_id}"
140 |         }
141 |         documents.append(doc)
142 |     
143 |     return documents
144 | 
145 | # Generate 5 test documents
146 | batch_docs = generate_batch_documents(5)
147 | ```
148 | 
149 | ## MCP Upload Examples
150 | 
151 | ### Single Document Upload
152 | ```json
153 | {
154 |   "filename": "test_document.txt",
155 |   "content_base64": "VGhpcyBpcyBhIHRlc3QgZG9jdW1lbnQgZm9yIE1DUCBzZXJ2ZXIgdGVzdGluZy4=",
156 |   "parent_id": "-root-",
157 |   "description": "Test upload via MCP Inspector"
158 | }
159 | ```
160 | 
161 | ### JSON Document Upload
162 | ```json
163 | {
164 |   "filename": "test_data.json", 
165 |   "content_base64": "eyJ0ZXN0IjogImRhdGEiLCAibnVtYmVycyI6IFsxLCAyLCAzXX0=",
166 |   "parent_id": "-root-",
167 |   "description": "JSON test data"
168 | }
169 | ```
170 | 
171 | ## Validation Functions
172 | 
173 | ### Base64 Validation
174 | ```python
175 | import re
176 | 
177 | def validate_base64(base64_string):
178 |     """Validate base64 string format."""
179 |     try:
180 |         # Check format
181 |         if not re.match(r'^[A-Za-z0-9+/]*={0,2}$', base64_string):
182 |             return False, "Invalid base64 characters"
183 |         
184 |         # Test decode
185 |         decoded = base64.b64decode(base64_string, validate=True)
186 |         return True, f"Valid base64 ({len(decoded)} bytes)"
187 |         
188 |     except Exception as e:
189 |         return False, f"Decode error: {str(e)}"
190 | 
191 | # Test validation
192 | valid, message = validate_base64("SGVsbG8gV29ybGQ=")
193 | print(f"Validation: {valid}, Message: {message}")
194 | ```
195 | 
196 | ### Content Size Calculation
197 | ```python
198 | def calculate_base64_size(text_content):
199 |     """Calculate the base64 encoded size before encoding."""
200 |     original_bytes = len(text_content.encode('utf-8'))
201 |     base64_bytes = ((original_bytes + 2) // 3) * 4
202 |     return {
203 |         "original_size": original_bytes,
204 |         "base64_size": base64_bytes,
205 |         "size_increase": f"{((base64_bytes / original_bytes - 1) * 100):.1f}%"
206 |     }
207 | 
208 | # Example
209 | size_info = calculate_base64_size("Hello World")
210 | print(size_info)
211 | # {'original_size': 11, 'base64_size': 16, 'size_increase': '45.5%'}
212 | ```
213 | 
214 | ## Quick Reference
215 | 
216 | ### Common Test Strings
217 | ```python
218 | TEST_STRINGS = {
219 |     "hello": "SGVsbG8gV29ybGQ=",
220 |     "test": "dGVzdA==", 
221 |     "sample": "c2FtcGxl",
222 |     "document": "ZG9jdW1lbnQ=",
223 |     "content": "Y29udGVudA==",
224 |     "data": "ZGF0YQ==",
225 |     "file": "ZmlsZQ==",
226 |     "upload": "dXBsb2Fk"
227 | }
228 | ```
229 | 
230 | ### Usage in MCP Inspector
231 | 1. **Generate content**: Use the generator functions above
232 | 2. **Copy base64**: Use the `content_base64` field from generated documents  
233 | 3. **Test upload**: Paste into MCP Inspector's upload_document tool
234 | 4. **Validate**: Check successful upload in Alfresco
235 | 
236 | ### File Size Limits
237 | - **Small files**: < 1MB (recommended for testing)
238 | - **Medium files**: 1-10MB (stress testing)  
239 | - **Large files**: > 10MB (may require chunking)
240 | 
241 | Remember: Base64 encoding increases file size by approximately 33%, so plan accordingly for upload limits. 
```

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

```python
  1 | """
  2 | File type analysis utility for Alfresco MCP Server.
  3 | Provides content type analysis and suggestions for different file types.
  4 | """
  5 | import pathlib
  6 | import mimetypes
  7 | from typing import Dict, List, Optional
  8 | 
  9 | 
 10 | def detect_file_extension_from_content(content: bytes) -> Optional[str]:
 11 |     """Detect file type from content and return appropriate extension.
 12 |     
 13 |     Args:
 14 |         content: Raw file content bytes
 15 |         
 16 |     Returns:
 17 |         File extension (e.g., '.pdf', '.txt', '.jpg') or None if unknown
 18 |     """
 19 |     if not content:
 20 |         return None
 21 |     
 22 |     # Check for common file signatures (magic numbers)
 23 |     if content.startswith(b'%PDF'):
 24 |         return '.pdf'
 25 |     elif content.startswith(b'\xff\xd8\xff'):
 26 |         return '.jpg'
 27 |     elif content.startswith(b'\x89PNG\r\n\x1a\n'):
 28 |         return '.png'
 29 |     elif content.startswith(b'GIF87a') or content.startswith(b'GIF89a'):
 30 |         return '.gif'
 31 |     elif content.startswith(b'PK\x03\x04') or content.startswith(b'PK\x05\x06') or content.startswith(b'PK\x07\x08'):
 32 |         # ZIP-based formats (could be .zip, .docx, .xlsx, .pptx, etc.)
 33 |         # For simplicity, assume .zip unless we detect Office formats
 34 |         if b'word/' in content[:1024] or b'xl/' in content[:1024] or b'ppt/' in content[:1024]:
 35 |             # Likely Office document but hard to determine exact type
 36 |             return '.zip'  # Conservative choice
 37 |         return '.zip'
 38 |     elif content.startswith(b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1'):
 39 |         # Microsoft Office old format
 40 |         return '.doc'  # Could also be .xls, .ppt but .doc is most common
 41 |     elif content.startswith(b'<?xml') or content.startswith(b'\xef\xbb\xbf<?xml'):
 42 |         return '.xml'
 43 |     elif content.startswith(b'<!DOCTYPE html') or content.startswith(b'<html'):
 44 |         return '.html'
 45 |     elif content.startswith(b'{\n') or content.startswith(b'{"') or content.startswith(b'[\n') or content.startswith(b'[{'):
 46 |         return '.json'
 47 |     else:
 48 |         # Try to detect if it's text content
 49 |         try:
 50 |             # Try to decode as UTF-8 text
 51 |             text_content = content.decode('utf-8')
 52 |             # Check if it contains mostly printable characters
 53 |             printable_chars = sum(1 for c in text_content if c.isprintable() or c.isspace())
 54 |             if len(text_content) > 0 and printable_chars / len(text_content) > 0.8:
 55 |                 return '.txt'
 56 |         except (UnicodeDecodeError, UnicodeError):
 57 |             pass
 58 |     
 59 |     # Unknown binary format
 60 |     return None
 61 | 
 62 | 
 63 | def analyze_content_type(filename: str, mime_type: str, content: bytes) -> dict:
 64 |     """Analyze file type and provide relevant suggestions.
 65 |     
 66 |     Args:
 67 |         filename: Name of the file
 68 |         mime_type: MIME type of the file
 69 |         content: File content as bytes
 70 |         
 71 |     Returns:
 72 |         Dictionary with category, suggestions, and file_size
 73 |     """
 74 |     file_size = len(content)
 75 |     
 76 |     # Get case-insensitive filename for macOS/Windows compatibility
 77 |     filename_lower = filename.lower()
 78 |     
 79 |     # Determine file category based on MIME type and extension
 80 |     if mime_type.startswith('image/'):
 81 |         category = 'images'
 82 |         suggestions = [
 83 |             "Can be used for thumbnails and previews",
 84 |             "Consider image optimization for web use"
 85 |         ]
 86 |     elif mime_type.startswith('video/') or mime_type.startswith('audio/'):
 87 |         category = 'media'
 88 |         suggestions = [
 89 |             "Large files may need streaming support",
 90 |             "Consider format compatibility for playback"
 91 |         ]
 92 |     elif mime_type in ['application/pdf', 'application/msword', 
 93 |                       'application/vnd.openxmlformats-officedocument.wordprocessingml.document']:
 94 |         category = 'documents'
 95 |         suggestions = [
 96 |             "PDF files support full-text search",
 97 |             ">> Consider using text extraction for searchable content",
 98 |             "Can be previewed in most browsers"
 99 |         ]
100 |     elif mime_type in ['application/vnd.ms-excel',
101 |                       'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']:
102 |         category = 'documents'
103 |         suggestions = [
104 |             ">> Excel spreadsheet - can be opened with Excel or LibreOffice Calc",
105 |             ">> May contain tracked changes or comments"
106 |         ]
107 |     elif mime_type in ['application/vnd.ms-powerpoint',
108 |                       'application/vnd.openxmlformats-officedocument.presentationml.presentation']:
109 |         category = 'documents'
110 |         suggestions = [
111 |             "Presentation files support slide previews",
112 |             "Consider extracting slide content for search"
113 |         ]
114 |     elif mime_type.startswith('text/') or 'javascript' in mime_type or 'json' in mime_type:
115 |         if filename_lower.endswith(('.py', '.js', '.java', '.cpp', '.c', '.cs', '.php', '.rb')):
116 |             category = 'code'
117 |             suggestions = [
118 |                 "Source code files support syntax highlighting",
119 |                 ">> Check contents before extraction for security",
120 |                 "Can be indexed for code search"
121 |             ]
122 |         else:
123 |             category = 'documents'
124 |             suggestions = [
125 |                 ">> Review for security before execution",
126 |                 ">> May require specific runtime environment"
127 |             ]
128 |     elif mime_type in ['application/zip', 'application/x-rar-compressed', 'application/gzip']:
129 |         category = 'archives'
130 |         suggestions = [
131 |             "Archive contents can be extracted and indexed",
132 |             ">> Check contents before extraction for security",
133 |             "May contain multiple file types"
134 |         ]
135 |     else:
136 |         category = 'other'
137 |         suggestions = [
138 |             "Unknown file type - review content manually",
139 |             "Consider file format documentation"
140 |         ]
141 |     
142 |     # Add size-based suggestions
143 |     if file_size > 100 * 1024 * 1024:  # > 100MB
144 |         suggestions.append("WARNING: Large file - consider network and storage impact")
145 |     
146 |     # Add security suggestions for executable files (case-insensitive for cross-platform)
147 |     executable_extensions = (
148 |         '.exe', '.bat', '.sh', '.com', '.scr',  # Windows & shell scripts
149 |         '.app', '.dmg', '.pkg',                 # macOS
150 |         '.deb', '.rpm', '.run', '.bin', '.appimage'  # Linux
151 |     )
152 |     if filename_lower.endswith(executable_extensions):
153 |         suggestions = [
154 |             "WARNING: Executable file - scan for security before running",
155 |             "Consider sandboxed execution environment"
156 |         ]
157 |         category = 'executable'
158 |     
159 |     return {
160 |         'category': category,
161 |         'suggestions': suggestions,
162 |         'file_size': file_size
163 |     } 
```
Page 1/4FirstPrevNextLast