Files
dss/tools/dss_mcp/integrations/confluence.py
Digital Production Factory 276ed71f31 Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm

Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)

Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability

Migration completed: $(date)
🤖 Clean migration with full functionality preserved
2025-12-09 18:45:48 -03:00

263 lines
8.0 KiB
Python

"""
Confluence Integration for MCP
Provides Confluence API tools for documentation and knowledge base.
"""
from typing import Dict, Any, List, Optional
from atlassian import Confluence
from mcp import types
from .base import BaseIntegration
# Confluence MCP Tool Definitions
CONFLUENCE_TOOLS = [
types.Tool(
name="confluence_create_page",
description="Create a new Confluence page",
inputSchema={
"type": "object",
"properties": {
"space_key": {
"type": "string",
"description": "Confluence space key"
},
"title": {
"type": "string",
"description": "Page title"
},
"body": {
"type": "string",
"description": "Page content (HTML or wiki markup)"
},
"parent_id": {
"type": "string",
"description": "Optional parent page ID"
}
},
"required": ["space_key", "title", "body"]
}
),
types.Tool(
name="confluence_get_page",
description="Get Confluence page by ID or title",
inputSchema={
"type": "object",
"properties": {
"page_id": {
"type": "string",
"description": "Page ID (use this OR title)"
},
"space_key": {
"type": "string",
"description": "Space key (required if using title)"
},
"title": {
"type": "string",
"description": "Page title (use this OR page_id)"
},
"expand": {
"type": "string",
"description": "Comma-separated list of expansions (body.storage, version, etc.)",
"default": "body.storage,version"
}
}
}
),
types.Tool(
name="confluence_update_page",
description="Update an existing Confluence page",
inputSchema={
"type": "object",
"properties": {
"page_id": {
"type": "string",
"description": "Page ID to update"
},
"title": {
"type": "string",
"description": "New page title"
},
"body": {
"type": "string",
"description": "New page content"
}
},
"required": ["page_id", "title", "body"]
}
),
types.Tool(
name="confluence_search",
description="Search Confluence pages using CQL",
inputSchema={
"type": "object",
"properties": {
"cql": {
"type": "string",
"description": "CQL query (e.g., 'space=DSS AND type=page')"
},
"limit": {
"type": "integer",
"description": "Maximum number of results",
"default": 25
}
},
"required": ["cql"]
}
),
types.Tool(
name="confluence_get_space",
description="Get Confluence space details",
inputSchema={
"type": "object",
"properties": {
"space_key": {
"type": "string",
"description": "Space key"
}
},
"required": ["space_key"]
}
)
]
class ConfluenceIntegration(BaseIntegration):
"""Confluence API integration with circuit breaker"""
def __init__(self, config: Dict[str, Any]):
"""
Initialize Confluence integration.
Args:
config: Must contain 'url', 'username', 'api_token'
"""
super().__init__("confluence", config)
url = config.get("url")
username = config.get("username")
api_token = config.get("api_token")
if not all([url, username, api_token]):
raise ValueError("Confluence configuration incomplete: url, username, api_token required")
self.confluence = Confluence(
url=url,
username=username,
password=api_token,
cloud=True
)
async def create_page(
self,
space_key: str,
title: str,
body: str,
parent_id: Optional[str] = None
) -> Dict[str, Any]:
"""Create a new page"""
def _create():
return self.confluence.create_page(
space=space_key,
title=title,
body=body,
parent_id=parent_id,
representation="storage"
)
return await self.call_api(_create)
async def get_page(
self,
page_id: Optional[str] = None,
space_key: Optional[str] = None,
title: Optional[str] = None,
expand: str = "body.storage,version"
) -> Dict[str, Any]:
"""Get page by ID or title"""
def _get():
if page_id:
return self.confluence.get_page_by_id(
page_id=page_id,
expand=expand
)
elif space_key and title:
return self.confluence.get_page_by_title(
space=space_key,
title=title,
expand=expand
)
else:
raise ValueError("Must provide either page_id or (space_key + title)")
return await self.call_api(_get)
async def update_page(
self,
page_id: str,
title: str,
body: str
) -> Dict[str, Any]:
"""Update an existing page"""
def _update():
# Get current version
page = self.confluence.get_page_by_id(page_id, expand="version")
current_version = page["version"]["number"]
return self.confluence.update_page(
page_id=page_id,
title=title,
body=body,
parent_id=None,
type="page",
representation="storage",
minor_edit=False,
version_comment="Updated via DSS MCP",
version_number=current_version + 1
)
return await self.call_api(_update)
async def search(self, cql: str, limit: int = 25) -> Dict[str, Any]:
"""Search pages using CQL"""
def _search():
return self.confluence.cql(cql, limit=limit)
return await self.call_api(_search)
async def get_space(self, space_key: str) -> Dict[str, Any]:
"""Get space details"""
def _get():
return self.confluence.get_space(space_key)
return await self.call_api(_get)
class ConfluenceTools:
"""MCP tool executor for Confluence integration"""
def __init__(self, config: Dict[str, Any]):
self.confluence = ConfluenceIntegration(config)
async def execute_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
"""Execute Confluence tool"""
handlers = {
"confluence_create_page": self.confluence.create_page,
"confluence_get_page": self.confluence.get_page,
"confluence_update_page": self.confluence.update_page,
"confluence_search": self.confluence.search,
"confluence_get_space": self.confluence.get_space
}
handler = handlers.get(tool_name)
if not handler:
return {"error": f"Unknown Confluence tool: {tool_name}"}
try:
clean_args = {k: v for k, v in arguments.items() if not k.startswith("_")}
result = await handler(**clean_args)
return result
except Exception as e:
return {"error": str(e)}