en un clic
c8y-mcp-server
Critical implementation patterns for building Cumulocity microservices that act as MCP (Model Context Protocol) servers. Use when developing MCP servers.
Menu
Critical implementation patterns for building Cumulocity microservices that act as MCP (Model Context Protocol) servers. Use when developing MCP servers.
REST API documentation and guidance for interacting with the Cumulocity AI service. Use when sending messages to the AI agent.
Multi-scope expert for Cumulocity IoT. Accesses live OpenAPI specs for Core API and Digital Twin Manager (DTM).
Setup the local testing environment for Cumulocity applications to test UI and microservices.
Guidelines for developing microfrontends, plugins, and blueprints in Cumulocity, including manifest configuration and package structure. Use when working with microfrontends.
Guidelines for what needs to be ensured if you want to create a new open source repository in the Cumulocity organization.
Standard practices for developing, testing, and building Python microservices in the Cumulocity ecosystem. Use when creating or refactoring Python microservices.
| name | c8y-mcp-server |
| description | Critical implementation patterns for building Cumulocity microservices that act as MCP (Model Context Protocol) servers. Use when developing MCP servers. |
When implementing an MCP (Model Context Protocol) server as a Cumulocity microservice using Python and FastAPI, follow these critical patterns to ensure compatibility with the platform's proxy and security layers.
Use FastMCP for a high-level API. Be cautious with initialization arguments; stick to the server name unless you've verified support for other parameters in the current version.
mcp_server = FastMCP("my-mcp-server")
The standard SseServerTransport enforces a leading slash on the message endpoint, which breaks when served under a Cumulocity context path (e.g., /service/my-microservice/).
Subclass SseServerTransport to return truly relative paths:
class CustomSseServerTransport(SseServerTransport):
def __init__(self, endpoint: str):
super().__init__(endpoint)
self._endpoint = endpoint # Relative path (e.g., "messages/")
@asynccontextmanager
async def connect_sse(self, scope, receive, send):
# Implementation should return self._endpoint directly in the 'endpoint' event
# to ensure the client POSTs back to the correct relative URL.
...
Cumulocity's proxy can be sensitive to trailing slash redirects (307). Explicitly register routes for both trailing and non-trailing slash paths:
app.add_route("/sse", SseHandler(), methods=["GET"])
app.add_route("/sse/", SseHandler(), methods=["GET"])
app.add_route("/sse/messages", MessagesHandler(), methods=["POST"])
app.add_route("/sse/messages/", MessagesHandler(), methods=["POST"])
Always add ProxyHeadersMiddleware to correctly handle X-Forwarded-* headers.
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
app.add_middleware(ProxyHeadersMiddleware, trusted_hosts="*")
Tool execution often occurs in a different task/context than the initial request.
Use contextvars with robust fallbacks to environment variables:
# context.py
base_url: ContextVar[Optional[str]] = ContextVar(
"base_url",
default=os.environ.get("C8Y_BASEURL")
)
# dtm_client.py fallback pattern
if not auth and os.environ.get("C8Y_USER"):
# Build Basic Auth header from env for PER_TENANT isolation
...
For maximum control over SSE lifecycles, implement handlers as ASGI classes rather than simple FastAPI decorators. This avoids issues with Request object serialization and send callable access.
class SseHandler:
async def __call__(self, scope, receive, send):
async with sse.connect_sse(scope, receive, send) as (read_stream, write_stream):
await mcp_server._mcp_server.run(read_stream, write_stream, ...)