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.
Files changed (118) hide show
  1. app/__init__.py +0 -0
  2. app/__main__.py +35 -0
  3. app/confluence/__init__.py +52 -0
  4. app/confluence/analytics.py +173 -0
  5. app/confluence/attachments.py +561 -0
  6. app/confluence/client.py +223 -0
  7. app/confluence/comments.py +238 -0
  8. app/confluence/config.py +266 -0
  9. app/confluence/constants.py +50 -0
  10. app/confluence/labels.py +79 -0
  11. app/confluence/pages.py +1202 -0
  12. app/confluence/protocols.py +106 -0
  13. app/confluence/search.py +198 -0
  14. app/confluence/spaces.py +100 -0
  15. app/confluence/users.py +100 -0
  16. app/confluence/utils.py +122 -0
  17. app/confluence/v2_adapter.py +1128 -0
  18. app/core/__init__.py +0 -0
  19. app/core/auth.py +88 -0
  20. app/core/confluence.py +2239 -0
  21. app/core/context.py +23 -0
  22. app/core/dependencies.py +795 -0
  23. app/core/jira.py +3722 -0
  24. app/core/server.py +835 -0
  25. app/core/session_store.py +104 -0
  26. app/core/tools.py +11 -0
  27. app/exceptions.py +2 -0
  28. app/jira/__init__.py +98 -0
  29. app/jira/attachments.py +467 -0
  30. app/jira/boards.py +88 -0
  31. app/jira/client.py +389 -0
  32. app/jira/comments.py +264 -0
  33. app/jira/config.py +342 -0
  34. app/jira/configuration.py +25 -0
  35. app/jira/constants.py +216 -0
  36. app/jira/development.py +410 -0
  37. app/jira/epics.py +946 -0
  38. app/jira/field_options.py +235 -0
  39. app/jira/fields.py +862 -0
  40. app/jira/formatting.py +345 -0
  41. app/jira/forms.py +225 -0
  42. app/jira/forms_api.py +408 -0
  43. app/jira/forms_common.py +133 -0
  44. app/jira/issues.py +1612 -0
  45. app/jira/links.py +224 -0
  46. app/jira/metrics.py +408 -0
  47. app/jira/projects.py +465 -0
  48. app/jira/protocols.py +285 -0
  49. app/jira/queues.py +218 -0
  50. app/jira/search.py +307 -0
  51. app/jira/sla.py +678 -0
  52. app/jira/sprints.py +218 -0
  53. app/jira/transitions.py +409 -0
  54. app/jira/users.py +413 -0
  55. app/jira/utils.py +123 -0
  56. app/jira/watchers.py +98 -0
  57. app/jira/worklog.py +246 -0
  58. app/k8s_probes.py +13 -0
  59. app/main.py +43 -0
  60. app/models/__init__.py +107 -0
  61. app/models/base.py +126 -0
  62. app/models/confluence/__init__.py +38 -0
  63. app/models/confluence/analytics.py +52 -0
  64. app/models/confluence/comment.py +109 -0
  65. app/models/confluence/common.py +175 -0
  66. app/models/confluence/label.py +62 -0
  67. app/models/confluence/page.py +296 -0
  68. app/models/confluence/search.py +74 -0
  69. app/models/confluence/space.py +57 -0
  70. app/models/confluence/user_search.py +145 -0
  71. app/models/constants.py +63 -0
  72. app/models/jira/__init__.py +110 -0
  73. app/models/jira/adf.py +362 -0
  74. app/models/jira/agile.py +152 -0
  75. app/models/jira/comment.py +97 -0
  76. app/models/jira/common.py +597 -0
  77. app/models/jira/field_option.py +105 -0
  78. app/models/jira/forms.py +162 -0
  79. app/models/jira/issue.py +815 -0
  80. app/models/jira/link.py +285 -0
  81. app/models/jira/metrics.py +231 -0
  82. app/models/jira/project.py +111 -0
  83. app/models/jira/queue.py +246 -0
  84. app/models/jira/search.py +114 -0
  85. app/models/jira/sla.py +221 -0
  86. app/models/jira/version.py +46 -0
  87. app/models/jira/workflow.py +93 -0
  88. app/models/jira/worklog.py +109 -0
  89. app/preprocessing/__init__.py +15 -0
  90. app/preprocessing/base.py +419 -0
  91. app/preprocessing/confluence.py +105 -0
  92. app/preprocessing/jira.py +608 -0
  93. app/routers/__init__.py +0 -0
  94. app/routers/k8s_health.py +31 -0
  95. app/routers/metrics.py +61 -0
  96. app/utils/__init__.py +45 -0
  97. app/utils/client_storage.py +180 -0
  98. app/utils/date.py +65 -0
  99. app/utils/decorators.py +173 -0
  100. app/utils/env.py +93 -0
  101. app/utils/environment.py +179 -0
  102. app/utils/io.py +58 -0
  103. app/utils/lifecycle.py +85 -0
  104. app/utils/logging.py +118 -0
  105. app/utils/media.py +128 -0
  106. app/utils/oauth.py +724 -0
  107. app/utils/oauth_proxy.py +73 -0
  108. app/utils/oauth_setup.py +450 -0
  109. app/utils/ssl.py +122 -0
  110. app/utils/token_verifier.py +29 -0
  111. app/utils/tools.py +63 -0
  112. app/utils/toolsets.py +271 -0
  113. app/utils/urls.py +205 -0
  114. et_devops_mcp_atlassian-1.0.dist-info/METADATA +175 -0
  115. et_devops_mcp_atlassian-1.0.dist-info/RECORD +118 -0
  116. et_devops_mcp_atlassian-1.0.dist-info/WHEEL +5 -0
  117. et_devops_mcp_atlassian-1.0.dist-info/entry_points.txt +2 -0
  118. 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
+ )