et-devops-mcp-atlassian 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.
- app/__init__.py +0 -0
- app/__main__.py +35 -0
- app/confluence/__init__.py +52 -0
- app/confluence/analytics.py +173 -0
- app/confluence/attachments.py +561 -0
- app/confluence/client.py +223 -0
- app/confluence/comments.py +238 -0
- app/confluence/config.py +266 -0
- app/confluence/constants.py +50 -0
- app/confluence/labels.py +79 -0
- app/confluence/pages.py +1202 -0
- app/confluence/protocols.py +106 -0
- app/confluence/search.py +198 -0
- app/confluence/spaces.py +100 -0
- app/confluence/users.py +100 -0
- app/confluence/utils.py +122 -0
- app/confluence/v2_adapter.py +1128 -0
- app/core/__init__.py +0 -0
- app/core/auth.py +88 -0
- app/core/confluence.py +2239 -0
- app/core/context.py +23 -0
- app/core/dependencies.py +795 -0
- app/core/jira.py +3722 -0
- app/core/server.py +835 -0
- app/core/session_store.py +104 -0
- app/core/tools.py +11 -0
- app/exceptions.py +2 -0
- app/jira/__init__.py +98 -0
- app/jira/attachments.py +467 -0
- app/jira/boards.py +88 -0
- app/jira/client.py +389 -0
- app/jira/comments.py +264 -0
- app/jira/config.py +342 -0
- app/jira/configuration.py +25 -0
- app/jira/constants.py +216 -0
- app/jira/development.py +410 -0
- app/jira/epics.py +946 -0
- app/jira/field_options.py +235 -0
- app/jira/fields.py +862 -0
- app/jira/formatting.py +345 -0
- app/jira/forms.py +225 -0
- app/jira/forms_api.py +408 -0
- app/jira/forms_common.py +133 -0
- app/jira/issues.py +1612 -0
- app/jira/links.py +224 -0
- app/jira/metrics.py +408 -0
- app/jira/projects.py +465 -0
- app/jira/protocols.py +285 -0
- app/jira/queues.py +218 -0
- app/jira/search.py +307 -0
- app/jira/sla.py +678 -0
- app/jira/sprints.py +218 -0
- app/jira/transitions.py +409 -0
- app/jira/users.py +413 -0
- app/jira/utils.py +123 -0
- app/jira/watchers.py +98 -0
- app/jira/worklog.py +246 -0
- app/k8s_probes.py +13 -0
- app/main.py +43 -0
- app/models/__init__.py +107 -0
- app/models/base.py +126 -0
- app/models/confluence/__init__.py +38 -0
- app/models/confluence/analytics.py +52 -0
- app/models/confluence/comment.py +109 -0
- app/models/confluence/common.py +175 -0
- app/models/confluence/label.py +62 -0
- app/models/confluence/page.py +296 -0
- app/models/confluence/search.py +74 -0
- app/models/confluence/space.py +57 -0
- app/models/confluence/user_search.py +145 -0
- app/models/constants.py +63 -0
- app/models/jira/__init__.py +110 -0
- app/models/jira/adf.py +362 -0
- app/models/jira/agile.py +152 -0
- app/models/jira/comment.py +97 -0
- app/models/jira/common.py +597 -0
- app/models/jira/field_option.py +105 -0
- app/models/jira/forms.py +162 -0
- app/models/jira/issue.py +815 -0
- app/models/jira/link.py +285 -0
- app/models/jira/metrics.py +231 -0
- app/models/jira/project.py +111 -0
- app/models/jira/queue.py +246 -0
- app/models/jira/search.py +114 -0
- app/models/jira/sla.py +221 -0
- app/models/jira/version.py +46 -0
- app/models/jira/workflow.py +93 -0
- app/models/jira/worklog.py +109 -0
- app/preprocessing/__init__.py +15 -0
- app/preprocessing/base.py +419 -0
- app/preprocessing/confluence.py +105 -0
- app/preprocessing/jira.py +608 -0
- app/routers/__init__.py +0 -0
- app/routers/k8s_health.py +31 -0
- app/routers/metrics.py +61 -0
- app/utils/__init__.py +45 -0
- app/utils/client_storage.py +180 -0
- app/utils/date.py +65 -0
- app/utils/decorators.py +173 -0
- app/utils/env.py +93 -0
- app/utils/environment.py +179 -0
- app/utils/io.py +58 -0
- app/utils/lifecycle.py +85 -0
- app/utils/logging.py +118 -0
- app/utils/media.py +128 -0
- app/utils/oauth.py +724 -0
- app/utils/oauth_proxy.py +73 -0
- app/utils/oauth_setup.py +450 -0
- app/utils/ssl.py +122 -0
- app/utils/token_verifier.py +29 -0
- app/utils/tools.py +63 -0
- app/utils/toolsets.py +271 -0
- app/utils/urls.py +205 -0
- et_devops_mcp_atlassian-1.0.dist-info/METADATA +175 -0
- et_devops_mcp_atlassian-1.0.dist-info/RECORD +118 -0
- et_devops_mcp_atlassian-1.0.dist-info/WHEEL +5 -0
- et_devops_mcp_atlassian-1.0.dist-info/entry_points.txt +2 -0
- et_devops_mcp_atlassian-1.0.dist-info/top_level.txt +1 -0
app/__init__.py
ADDED
|
File without changes
|
app/__main__.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
|
|
5
|
+
load_dotenv(override=False)
|
|
6
|
+
|
|
7
|
+
if not os.getenv("JIRA_URL") and os.getenv("ATLASSIAN_DC_JIRA_URL"):
|
|
8
|
+
os.environ["JIRA_URL"] = os.getenv("ATLASSIAN_DC_JIRA_URL") or ""
|
|
9
|
+
if not os.getenv("CONFLUENCE_URL") and os.getenv("ATLASSIAN_DC_CONFLUENCE_URL"):
|
|
10
|
+
os.environ["CONFLUENCE_URL"] = os.getenv("ATLASSIAN_DC_CONFLUENCE_URL") or ""
|
|
11
|
+
|
|
12
|
+
from app.core import tools # noqa: E402
|
|
13
|
+
from app.core.server import create_mcp # noqa: E402
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main() -> None:
|
|
17
|
+
mcp = create_mcp(
|
|
18
|
+
name="Atlassian MCP",
|
|
19
|
+
instructions="""
|
|
20
|
+
This server provides tools to interact with Atlassian Jira and Confluence.
|
|
21
|
+
|
|
22
|
+
AUTHENTICATION: Every Jira tool requires a jira_pat parameter and every Confluence tool requires a confluence_pat parameter.
|
|
23
|
+
Jira and Confluence are separate systems and always require separate PATs — never reuse a Jira PAT for Confluence or vice versa.
|
|
24
|
+
Ask the user for the relevant PAT before calling any tool, then pass it in every call.
|
|
25
|
+
|
|
26
|
+
Use the Jira tools to manage issues, projects, sprints, and workflows.
|
|
27
|
+
Use the Confluence tools to search, read, and manage pages and spaces.
|
|
28
|
+
""",
|
|
29
|
+
)
|
|
30
|
+
mcp.mount(tools.mcp)
|
|
31
|
+
mcp.run()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
main()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Confluence API integration module.
|
|
2
|
+
|
|
3
|
+
This module provides access to Confluence content through the Model Context Protocol.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .analytics import AnalyticsMixin
|
|
7
|
+
from .attachments import AttachmentsMixin
|
|
8
|
+
from .client import ConfluenceClient
|
|
9
|
+
from .comments import CommentsMixin
|
|
10
|
+
from .config import ConfluenceConfig
|
|
11
|
+
from .labels import LabelsMixin
|
|
12
|
+
from .pages import PagesMixin
|
|
13
|
+
from .search import SearchMixin
|
|
14
|
+
from .spaces import SpacesMixin
|
|
15
|
+
from .users import UsersMixin
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ConfluenceFetcher(
|
|
19
|
+
SearchMixin,
|
|
20
|
+
SpacesMixin,
|
|
21
|
+
PagesMixin,
|
|
22
|
+
CommentsMixin,
|
|
23
|
+
LabelsMixin,
|
|
24
|
+
UsersMixin,
|
|
25
|
+
AnalyticsMixin,
|
|
26
|
+
AttachmentsMixin,
|
|
27
|
+
):
|
|
28
|
+
"""Main entry point for Confluence operations, providing backward compatibility.
|
|
29
|
+
|
|
30
|
+
This class combines functionality from various mixins to maintain the same
|
|
31
|
+
API as the original ConfluenceFetcher class.
|
|
32
|
+
|
|
33
|
+
Available mixins:
|
|
34
|
+
- SearchMixin: CQL search operations
|
|
35
|
+
- SpacesMixin: Space operations
|
|
36
|
+
- PagesMixin: Page operations
|
|
37
|
+
- CommentsMixin: Comment operations
|
|
38
|
+
- LabelsMixin: Label operations
|
|
39
|
+
- UsersMixin: User operations
|
|
40
|
+
- AnalyticsMixin: Page view analytics (Cloud only)
|
|
41
|
+
- AttachmentsMixin: Attachment operations
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
__all__ = [
|
|
48
|
+
"ConfluenceFetcher",
|
|
49
|
+
"ConfluenceConfig",
|
|
50
|
+
"ConfluenceClient",
|
|
51
|
+
"AnalyticsMixin",
|
|
52
|
+
]
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Analytics mixin for Confluence page view statistics.
|
|
2
|
+
|
|
3
|
+
This module provides functionality to retrieve page view statistics
|
|
4
|
+
from Confluence Cloud using the Analytics API.
|
|
5
|
+
|
|
6
|
+
Note: The Analytics API is only available for Confluence Cloud.
|
|
7
|
+
Server/Data Center instances do not support this API.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from requests.exceptions import HTTPError
|
|
15
|
+
|
|
16
|
+
from ..models.confluence.analytics import PageViews, PageViewsBatchResponse
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger("mcp-atlassian")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AnalyticsMixin:
|
|
22
|
+
"""Mixin providing Confluence page view analytics functionality.
|
|
23
|
+
|
|
24
|
+
This mixin requires the class to have:
|
|
25
|
+
- self.confluence: Atlassian Confluence client
|
|
26
|
+
- self.config: ConfluenceConfig instance
|
|
27
|
+
- self.v2_adapter: Optional ConfluenceV2Adapter for OAuth
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
# Type hints for attributes expected from the base class
|
|
31
|
+
confluence: Any
|
|
32
|
+
config: Any
|
|
33
|
+
v2_adapter: Any
|
|
34
|
+
|
|
35
|
+
def get_page_views(
|
|
36
|
+
self,
|
|
37
|
+
page_id: str,
|
|
38
|
+
include_title: bool = True,
|
|
39
|
+
) -> PageViews:
|
|
40
|
+
"""Get view statistics for a Confluence page.
|
|
41
|
+
|
|
42
|
+
Note: This API is only available for Confluence Cloud.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
page_id: The ID of the page
|
|
46
|
+
include_title: Whether to fetch and include the page title
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
PageViews with view statistics
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
ValueError: If the page is not found or API fails
|
|
53
|
+
HTTPError: If authentication fails (401/403 are propagated)
|
|
54
|
+
"""
|
|
55
|
+
if not self.config.is_cloud:
|
|
56
|
+
raise ValueError(
|
|
57
|
+
"Page view analytics is only available for Confluence Cloud. "
|
|
58
|
+
"Server/Data Center instances do not support the Analytics API."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Get page title if requested
|
|
62
|
+
page_title = None
|
|
63
|
+
if include_title:
|
|
64
|
+
try:
|
|
65
|
+
page_info = self.confluence.get_page_by_id(page_id, expand="title")
|
|
66
|
+
page_title = page_info.get("title")
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logger.warning(f"Could not fetch title for page {page_id}: {e}")
|
|
69
|
+
|
|
70
|
+
# Get view statistics using v2 adapter or direct API
|
|
71
|
+
try:
|
|
72
|
+
if hasattr(self, "v2_adapter") and self.v2_adapter:
|
|
73
|
+
views_data = self.v2_adapter.get_page_views(page_id)
|
|
74
|
+
else:
|
|
75
|
+
views_data = self._get_page_views_direct(page_id)
|
|
76
|
+
|
|
77
|
+
# Parse the response
|
|
78
|
+
total_views = views_data.get("count", 0)
|
|
79
|
+
|
|
80
|
+
# Parse last viewed timestamp if available
|
|
81
|
+
last_viewed = None
|
|
82
|
+
last_seen_str = views_data.get("lastSeen")
|
|
83
|
+
if last_seen_str:
|
|
84
|
+
try:
|
|
85
|
+
# Try parsing ISO format timestamp
|
|
86
|
+
last_viewed = datetime.fromisoformat(
|
|
87
|
+
last_seen_str.replace("Z", "+00:00")
|
|
88
|
+
)
|
|
89
|
+
except (ValueError, AttributeError):
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
return PageViews(
|
|
93
|
+
page_id=page_id,
|
|
94
|
+
page_title=page_title,
|
|
95
|
+
total_views=total_views,
|
|
96
|
+
last_viewed=last_viewed,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
except HTTPError as e:
|
|
100
|
+
# Propagate auth errors
|
|
101
|
+
if e.response is not None and e.response.status_code in [401, 403]:
|
|
102
|
+
raise
|
|
103
|
+
logger.warning(f"Failed to get views for page {page_id}: {e}")
|
|
104
|
+
# Return zero views on error (non-auth)
|
|
105
|
+
return PageViews(
|
|
106
|
+
page_id=page_id,
|
|
107
|
+
page_title=page_title,
|
|
108
|
+
total_views=0,
|
|
109
|
+
)
|
|
110
|
+
except Exception as e:
|
|
111
|
+
logger.warning(f"Unexpected error getting views for page {page_id}: {e}")
|
|
112
|
+
return PageViews(
|
|
113
|
+
page_id=page_id,
|
|
114
|
+
page_title=page_title,
|
|
115
|
+
total_views=0,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def _get_page_views_direct(
|
|
119
|
+
self,
|
|
120
|
+
page_id: str,
|
|
121
|
+
) -> dict:
|
|
122
|
+
"""Get page views using direct API call.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
page_id: The ID of the page
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Dictionary with view statistics
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
HTTPError: If the API request fails
|
|
132
|
+
"""
|
|
133
|
+
url = f"{self.confluence.url}/rest/api/analytics/content/{page_id}/views"
|
|
134
|
+
response = self.confluence._session.get(url)
|
|
135
|
+
response.raise_for_status()
|
|
136
|
+
return response.json()
|
|
137
|
+
|
|
138
|
+
def batch_get_page_views(
|
|
139
|
+
self,
|
|
140
|
+
page_ids: list[str],
|
|
141
|
+
include_title: bool = True,
|
|
142
|
+
) -> PageViewsBatchResponse:
|
|
143
|
+
"""Get view statistics for multiple pages.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
page_ids: List of page IDs
|
|
147
|
+
include_title: Whether to fetch and include page titles
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
PageViewsBatchResponse with results for all pages
|
|
151
|
+
"""
|
|
152
|
+
pages: list[PageViews] = []
|
|
153
|
+
errors: list[dict[str, str]] = []
|
|
154
|
+
|
|
155
|
+
for page_id in page_ids:
|
|
156
|
+
try:
|
|
157
|
+
page_views = self.get_page_views(page_id, include_title=include_title)
|
|
158
|
+
pages.append(page_views)
|
|
159
|
+
except HTTPError as e:
|
|
160
|
+
# Propagate auth errors
|
|
161
|
+
if e.response is not None and e.response.status_code in [401, 403]:
|
|
162
|
+
raise
|
|
163
|
+
errors.append({"page_id": page_id, "error": str(e)})
|
|
164
|
+
except Exception as e:
|
|
165
|
+
errors.append({"page_id": page_id, "error": str(e)})
|
|
166
|
+
|
|
167
|
+
return PageViewsBatchResponse(
|
|
168
|
+
pages=pages,
|
|
169
|
+
total_count=len(page_ids),
|
|
170
|
+
success_count=len(pages),
|
|
171
|
+
error_count=len(errors),
|
|
172
|
+
errors=errors,
|
|
173
|
+
)
|