fix(planka): handle nested items in API response and add safety checks

This commit is contained in:
Flash
2026-04-03 20:09:16 +00:00
parent 6176d6a636
commit 3d8bd23f2d

View File

@@ -16,6 +16,8 @@ class PlankaClient:
def __init__(self, url: str, token: str): def __init__(self, url: str, token: str):
self.url = url.rstrip("/") self.url = url.rstrip("/")
self.token = token self.token = token
# Planka uses standard JWT tokens, but they don't always require "Bearer " in the header
# for all internal API calls, though standard is to use it.
self.headers = {"Authorization": f"Bearer {token}"} self.headers = {"Authorization": f"Bearer {token}"}
def _get(self, endpoint: str, params: Optional[dict] = None) -> Any: def _get(self, endpoint: str, params: Optional[dict] = None) -> Any:
@@ -29,13 +31,23 @@ class PlankaClient:
return response.json() return response.json()
def get_projects(self) -> List[dict]: def get_projects(self) -> List[dict]:
return self._get("projects") data = self._get("projects")
# Handle Planka API response structure which often wraps items in 'items'
if isinstance(data, dict) and "items" in data:
return data["items"]
return data
def get_boards(self, project_id: str) -> List[dict]: def get_boards(self, project_id: str) -> List[dict]:
return self._get(f"projects/{project_id}/boards") data = self._get(f"projects/{project_id}/boards")
if isinstance(data, dict) and "items" in data:
return data["items"]
return data
def get_cards(self, board_id: str) -> List[dict]: def get_cards(self, board_id: str) -> List[dict]:
return self._get(f"boards/{board_id}/cards") data = self._get(f"boards/{board_id}/cards")
if isinstance(data, dict) and "items" in data:
return data["items"]
return data
def create_card(self, board_id: str, list_id: str, name: str, description: str = "") -> dict: def create_card(self, board_id: str, list_id: str, name: str, description: str = "") -> dict:
return self._post("cards", { return self._post("cards", {
@@ -60,18 +72,24 @@ def get_client():
def list_projects() -> str: def list_projects() -> str:
"""List all Planka projects available to the user.""" """List all Planka projects available to the user."""
projects = get_client().get_projects() projects = get_client().get_projects()
if not projects:
return "No projects found."
return "\n".join([f"- {p['name']} (ID: {p['id']})" for p in projects]) return "\n".join([f"- {p['name']} (ID: {p['id']})" for p in projects])
@mcp.tool() @mcp.tool()
def list_boards(project_id: str) -> str: def list_boards(project_id: str) -> str:
"""List all boards for a given project ID.""" """List all boards for a given project ID."""
boards = get_client().get_boards(project_id) boards = get_client().get_boards(project_id)
if not boards:
return "No boards found."
return "\n".join([f"- {b['name']} (ID: {b['id']})" for b in boards]) return "\n".join([f"- {b['name']} (ID: {b['id']})" for b in boards])
@mcp.tool() @mcp.tool()
def list_cards(board_id: str) -> str: def list_cards(board_id: str) -> str:
"""List all cards for a given board ID.""" """List all cards for a given board ID."""
cards = get_client().get_cards(board_id) cards = get_client().get_cards(board_id)
if not cards:
return "No cards found."
return "\n".join([f"- {c['name']} (ID: {c['id']})" for c in cards]) return "\n".join([f"- {c['name']} (ID: {c['id']})" for c in cards])
@mcp.tool() @mcp.tool()