python-alfresco-mcp-server 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- alfresco_mcp_server/__init__.py +27 -0
- alfresco_mcp_server/config.py +104 -0
- alfresco_mcp_server/fastmcp_server.py +259 -0
- alfresco_mcp_server/prompts/__init__.py +7 -0
- alfresco_mcp_server/prompts/search_and_analyze.py +67 -0
- alfresco_mcp_server/resources/__init__.py +7 -0
- alfresco_mcp_server/resources/repository_resources.py +205 -0
- alfresco_mcp_server/tools/__init__.py +13 -0
- alfresco_mcp_server/tools/core/__init__.py +27 -0
- alfresco_mcp_server/tools/core/browse_repository.py +164 -0
- alfresco_mcp_server/tools/core/cancel_checkout.py +160 -0
- alfresco_mcp_server/tools/core/checkin_document.py +264 -0
- alfresco_mcp_server/tools/core/checkout_document.py +258 -0
- alfresco_mcp_server/tools/core/create_folder.py +105 -0
- alfresco_mcp_server/tools/core/delete_node.py +88 -0
- alfresco_mcp_server/tools/core/download_document.py +214 -0
- alfresco_mcp_server/tools/core/get_node_properties.py +195 -0
- alfresco_mcp_server/tools/core/update_node_properties.py +138 -0
- alfresco_mcp_server/tools/core/upload_document.py +244 -0
- alfresco_mcp_server/tools/search/__init__.py +15 -0
- alfresco_mcp_server/tools/search/advanced_search.py +204 -0
- alfresco_mcp_server/tools/search/cmis_search.py +180 -0
- alfresco_mcp_server/tools/search/search_by_metadata.py +183 -0
- alfresco_mcp_server/tools/search/search_content.py +186 -0
- alfresco_mcp_server/utils/__init__.py +34 -0
- alfresco_mcp_server/utils/connection.py +114 -0
- alfresco_mcp_server/utils/file_type_analysis.py +163 -0
- alfresco_mcp_server/utils/json_utils.py +135 -0
- python_alfresco_mcp_server-1.1.0.dist-info/METADATA +631 -0
- python_alfresco_mcp_server-1.1.0.dist-info/RECORD +34 -0
- python_alfresco_mcp_server-1.1.0.dist-info/WHEEL +5 -0
- python_alfresco_mcp_server-1.1.0.dist-info/entry_points.txt +2 -0
- python_alfresco_mcp_server-1.1.0.dist-info/licenses/LICENSE +201 -0
- python_alfresco_mcp_server-1.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
]
|
|
@@ -0,0 +1,104 @@
|
|
|
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()
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Server for Alfresco using FastMCP 2.0
|
|
3
|
+
Modular implementation with separated concerns and self-contained tools
|
|
4
|
+
"""
|
|
5
|
+
import logging
|
|
6
|
+
from fastmcp import FastMCP, Context
|
|
7
|
+
|
|
8
|
+
# Search tools imports
|
|
9
|
+
from .tools.search.search_content import search_content_impl
|
|
10
|
+
from .tools.search.advanced_search import advanced_search_impl
|
|
11
|
+
from .tools.search.search_by_metadata import search_by_metadata_impl
|
|
12
|
+
from .tools.search.cmis_search import cmis_search_impl
|
|
13
|
+
|
|
14
|
+
# Core tools imports
|
|
15
|
+
from .tools.core.browse_repository import browse_repository_impl
|
|
16
|
+
from .tools.core.upload_document import upload_document_impl
|
|
17
|
+
from .tools.core.download_document import download_document_impl
|
|
18
|
+
from .tools.core.create_folder import create_folder_impl
|
|
19
|
+
from .tools.core.get_node_properties import get_node_properties_impl
|
|
20
|
+
from .tools.core.update_node_properties import update_node_properties_impl
|
|
21
|
+
from .tools.core.delete_node import delete_node_impl
|
|
22
|
+
from .tools.core.checkout_document import checkout_document_impl
|
|
23
|
+
from .tools.core.checkin_document import checkin_document_impl
|
|
24
|
+
from .tools.core.cancel_checkout import cancel_checkout_impl
|
|
25
|
+
|
|
26
|
+
# Resource imports
|
|
27
|
+
from .resources.repository_resources import (
|
|
28
|
+
get_repository_info_impl
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Prompt imports
|
|
32
|
+
from .prompts.search_and_analyze import search_and_analyze_impl
|
|
33
|
+
|
|
34
|
+
# Configure logging
|
|
35
|
+
logging.basicConfig(level=logging.INFO)
|
|
36
|
+
|
|
37
|
+
# Reduce verbosity of noisy loggers
|
|
38
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
39
|
+
logging.getLogger("httpcore").setLevel(logging.WARNING)
|
|
40
|
+
|
|
41
|
+
logger = logging.getLogger(__name__)
|
|
42
|
+
|
|
43
|
+
# Initialize MCP server
|
|
44
|
+
mcp = FastMCP("MCP Server for Alfresco Content Services")
|
|
45
|
+
|
|
46
|
+
# ================== SEARCH TOOLS ==================
|
|
47
|
+
|
|
48
|
+
@mcp.tool
|
|
49
|
+
async def search_content(
|
|
50
|
+
query: str,
|
|
51
|
+
max_results: int = 25,
|
|
52
|
+
node_type: str = "",
|
|
53
|
+
ctx: Context = None
|
|
54
|
+
) -> str:
|
|
55
|
+
"""Search for content in Alfresco using AFTS query language."""
|
|
56
|
+
return await search_content_impl(query, max_results, node_type, ctx)
|
|
57
|
+
|
|
58
|
+
@mcp.tool
|
|
59
|
+
async def advanced_search(
|
|
60
|
+
query: str,
|
|
61
|
+
sort_field: str = "cm:modified",
|
|
62
|
+
sort_ascending: bool = False,
|
|
63
|
+
max_results: int = 25,
|
|
64
|
+
ctx: Context = None
|
|
65
|
+
) -> str:
|
|
66
|
+
"""Advanced search with sorting and filtering capabilities."""
|
|
67
|
+
return await advanced_search_impl(query, sort_field, sort_ascending, max_results, ctx)
|
|
68
|
+
|
|
69
|
+
@mcp.tool
|
|
70
|
+
async def search_by_metadata(
|
|
71
|
+
term: str = "",
|
|
72
|
+
creator: str = "",
|
|
73
|
+
content_type: str = "",
|
|
74
|
+
max_results: int = 25,
|
|
75
|
+
ctx: Context = None
|
|
76
|
+
) -> str:
|
|
77
|
+
"""Search for content in Alfresco by metadata fields."""
|
|
78
|
+
return await search_by_metadata_impl(term, creator, content_type, max_results, ctx)
|
|
79
|
+
|
|
80
|
+
@mcp.tool
|
|
81
|
+
async def cmis_search(
|
|
82
|
+
cmis_query: str = "SELECT * FROM cmis:document WHERE cmis:contentStreamMimeType = 'application/pdf'",
|
|
83
|
+
max_results: int = 25,
|
|
84
|
+
ctx: Context = None
|
|
85
|
+
) -> str:
|
|
86
|
+
"""Search using CMIS SQL syntax. Default query searches for PDF documents."""
|
|
87
|
+
return await cmis_search_impl(cmis_query, max_results, ctx)
|
|
88
|
+
|
|
89
|
+
# ================== CORE TOOLS ==================
|
|
90
|
+
|
|
91
|
+
@mcp.tool
|
|
92
|
+
async def browse_repository(
|
|
93
|
+
parent_id: str = "-my-",
|
|
94
|
+
max_items: int = 25,
|
|
95
|
+
ctx: Context = None
|
|
96
|
+
) -> str:
|
|
97
|
+
"""Browse the Alfresco repository structure."""
|
|
98
|
+
return await browse_repository_impl(parent_id, max_items, ctx)
|
|
99
|
+
|
|
100
|
+
@mcp.tool
|
|
101
|
+
async def upload_document(
|
|
102
|
+
file_path: str = "",
|
|
103
|
+
base64_content: str = "",
|
|
104
|
+
parent_id: str = "-shared-",
|
|
105
|
+
description: str = "",
|
|
106
|
+
ctx: Context = None
|
|
107
|
+
) -> str:
|
|
108
|
+
"""Upload a document to Alfresco."""
|
|
109
|
+
return await upload_document_impl(file_path, base64_content, parent_id, description, ctx)
|
|
110
|
+
|
|
111
|
+
@mcp.tool
|
|
112
|
+
async def download_document(
|
|
113
|
+
node_id: str,
|
|
114
|
+
save_to_disk: bool = True,
|
|
115
|
+
attachment: bool = True,
|
|
116
|
+
ctx: Context = None
|
|
117
|
+
) -> str:
|
|
118
|
+
"""Download a document from Alfresco repository."""
|
|
119
|
+
return await download_document_impl(node_id, save_to_disk, attachment, ctx)
|
|
120
|
+
|
|
121
|
+
@mcp.tool
|
|
122
|
+
async def create_folder(
|
|
123
|
+
folder_name: str,
|
|
124
|
+
parent_id: str = "-shared-",
|
|
125
|
+
description: str = "",
|
|
126
|
+
ctx: Context = None
|
|
127
|
+
) -> str:
|
|
128
|
+
"""Create a new folder in Alfresco."""
|
|
129
|
+
return await create_folder_impl(folder_name, parent_id, description, ctx)
|
|
130
|
+
|
|
131
|
+
@mcp.tool
|
|
132
|
+
async def get_node_properties(node_id: str, ctx: Context = None) -> str:
|
|
133
|
+
"""Get metadata and properties of a document or folder."""
|
|
134
|
+
return await get_node_properties_impl(node_id, ctx)
|
|
135
|
+
|
|
136
|
+
@mcp.tool
|
|
137
|
+
async def update_node_properties(
|
|
138
|
+
node_id: str,
|
|
139
|
+
name: str = "",
|
|
140
|
+
title: str = "",
|
|
141
|
+
description: str = "",
|
|
142
|
+
author: str = "",
|
|
143
|
+
ctx: Context = None
|
|
144
|
+
) -> str:
|
|
145
|
+
"""Update metadata and properties of a document or folder."""
|
|
146
|
+
return await update_node_properties_impl(node_id, name, title, description, author, ctx)
|
|
147
|
+
|
|
148
|
+
@mcp.tool
|
|
149
|
+
async def delete_node(
|
|
150
|
+
node_id: str,
|
|
151
|
+
permanent: bool = False,
|
|
152
|
+
ctx: Context = None
|
|
153
|
+
) -> str:
|
|
154
|
+
"""Delete a document or folder from Alfresco."""
|
|
155
|
+
return await delete_node_impl(node_id, permanent, ctx)
|
|
156
|
+
|
|
157
|
+
# ================== CHECKOUT/CHECKIN TOOLS ==================
|
|
158
|
+
|
|
159
|
+
@mcp.tool
|
|
160
|
+
async def checkout_document(
|
|
161
|
+
node_id: str,
|
|
162
|
+
download_for_editing: bool = True,
|
|
163
|
+
ctx: Context = None
|
|
164
|
+
) -> str:
|
|
165
|
+
"""Check out a document for editing using Alfresco REST API."""
|
|
166
|
+
return await checkout_document_impl(node_id, download_for_editing, ctx)
|
|
167
|
+
|
|
168
|
+
@mcp.tool
|
|
169
|
+
async def checkin_document(
|
|
170
|
+
node_id: str,
|
|
171
|
+
comment: str = "",
|
|
172
|
+
major_version: bool = False,
|
|
173
|
+
file_path: str = "",
|
|
174
|
+
new_name: str = "",
|
|
175
|
+
ctx: Context = None
|
|
176
|
+
) -> str:
|
|
177
|
+
"""Check in a document after editing using Alfresco REST API."""
|
|
178
|
+
return await checkin_document_impl(node_id, comment, major_version, file_path, new_name, ctx)
|
|
179
|
+
|
|
180
|
+
@mcp.tool
|
|
181
|
+
async def cancel_checkout(
|
|
182
|
+
node_id: str,
|
|
183
|
+
ctx: Context = None
|
|
184
|
+
) -> str:
|
|
185
|
+
"""Cancel checkout of a document, discarding any working copy."""
|
|
186
|
+
return await cancel_checkout_impl(node_id, ctx)
|
|
187
|
+
|
|
188
|
+
# ================== RESOURCES ==================
|
|
189
|
+
|
|
190
|
+
@mcp.resource("alfresco://repository/info", description="📊 Live Alfresco repository information including version, edition, and connection status")
|
|
191
|
+
async def repository_info() -> str:
|
|
192
|
+
"""Get Alfresco repository information using Discovery Client."""
|
|
193
|
+
return await get_repository_info_impl()
|
|
194
|
+
|
|
195
|
+
@mcp.tool
|
|
196
|
+
async def get_repository_info_tool(ctx: Context = None) -> str:
|
|
197
|
+
"""Get Alfresco repository information using Discovery Client (as tool instead of resource)."""
|
|
198
|
+
return await get_repository_info_impl()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
# ================== PROMPTS ==================
|
|
202
|
+
|
|
203
|
+
@mcp.prompt(description="🔎 Generate comprehensive search and analysis steps for Alfresco documents with customizable analysis types")
|
|
204
|
+
async def search_and_analyze(query: str, analysis_type: str = "summary") -> str:
|
|
205
|
+
"""Generate comprehensive search and analysis prompts for Alfresco documents."""
|
|
206
|
+
return await search_and_analyze_impl(query, analysis_type)
|
|
207
|
+
|
|
208
|
+
# ================== MAIN ENTRY POINT ==================
|
|
209
|
+
|
|
210
|
+
def main():
|
|
211
|
+
"""Main entry point for the FastMCP 2.0 Alfresco server."""
|
|
212
|
+
import argparse
|
|
213
|
+
|
|
214
|
+
parser = argparse.ArgumentParser(description="MCP Server for Alfresco 2.0")
|
|
215
|
+
parser.add_argument(
|
|
216
|
+
"--transport",
|
|
217
|
+
choices=["stdio", "http", "sse"],
|
|
218
|
+
default="stdio",
|
|
219
|
+
help="Transport method (default: stdio)"
|
|
220
|
+
)
|
|
221
|
+
parser.add_argument(
|
|
222
|
+
"--host",
|
|
223
|
+
default="localhost",
|
|
224
|
+
help="Host for HTTP/SSE transport (default: localhost)"
|
|
225
|
+
)
|
|
226
|
+
parser.add_argument(
|
|
227
|
+
"--port",
|
|
228
|
+
type=int,
|
|
229
|
+
default=8000,
|
|
230
|
+
help="Port for HTTP/SSE transport (default: 8000)"
|
|
231
|
+
)
|
|
232
|
+
parser.add_argument(
|
|
233
|
+
"--log-level",
|
|
234
|
+
choices=["DEBUG", "INFO", "WARNING", "ERROR"],
|
|
235
|
+
default="INFO",
|
|
236
|
+
help="Logging level (default: INFO)"
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
args = parser.parse_args()
|
|
240
|
+
|
|
241
|
+
# Configure logging
|
|
242
|
+
logging.basicConfig(
|
|
243
|
+
level=getattr(logging, args.log_level),
|
|
244
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
logger.info(">> Starting MCP Server for Alfresco")
|
|
248
|
+
logger.info(">> Hierarchical structure: tools/{core,search}, resources, prompts, utils")
|
|
249
|
+
|
|
250
|
+
# Run server with specified transport
|
|
251
|
+
if args.transport == "stdio":
|
|
252
|
+
mcp.run(transport="stdio")
|
|
253
|
+
elif args.transport == "http":
|
|
254
|
+
mcp.run(transport="http", host=args.host, port=args.port)
|
|
255
|
+
elif args.transport == "sse":
|
|
256
|
+
mcp.run(transport="sse", host=args.host, port=args.port)
|
|
257
|
+
|
|
258
|
+
if __name__ == "__main__":
|
|
259
|
+
main()
|
|
@@ -0,0 +1,67 @@
|
|
|
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
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Repository resources for Alfresco MCP Server.
|
|
3
|
+
Self-contained resources for repository information, health, stats, and configuration.
|
|
4
|
+
Returns data or indicates when unavailable.
|
|
5
|
+
"""
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
from python_alfresco_api.client_factory import ClientFactory
|
|
11
|
+
from ..utils.connection import ensure_connection, get_client_factory
|
|
12
|
+
from ..utils.json_utils import safe_format_output
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def get_repository_info_impl() -> str:
|
|
18
|
+
"""Get Alfresco repository information using Discovery API.
|
|
19
|
+
Returns comprehensive repository details or connection status.
|
|
20
|
+
"""
|
|
21
|
+
try:
|
|
22
|
+
await ensure_connection()
|
|
23
|
+
client_factory: ClientFactory = await get_client_factory()
|
|
24
|
+
|
|
25
|
+
logger.info("Getting repository information via Discovery API")
|
|
26
|
+
|
|
27
|
+
# Use the working pattern from test script - high-level API
|
|
28
|
+
discovery_client = client_factory.create_discovery_client()
|
|
29
|
+
|
|
30
|
+
# Check if discovery client has the discovery attribute (working pattern from test)
|
|
31
|
+
if not hasattr(discovery_client, 'discovery'):
|
|
32
|
+
logger.warning("Discovery client does not have discovery attribute")
|
|
33
|
+
return safe_format_output(f"""⚠️ **Repository Information - Discovery Client Unavailable**
|
|
34
|
+
|
|
35
|
+
**Status**: Discovery client initialization failed
|
|
36
|
+
|
|
37
|
+
**Available Information**:
|
|
38
|
+
🔗 **Server**: {os.getenv('ALFRESCO_URL', 'http://localhost:8080')}
|
|
39
|
+
👤 **Connected as**: {os.getenv('ALFRESCO_USERNAME', 'admin')}
|
|
40
|
+
❌ **Discovery API**: Client not available
|
|
41
|
+
|
|
42
|
+
**Note**: This could indicate:
|
|
43
|
+
- Authentication issues
|
|
44
|
+
- Network connectivity problems
|
|
45
|
+
- Server configuration issues
|
|
46
|
+
|
|
47
|
+
**Recommendation**: Check connection settings and server status.""")
|
|
48
|
+
|
|
49
|
+
# Get repository information using high-level Discovery API (working pattern from test)
|
|
50
|
+
repo_info = discovery_client.discovery.get_repository_information()
|
|
51
|
+
|
|
52
|
+
# Handle None response (HTTP 501 - Discovery API disabled)
|
|
53
|
+
if repo_info is None:
|
|
54
|
+
logger.warning("Discovery API is disabled on this Alfresco instance (returned None)")
|
|
55
|
+
return safe_format_output(f"""⚠️ **Repository Information - Discovery API Disabled**
|
|
56
|
+
|
|
57
|
+
**Status**: Discovery API is disabled on this Alfresco instance (HTTP 501)
|
|
58
|
+
|
|
59
|
+
**Available Information**:
|
|
60
|
+
🔗 **Server**: {os.getenv('ALFRESCO_URL', 'http://localhost:8080')}
|
|
61
|
+
👤 **Connected as**: {os.getenv('ALFRESCO_USERNAME', 'admin')}
|
|
62
|
+
✅ **Core API**: Available (connection successful)
|
|
63
|
+
❌ **Discovery API**: Disabled by administrator
|
|
64
|
+
|
|
65
|
+
**Note**: The Discovery API provides detailed repository information including:
|
|
66
|
+
- Version and edition details
|
|
67
|
+
- License information and entitlements
|
|
68
|
+
- Installed modules and their versions
|
|
69
|
+
- Repository status and capabilities
|
|
70
|
+
|
|
71
|
+
**Administrator Action Required**: To enable Discovery API, the Alfresco administrator needs to:
|
|
72
|
+
1. Enable the Discovery API in the repository configuration
|
|
73
|
+
2. Restart the Alfresco service
|
|
74
|
+
3. Ensure proper permissions are configured
|
|
75
|
+
|
|
76
|
+
**Alternative**: Use Core API tools for basic repository operations.""")
|
|
77
|
+
|
|
78
|
+
if repo_info and hasattr(repo_info, 'entry'):
|
|
79
|
+
entry = repo_info.entry
|
|
80
|
+
repository = getattr(entry, 'repository', {})
|
|
81
|
+
|
|
82
|
+
# Build comprehensive repository information
|
|
83
|
+
result = "🏢 **Alfresco Repository Information**\n\n"
|
|
84
|
+
|
|
85
|
+
# Repository ID and Edition
|
|
86
|
+
repo_id = getattr(repository, 'id', 'Unknown')
|
|
87
|
+
edition = getattr(repository, 'edition', 'Unknown')
|
|
88
|
+
logger.info(f"✅ Retrieved repository info: {edition} edition")
|
|
89
|
+
result += f"🆔 **Repository ID**: {repo_id}\n"
|
|
90
|
+
result += f"🏷️ **Edition**: {edition}\n\n"
|
|
91
|
+
|
|
92
|
+
# Version Information
|
|
93
|
+
version_info = getattr(repository, 'version', {})
|
|
94
|
+
if hasattr(version_info, 'major'):
|
|
95
|
+
result += "📦 **Version Details**:\n"
|
|
96
|
+
result += f" • Major: {getattr(version_info, 'major', 'Unknown')}\n"
|
|
97
|
+
result += f" • Minor: {getattr(version_info, 'minor', 'Unknown')}\n"
|
|
98
|
+
result += f" • Patch: {getattr(version_info, 'patch', 'Unknown')}\n"
|
|
99
|
+
result += f" • Hotfix: {getattr(version_info, 'hotfix', 'Unknown')}\n"
|
|
100
|
+
result += f" • Schema: {getattr(version_info, 'schema', 'Unknown')}\n"
|
|
101
|
+
result += f" • Label: {getattr(version_info, 'label', 'Unknown')}\n"
|
|
102
|
+
result += f" • Display: {getattr(version_info, 'display', 'Unknown')}\n\n"
|
|
103
|
+
|
|
104
|
+
# Repository Status
|
|
105
|
+
status_info = getattr(repository, 'status', {})
|
|
106
|
+
if hasattr(status_info, 'is_read_only'):
|
|
107
|
+
result += "STATUS **Repository Status**:\n"
|
|
108
|
+
result += f" • Read Only: {'Yes' if getattr(status_info, 'is_read_only', False) else 'No'}\n"
|
|
109
|
+
result += f" • Audit Enabled: {'Yes' if getattr(status_info, 'is_audit_enabled', False) else 'No'}\n"
|
|
110
|
+
result += f" • Quick Share Enabled: {'Yes' if getattr(status_info, 'is_quick_share_enabled', False) else 'No'}\n"
|
|
111
|
+
result += f" • Thumbnail Generation: {'Yes' if getattr(status_info, 'is_thumbnail_generation_enabled', False) else 'No'}\n\n"
|
|
112
|
+
|
|
113
|
+
# License Information
|
|
114
|
+
license_info = getattr(repository, 'license', {})
|
|
115
|
+
if hasattr(license_info, 'issued_at'):
|
|
116
|
+
result += "📄 **License Information**:\n"
|
|
117
|
+
result += f" • Issued At: {getattr(license_info, 'issued_at', 'Unknown')}\n"
|
|
118
|
+
result += f" • Expires At: {getattr(license_info, 'expires_at', 'Unknown')}\n"
|
|
119
|
+
result += f" • Remaining Days: {getattr(license_info, 'remaining_days', 'Unknown')}\n"
|
|
120
|
+
result += f" • Holder: {getattr(license_info, 'holder', 'Unknown')}\n"
|
|
121
|
+
result += f" • Mode: {getattr(license_info, 'mode', 'Unknown')}\n"
|
|
122
|
+
|
|
123
|
+
# License Entitlements
|
|
124
|
+
entitlements = getattr(license_info, 'entitlements', {})
|
|
125
|
+
if hasattr(entitlements, 'max_users'):
|
|
126
|
+
result += f" • Max Users: {getattr(entitlements, 'max_users', 'Unknown')}\n"
|
|
127
|
+
result += f" • Max Documents: {getattr(entitlements, 'max_docs', 'Unknown')}\n"
|
|
128
|
+
result += f" • Cluster Enabled: {'Yes' if getattr(entitlements, 'is_cluster_enabled', False) else 'No'}\n"
|
|
129
|
+
result += f" • Cryptodoc Enabled: {'Yes' if getattr(entitlements, 'is_cryptodoc_enabled', False) else 'No'}\n"
|
|
130
|
+
result += "\n"
|
|
131
|
+
|
|
132
|
+
# Modules Information
|
|
133
|
+
modules = getattr(repository, 'modules', [])
|
|
134
|
+
if modules and len(modules) > 0:
|
|
135
|
+
result += f"🧩 **Installed Modules** ({len(modules)} total):\n"
|
|
136
|
+
for i, module in enumerate(modules[:10], 1): # Show first 10 modules
|
|
137
|
+
module_id = getattr(module, 'id', 'Unknown')
|
|
138
|
+
module_title = getattr(module, 'title', 'Unknown')
|
|
139
|
+
module_version = getattr(module, 'version', 'Unknown')
|
|
140
|
+
module_state = getattr(module, 'install_state', 'Unknown')
|
|
141
|
+
result += f" {i}. **{module_title}** (ID: {module_id})\n"
|
|
142
|
+
result += f" • Version: {module_version}\n"
|
|
143
|
+
result += f" • State: {module_state}\n"
|
|
144
|
+
|
|
145
|
+
install_date = getattr(module, 'install_date', None)
|
|
146
|
+
if install_date:
|
|
147
|
+
result += f" • Installed: {install_date}\n"
|
|
148
|
+
result += "\n"
|
|
149
|
+
|
|
150
|
+
if len(modules) > 10:
|
|
151
|
+
result += f" *... and {len(modules) - 10} more modules*\n\n"
|
|
152
|
+
|
|
153
|
+
# Connection Details
|
|
154
|
+
result += "🔗 **Connection Details**:\n"
|
|
155
|
+
result += f" • Server: {os.getenv('ALFRESCO_URL', 'http://localhost:8080')}\n"
|
|
156
|
+
result += f" • Connected as: {os.getenv('ALFRESCO_USERNAME', 'admin')}\n"
|
|
157
|
+
result += f" • Data Source: Discovery API (High-Level)\n"
|
|
158
|
+
|
|
159
|
+
return safe_format_output(result)
|
|
160
|
+
|
|
161
|
+
except Exception as discovery_error:
|
|
162
|
+
error_str = str(discovery_error)
|
|
163
|
+
|
|
164
|
+
# Check if Discovery API is disabled (501 error)
|
|
165
|
+
if "501" in error_str or "Discovery is disabled" in error_str:
|
|
166
|
+
logger.warning("Discovery API is disabled on this Alfresco instance")
|
|
167
|
+
return safe_format_output(f"""WARNING: **Repository Information - Discovery API Disabled**
|
|
168
|
+
|
|
169
|
+
**Status**: Discovery API is disabled on this Alfresco instance (HTTP 501)
|
|
170
|
+
|
|
171
|
+
**Available Information**:
|
|
172
|
+
🔗 **Server**: {os.getenv('ALFRESCO_URL', 'http://localhost:8080')}
|
|
173
|
+
👤 **Connected as**: {os.getenv('ALFRESCO_USERNAME', 'admin')}
|
|
174
|
+
✅ **Core API**: Available (connection successful)
|
|
175
|
+
❌ **Discovery API**: Disabled by administrator
|
|
176
|
+
|
|
177
|
+
**Note**: The Discovery API provides detailed repository information including:
|
|
178
|
+
- Version and edition details
|
|
179
|
+
- License information and entitlements
|
|
180
|
+
- Installed modules and their versions
|
|
181
|
+
- Repository status and capabilities
|
|
182
|
+
|
|
183
|
+
**Administrator Action Required**: To enable Discovery API, the Alfresco administrator needs to:
|
|
184
|
+
1. Enable the Discovery API in the repository configuration
|
|
185
|
+
2. Restart the Alfresco service
|
|
186
|
+
3. Ensure proper permissions are configured
|
|
187
|
+
|
|
188
|
+
**Alternative**: Use Core API tools for basic repository operations.""")
|
|
189
|
+
else:
|
|
190
|
+
# Other Discovery API errors
|
|
191
|
+
logger.error(f"Discovery API failed: {error_str}")
|
|
192
|
+
return safe_format_output(f"""ERROR: **Repository Information Unavailable**
|
|
193
|
+
|
|
194
|
+
**Error**: Discovery API failed
|
|
195
|
+
**Details**: {error_str}
|
|
196
|
+
|
|
197
|
+
🔗 **Server**: {os.getenv('ALFRESCO_URL', 'http://localhost:8080')}
|
|
198
|
+
👤 **Connected as**: {os.getenv('ALFRESCO_USERNAME', 'admin')}
|
|
199
|
+
|
|
200
|
+
**Possible Causes**:
|
|
201
|
+
- Discovery API endpoint not available
|
|
202
|
+
- Insufficient permissions
|
|
203
|
+
- Network connectivity issues
|
|
204
|
+
- Repository service issues
|
|
205
|
+
**Recommendation**: Check server logs and verify Discovery API availability.""")
|