Symlinks dont resolve on remote VM during Docker build context. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
95 lines
3.0 KiB
Python
95 lines
3.0 KiB
Python
"""Composite MCP server: proxies mcp-atlassian + adds section tools.
|
|
|
|
Spawns mcp-atlassian as a subprocess (stdio), proxies all its tools, and
|
|
registers the confluence_section_* tools from this package. Claude Code
|
|
sees a single MCP server with all tools under one prefix.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import json
|
|
import logging
|
|
import os
|
|
import sys
|
|
|
|
from mcp.server.fastmcp import FastMCP
|
|
from mcp.client.session import ClientSession
|
|
from mcp.client.stdio import StdioServerParameters, stdio_client
|
|
|
|
from confluence_collab.server import (
|
|
confluence_section_list,
|
|
confluence_section_get,
|
|
confluence_section_update,
|
|
confluence_section_append,
|
|
confluence_section_delete,
|
|
)
|
|
|
|
logger = logging.getLogger("confluence-collab-proxy")
|
|
|
|
mcp = FastMCP("atlassian-with-sections")
|
|
|
|
# Register section tools directly (they're already decorated with @mcp.tool in server.py,
|
|
# but we need to re-register them on this new FastMCP instance)
|
|
mcp.tool()(confluence_section_list)
|
|
mcp.tool()(confluence_section_get)
|
|
mcp.tool()(confluence_section_update)
|
|
mcp.tool()(confluence_section_append)
|
|
mcp.tool()(confluence_section_delete)
|
|
|
|
|
|
async def _proxy_upstream_tools() -> None:
|
|
"""Connect to mcp-atlassian subprocess and proxy its tools."""
|
|
cmd = "uvx"
|
|
args = ["--python", "3.13", "mcp-atlassian"]
|
|
|
|
server_params = StdioServerParameters(command=cmd, args=args, env=dict(os.environ))
|
|
|
|
async with stdio_client(server_params) as (read, write):
|
|
async with ClientSession(read, write) as session:
|
|
await session.initialize()
|
|
|
|
# List upstream tools
|
|
tools_result = await session.list_tools()
|
|
logger.info("Proxying %d upstream tools from mcp-atlassian", len(tools_result.tools))
|
|
|
|
# Register each upstream tool as a proxy on our server
|
|
for tool in tools_result.tools:
|
|
_register_proxy_tool(tool, session)
|
|
|
|
# Keep running until interrupted
|
|
await asyncio.Event().wait()
|
|
|
|
|
|
def _register_proxy_tool(tool, session: ClientSession) -> None:
|
|
"""Register a proxied tool from the upstream MCP server."""
|
|
|
|
async def proxy_handler(**kwargs):
|
|
result = await session.call_tool(tool.name, kwargs)
|
|
# Return concatenated text content
|
|
texts = []
|
|
for content in result.content:
|
|
if hasattr(content, "text"):
|
|
texts.append(content.text)
|
|
return "\n".join(texts) if texts else ""
|
|
|
|
proxy_handler.__name__ = tool.name
|
|
proxy_handler.__doc__ = tool.description or ""
|
|
|
|
# Build parameter annotations from tool schema
|
|
mcp.tool(name=tool.name, description=tool.description or "")(proxy_handler)
|
|
|
|
|
|
def main():
|
|
"""Run the composite MCP server.
|
|
|
|
Note: The proxy approach requires running the upstream mcp-atlassian
|
|
as a subprocess. For simpler deployment, use the standalone server.py
|
|
which only provides section tools, and keep mcp-atlassian separate.
|
|
"""
|
|
mcp.run(transport="stdio")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|