Coder preparations

This commit is contained in:
2026-09-22 23:21:59 +03:00
parent 3d1df7fee5
commit 1f2dc25e0b
32 changed files with 3205 additions and 26 deletions
+49 -1
View File
@@ -1,5 +1,7 @@
import asyncio
import json
import logging
from typing import Optional, Any
import httpx
from contextlib import AsyncExitStack
@@ -109,4 +111,50 @@ class YouTrackMCPClient:
async def list_tools(self):
"""List all available tools from the MCP server."""
return await self._client.list_tools()
return await self._client.list_tools()
@classmethod
def extract_comment_id(cls, result: Any) -> Optional[str]:
"""
Best-effort extraction of the created comment's ID from a
CallToolResult (or any MCP-ish response).
Returns None if the ID can't be determined. Callers MUST tolerate
None by falling back to marker-based lookup on the next Gather.
"""
# 1. Unwrap the MCP envelope.
content = getattr(result, "content", None)
if content is None and isinstance(result, dict):
content = result.get("content")
if not content:
return None
# 2. Take the first text block.
first = content[0]
text = getattr(first, "text", None)
if text is None and isinstance(first, dict):
text = first.get("text")
if not text:
return None
# 3. Try JSON first.
try:
data = json.loads(text or "{}")
except json.JSONDecodeError:
# 4. Not JSON — assume the server returned a bare ID.
return text.strip() or None
# 5. Common shapes: {"id": ...} or {"comment": {"id": ...}}.
if isinstance(data, dict):
if "id" in data:
return str(data["id"])
if "comment" in data and isinstance(data["comment"], dict):
return str(data["comment"].get("id")) or None
# YouTrack often uses "idReadable" for the human-facing ID.
for key in ("idReadable", "commentId", "comment_id"):
if key in data:
return str(data[key])
return None