From 09e1e5298599c4c5ba53eb73044bbea43f25ecf8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 07:21:39 +0000 Subject: [PATCH 1/3] Initial plan From 4a14e85bd42a2581061999de35a0f4fc601b9bc2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 07:26:15 +0000 Subject: [PATCH 2/3] Add Atlassian Jira MCP integration (jira_client.py + jira_mcp.py) Agent-Logs-Url: https://github.com/soloxio/iot-client-simulator/sessions/5300316b-4b8d-4be0-a1d4-729e2a1d784f Co-authored-by: soloxio <44388140+soloxio@users.noreply.github.com> --- __pycache__/jira_client.cpython-312.pyc | Bin 0 -> 9938 bytes __pycache__/jira_mcp.cpython-312.pyc | Bin 0 -> 3631 bytes jira_client.py | 230 ++++++++++++++++++++++++ jira_mcp.py | 76 ++++++++ 4 files changed, 306 insertions(+) create mode 100644 __pycache__/jira_client.cpython-312.pyc create mode 100644 __pycache__/jira_mcp.cpython-312.pyc create mode 100644 jira_client.py create mode 100644 jira_mcp.py diff --git a/__pycache__/jira_client.cpython-312.pyc b/__pycache__/jira_client.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc1190bddd1200bcb00922bcdd341cf043b74072 GIT binary patch literal 9938 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 HP%i%;RRHXQ literal 0 HcmV?d00001 diff --git a/__pycache__/jira_mcp.cpython-312.pyc b/__pycache__/jira_mcp.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df78b890cdd65ffc33f39ec855b15d74a2fc5c30 GIT binary patch literal 3631 zcmZ`+U2Gf25#Hk+|Be(X$#QHfinORLi&P>haTBLyY`3&TS+r%!)lX5Y4GtIHN;-)= zGJ8i$B&b3u3RFe|RA_*jLQvQbez0K|$y1R61q$@3KwqdxhLwXJiY7qeHzjI;!hPxN zQb#eiyufjHXJ%(-XJ@`y{=KP5M$kUk{G0weA3|TUiPNOoK-l{&3~nI}O(RXvTp1zj znsy0nEM~-M5qc>jW!=;6tY_Mj^-g=UzG)xu-P3-}Gc9Z0>3|m0e7}^YgIbg3hrUS* zX)^R764n9>5rpV*7b4<3cD6p~fgXCWKYbw71_COIMKKyyJ6s426DvK3FY8prx@i^& z9(uj+4SYjau{B3<+{&n?sjCLQI(QB1hDBznYUw!xtA>X26evtirsVyCPKkzRbdu4` zep#L})mb9bp1p{Ps6PszO=dD;wmN7K4 zl+RHMUrvt4m5cF-gfcZgdVbEb@@8ycAY07Q*?!frv)>@rbBm@X665C!CZRthOKLWs zA^quG_BpBewWKmRb|sNIf3BD-&^|S<_gT3GVqD;&`Jy!k=bi^oDcN+sKVOvP|?7p(= zW2E`ZB3L0E?Dyeu2^oO~Ir)IsKn)yF!NO)|=Yt+BY;|T}p;VS=tSo^wZ-OQ622H`T zd%n$)Jm|r;Cg2=;n_37shu>R+=RQ0up<-inG@PV}qTy62$RiIgkRp!ZQgEV>&8oBr z0K#VwzXg-Q37f?sNoNfJBR-2142E(*(4wWzcsfn;mTIISC#Y@_KqwrU(Wz)TV&n{hYx!3?KB^lFka`?SAzf?B&O~D+5#yhhT<_w78`k50u6%@} z0o%H+iTYBwLK;`)%NY%MC>DK-6X{^$@VH&<3Ca2gSMX-g)9MBCU#SXZ?Dw0 zw`DFenoM1pu>FjsSdg(%kL~6Hwy&;C&~5+zBxl%eCTrRf?^uE`w_*JB(!g9UO9lX@ z4MGPNb97-q2kc4nHy@%?)js>fqh`JFB4 z+*gvnD)(+luWpCi9)}|v;mEz?4|}TNGb7J>h75Auhqq0TK|7uU<|+YYuo4t8w>yY2+;wpW9% ztt7ti2DjTzFoj8Bb%<#jtVp**>!Iqgo;&p3sZS#JBlp@ree1KfzjXYmhtH83ursOn|q;a!V5$_=jYe=4#*{f`ppbz&`}}G)b2j zTFnO>D-QZHvYMP(89Dgk;MfF;x>Kdr=L=%#C@XPLO!Rc^p}`6u(Ml~1B~K%U4x})T zVqQ>d>F1^Fj5gD%q{b$d!LihE^3v3JV#q!kpPC#=q$ZPt@yX;^N*RtPM}gdOEiryI zIWfUVLy1%pNMV>xCHVT`v8mLMEzT0l7EOyrU3PH49P>o7J-ncrCPOuhDT`q{q&Om+ zNuxk~6F&1t@Uw!x@*R2XJH6pMU5%XEVdLnAFIs)|A|H2e_`0h-F+M)I;X7IFKD+C8 zhde98yFnCe{Y~sc?T{d zz<7{(wpsNEac*l_u)K~`S#YTEeiTk3s~!)h5S?0fS@jqwyXNcF%vp`<5dc-IUfDDk zuUC!L{D3EdovLi(Ul=;;5Lm1Qm3vjg-BUcjFIe1a#B!1JhhmF|j zZI8yaLF5!ohUpri6y8pr8d}n0OhzP2X+T@{EHjz&8OqkeNXn|ZL0RBjfX`%UxPo?F zes{}G%gY~ita`SaUwYh(H=6O?_RZ$kR^25qYdu_2A>e}*QHuY7G+-_fQuLP^1?uUb)T)uz#k?-xDaP$9m{h;8f zc@#GOMQL$n3zn4;y None: + self.failure_type = failure_type + self.detail = detail + super().__init__(f"[{failure_type.value}] {detail}") + + +@dataclass +class LinkedIssue: + key: str + summary: str + link_type: str + + +@dataclass +class JiraIssue: + key: str + summary: str + status: str + assignee: str | None + acceptance_criteria: list[str] = field(default_factory=list) + linked_issues: list[LinkedIssue] = field(default_factory=list) + + +class JiraClient: + """Minimal Jira REST v3 client driven by environment variables. + + Required environment variables + ------------------------------ + JIRA_BASE_URL e.g. ``https://myorg.atlassian.net`` + JIRA_USER Atlassian account e-mail + JIRA_API_TOKEN API token from https://id.atlassian.com/manage-profile/security/api-tokens + """ + + _ENV_BASE_URL = "JIRA_BASE_URL" + _ENV_USER = "JIRA_USER" + _ENV_TOKEN = "JIRA_API_TOKEN" + + def __init__(self) -> None: + base_url = os.environ.get(self._ENV_BASE_URL, "").strip() + user = os.environ.get(self._ENV_USER, "").strip() + token = os.environ.get(self._ENV_TOKEN, "").strip() + + if not (base_url and user and token): + missing = [ + v + for v, val in ( + (self._ENV_BASE_URL, base_url), + (self._ENV_USER, user), + (self._ENV_TOKEN, token), + ) + if not val + ] + raise JiraMcpError( + FailureType.MCP_NOT_CONFIGURED, + f"Missing environment variable(s): {', '.join(missing)}. " + "Set JIRA_BASE_URL, JIRA_USER, and JIRA_API_TOKEN to enable " + "Atlassian MCP access.", + ) + + self._base_url = base_url.rstrip("/") + self._auth = (user, token) + + # ------------------------------------------------------------------ + # Public API + # ------------------------------------------------------------------ + + def get_issue(self, issue_key: str) -> JiraIssue: + """Fetch a Jira issue and return a structured :class:`JiraIssue`. + + Parameters + ---------- + issue_key: + Jira issue key, e.g. ``"D0-1"``. + + Raises + ------ + JiraMcpError + With the appropriate :class:`FailureType` on any error. + """ + data = self._fetch_issue_json(issue_key) + return self._parse(data) + + # ------------------------------------------------------------------ + # Internal helpers + # ------------------------------------------------------------------ + + def _fetch_issue_json(self, issue_key: str) -> dict[str, Any]: + url = f"{self._base_url}/rest/api/3/issue/{issue_key}" + try: + response = requests.get( + url, + auth=self._auth, + headers={"Accept": "application/json"}, + timeout=15, + ) + except requests.RequestException as exc: + # Network errors mean the integration cannot reach Jira at all, + # which is functionally equivalent to the MCP not being configured + # (wrong base URL, no connectivity, firewall, etc.). + raise JiraMcpError( + FailureType.MCP_NOT_CONFIGURED, + f"Cannot reach Jira at {self._base_url} – check JIRA_BASE_URL " + f"and network connectivity. Underlying error: {exc}", + ) from exc + + if response.status_code == 401: + raise JiraMcpError( + FailureType.AUTHENTICATION_FAILED, + "Jira rejected the supplied credentials (HTTP 401). " + "Check JIRA_USER and JIRA_API_TOKEN.", + ) + if response.status_code == 403: + raise JiraMcpError( + FailureType.PERMISSION_DENIED, + f"Authenticated but not authorised to read {issue_key} (HTTP 403).", + ) + if response.status_code == 404: + raise JiraMcpError( + FailureType.ISSUE_NOT_FOUND, + f"Issue {issue_key!r} was not found in Jira (HTTP 404).", + ) + if not response.ok: + # Unexpected status codes (e.g. 500 / 502 / 503) indicate the Jira + # service is unhealthy or the base URL points to the wrong host. + # Mapped to MCP_NOT_CONFIGURED because the integration cannot be + # used in its current state (the problem statement defines only the + # four failure types: not-configured / auth / permission / not-found). + raise JiraMcpError( + FailureType.MCP_NOT_CONFIGURED, + f"Jira returned HTTP {response.status_code} (server or proxy error) " + f"for {issue_key}. Verify JIRA_BASE_URL. " + f"Response: {response.text[:200]}", + ) + + return response.json() + + @staticmethod + def _parse(data: dict[str, Any]) -> JiraIssue: + fields: dict[str, Any] = data.get("fields", {}) + + # --- assignee --- + assignee_obj = fields.get("assignee") or {} + assignee = assignee_obj.get("displayName") or assignee_obj.get("name") + + # --- acceptance criteria --- + # Commonly stored in a custom field named "Acceptance Criteria" or + # in the description document. We check the custom field first. + raw_ac = ( + fields.get("customfield_acceptance_criteria") + or fields.get("customfield_10016") # common AC field id + or "" + ) + if isinstance(raw_ac, dict): + # Atlassian Document Format – extract plain text from paragraphs + raw_ac = _extract_adf_text(raw_ac) + acceptance_criteria = _parse_criteria_lines(str(raw_ac)) if raw_ac else [] + + # --- linked issues --- + links_raw: list[dict] = fields.get("issuelinks", []) + linked: list[LinkedIssue] = [] + for link in links_raw: + link_type = link.get("type", {}).get("name", "") + for direction in ("inwardIssue", "outwardIssue"): + target = link.get(direction) + if target: + linked.append( + LinkedIssue( + key=target.get("key", ""), + summary=target.get("fields", {}).get("summary", ""), + link_type=link_type, + ) + ) + + return JiraIssue( + key=data.get("key", ""), + summary=fields.get("summary", ""), + status=fields.get("status", {}).get("name", ""), + assignee=assignee, + acceptance_criteria=acceptance_criteria, + linked_issues=linked, + ) + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _extract_adf_text(doc: dict) -> str: + """Recursively extract plain text from an Atlassian Document Format node.""" + parts: list[str] = [] + if doc.get("type") == "text": + parts.append(doc.get("text", "")) + for child in doc.get("content", []): + parts.append(_extract_adf_text(child)) + return "\n".join(filter(None, parts)) + + +def _parse_criteria_lines(text: str) -> list[str]: + """Split multi-line acceptance-criteria text into individual criteria.""" + lines = [line.strip().lstrip("-*•·").strip() for line in text.splitlines()] + return [line for line in lines if line] diff --git a/jira_mcp.py b/jira_mcp.py new file mode 100644 index 0000000..1804a25 --- /dev/null +++ b/jira_mcp.py @@ -0,0 +1,76 @@ +"""Fetch Jira issue D0-1 via the Atlassian MCP integration and print the +required fields. + +Usage +----- +Set the three environment variables and run this script:: + + export JIRA_BASE_URL=https://myorg.atlassian.net + export JIRA_USER=user@example.com + export JIRA_API_TOKEN= + python jira_mcp.py + +If any variable is missing the script reports **MCP not configured** and +exits with code 1. Other failure modes (auth / permission / not-found) are +reported with their exact label so the caller can take corrective action. +""" + +from __future__ import annotations + +import sys + +from jira_client import FailureType, JiraClient, JiraIssue, JiraMcpError + +_ISSUE_KEY = "D0-1" +_MAX_AC = 3 +_MAX_LINKS = 2 + + +def _print_issue(issue: JiraIssue) -> None: + print(f"Issue key : {issue.key}") + print(f"Summary : {issue.summary}") + print(f"Status : {issue.status}") + print(f"Assignee : {issue.assignee or '(unassigned)'}") + + print("Acceptance criteria (first 3):") + criteria = issue.acceptance_criteria[:_MAX_AC] + if criteria: + for i, criterion in enumerate(criteria, 1): + print(f" {i}. {criterion}") + else: + print(" (none found)") + + print("Linked issues (first 2):") + links = issue.linked_issues[:_MAX_LINKS] + if links: + for link in links: + print(f" [{link.link_type}] {link.key} – {link.summary}") + else: + print(" (none found)") + + +def _failure_label(failure_type: FailureType) -> str: + labels = { + FailureType.MCP_NOT_CONFIGURED: "MCP not configured", + FailureType.AUTHENTICATION_FAILED: "authentication failed", + FailureType.PERMISSION_DENIED: "permission denied", + FailureType.ISSUE_NOT_FOUND: "issue not found", + } + return labels.get(failure_type, str(failure_type)) + + +def main() -> int: + try: + client = JiraClient() + issue = client.get_issue(_ISSUE_KEY) + _print_issue(issue) + return 0 + except JiraMcpError as exc: + label = _failure_label(exc.failure_type) + print(f"ERROR: {label}") + print(f"Detail: {exc.detail}", file=sys.stderr) + return 1 + + +if __name__ == "__main__": + sys.exit(main()) From 2a1b41afddc6b75c452d7f9c266ee63e06ed6d03 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 29 Mar 2026 07:26:37 +0000 Subject: [PATCH 3/3] Add .gitignore and remove accidentally committed __pycache__ files Agent-Logs-Url: https://github.com/soloxio/iot-client-simulator/sessions/5300316b-4b8d-4be0-a1d4-729e2a1d784f Co-authored-by: soloxio <44388140+soloxio@users.noreply.github.com> --- .gitignore | 9 +++++++++ __pycache__/jira_client.cpython-312.pyc | Bin 9938 -> 0 bytes __pycache__/jira_mcp.cpython-312.pyc | Bin 3631 -> 0 bytes 3 files changed, 9 insertions(+) create mode 100644 .gitignore delete mode 100644 __pycache__/jira_client.cpython-312.pyc delete mode 100644 __pycache__/jira_mcp.cpython-312.pyc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e61f6e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +__pycache__/ +*.py[cod] +*.pyo +.venv/ +venv/ +*.egg-info/ +dist/ +build/ +.env diff --git a/__pycache__/jira_client.cpython-312.pyc b/__pycache__/jira_client.cpython-312.pyc deleted file mode 100644 index cc1190bddd1200bcb00922bcdd341cf043b74072..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9938 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 HP%i%;RRHXQ diff --git a/__pycache__/jira_mcp.cpython-312.pyc b/__pycache__/jira_mcp.cpython-312.pyc deleted file mode 100644 index df78b890cdd65ffc33f39ec855b15d74a2fc5c30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3631 zcmZ`+U2Gf25#Hk+|Be(X$#QHfinORLi&P>haTBLyY`3&TS+r%!)lX5Y4GtIHN;-)= zGJ8i$B&b3u3RFe|RA_*jLQvQbez0K|$y1R61q$@3KwqdxhLwXJiY7qeHzjI;!hPxN zQb#eiyufjHXJ%(-XJ@`y{=KP5M$kUk{G0weA3|TUiPNOoK-l{&3~nI}O(RXvTp1zj znsy0nEM~-M5qc>jW!=;6tY_Mj^-g=UzG)xu-P3-}Gc9Z0>3|m0e7}^YgIbg3hrUS* zX)^R764n9>5rpV*7b4<3cD6p~fgXCWKYbw71_COIMKKyyJ6s426DvK3FY8prx@i^& z9(uj+4SYjau{B3<+{&n?sjCLQI(QB1hDBznYUw!xtA>X26evtirsVyCPKkzRbdu4` zep#L})mb9bp1p{Ps6PszO=dD;wmN7K4 zl+RHMUrvt4m5cF-gfcZgdVbEb@@8ycAY07Q*?!frv)>@rbBm@X665C!CZRthOKLWs zA^quG_BpBewWKmRb|sNIf3BD-&^|S<_gT3GVqD;&`Jy!k=bi^oDcN+sKVOvP|?7p(= zW2E`ZB3L0E?Dyeu2^oO~Ir)IsKn)yF!NO)|=Yt+BY;|T}p;VS=tSo^wZ-OQ622H`T zd%n$)Jm|r;Cg2=;n_37shu>R+=RQ0up<-inG@PV}qTy62$RiIgkRp!ZQgEV>&8oBr z0K#VwzXg-Q37f?sNoNfJBR-2142E(*(4wWzcsfn;mTIISC#Y@_KqwrU(Wz)TV&n{hYx!3?KB^lFka`?SAzf?B&O~D+5#yhhT<_w78`k50u6%@} z0o%H+iTYBwLK;`)%NY%MC>DK-6X{^$@VH&<3Ca2gSMX-g)9MBCU#SXZ?Dw0 zw`DFenoM1pu>FjsSdg(%kL~6Hwy&;C&~5+zBxl%eCTrRf?^uE`w_*JB(!g9UO9lX@ z4MGPNb97-q2kc4nHy@%?)js>fqh`JFB4 z+*gvnD)(+luWpCi9)}|v;mEz?4|}TNGb7J>h75Auhqq0TK|7uU<|+YYuo4t8w>yY2+;wpW9% ztt7ti2DjTzFoj8Bb%<#jtVp**>!Iqgo;&p3sZS#JBlp@ree1KfzjXYmhtH83ursOn|q;a!V5$_=jYe=4#*{f`ppbz&`}}G)b2j zTFnO>D-QZHvYMP(89Dgk;MfF;x>Kdr=L=%#C@XPLO!Rc^p}`6u(Ml~1B~K%U4x})T zVqQ>d>F1^Fj5gD%q{b$d!LihE^3v3JV#q!kpPC#=q$ZPt@yX;^N*RtPM}gdOEiryI zIWfUVLy1%pNMV>xCHVT`v8mLMEzT0l7EOyrU3PH49P>o7J-ncrCPOuhDT`q{q&Om+ zNuxk~6F&1t@Uw!x@*R2XJH6pMU5%XEVdLnAFIs)|A|H2e_`0h-F+M)I;X7IFKD+C8 zhde98yFnCe{Y~sc?T{d zz<7{(wpsNEac*l_u)K~`S#YTEeiTk3s~!)h5S?0fS@jqwyXNcF%vp`<5dc-IUfDDk zuUC!L{D3EdovLi(Ul=;;5Lm1Qm3vjg-BUcjFIe1a#B!1JhhmF|j zZI8yaLF5!ohUpri6y8pr8d}n0OhzP2X+T@{EHjz&8OqkeNXn|ZL0RBjfX`%UxPo?F zes{}G%gY~ita`SaUwYh(H=6O?_RZ$kR^25qYdu_2A>e}*QHuY7G+-_fQuLP^1?uUb)T)uz#k?-xDaP$9m{h;8f zc@#GOMQL$n3zn4;y