Add basic Planka MCP server with project, board, and card tools
This commit is contained in:
84
server.py
Normal file
84
server.py
Normal file
@@ -0,0 +1,84 @@
|
||||
import os
|
||||
import requests
|
||||
from typing import Any, List, Optional
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
PLANKA_URL = os.getenv("PLANKA_URL", "http://localhost:3000")
|
||||
PLANKA_TOKEN = os.getenv("PLANKA_TOKEN")
|
||||
|
||||
# Initialize FastMCP server
|
||||
mcp = FastMCP("Planka")
|
||||
|
||||
class PlankaClient:
|
||||
def __init__(self, url: str, token: str):
|
||||
self.url = url.rstrip("/")
|
||||
self.token = token
|
||||
self.headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
def _get(self, endpoint: str, params: Optional[dict] = None) -> Any:
|
||||
response = requests.get(f"{self.url}/api/{endpoint}", headers=self.headers, params=params)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def _post(self, endpoint: str, data: dict) -> Any:
|
||||
response = requests.post(f"{self.url}/api/{endpoint}", headers=self.headers, json=data)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def get_projects(self) -> List[dict]:
|
||||
return self._get("projects")
|
||||
|
||||
def get_boards(self, project_id: str) -> List[dict]:
|
||||
return self._get(f"projects/{project_id}/boards")
|
||||
|
||||
def get_cards(self, board_id: str) -> List[dict]:
|
||||
return self._get(f"boards/{board_id}/cards")
|
||||
|
||||
def create_card(self, board_id: str, list_id: str, name: str, description: str = "") -> dict:
|
||||
return self._post("cards", {
|
||||
"boardId": board_id,
|
||||
"listId": list_id,
|
||||
"name": name,
|
||||
"description": description
|
||||
})
|
||||
|
||||
# Initialize client lazily
|
||||
client = None
|
||||
|
||||
def get_client():
|
||||
global client
|
||||
if client is None:
|
||||
if not PLANKA_TOKEN:
|
||||
raise ValueError("PLANKA_TOKEN is required")
|
||||
client = PlankaClient(PLANKA_URL, PLANKA_TOKEN)
|
||||
return client
|
||||
|
||||
@mcp.tool()
|
||||
def list_projects() -> str:
|
||||
"""List all Planka projects available to the user."""
|
||||
projects = get_client().get_projects()
|
||||
return "\n".join([f"- {p['name']} (ID: {p['id']})" for p in projects])
|
||||
|
||||
@mcp.tool()
|
||||
def list_boards(project_id: str) -> str:
|
||||
"""List all boards for a given project ID."""
|
||||
boards = get_client().get_boards(project_id)
|
||||
return "\n".join([f"- {b['name']} (ID: {b['id']})" for b in boards])
|
||||
|
||||
@mcp.tool()
|
||||
def list_cards(board_id: str) -> str:
|
||||
"""List all cards for a given board ID."""
|
||||
cards = get_client().get_cards(board_id)
|
||||
return "\n".join([f"- {c['name']} (ID: {c['id']})" for c in cards])
|
||||
|
||||
@mcp.tool()
|
||||
def create_card(board_id: str, list_id: str, name: str, description: str = "") -> str:
|
||||
"""Create a new card on a Planka board."""
|
||||
card = get_client().create_card(board_id, list_id, name, description)
|
||||
return f"Created card: {card['name']} (ID: {card['id']})"
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
Reference in New Issue
Block a user