Skip to content

Fixing the Microsoft Copilot- Remote MCP Server Integration!

Fixing Microsoft Copilot Agent Integration with Remote MCP Server

As the Model Context Protocol (MCP) gains momentum in the agentic ecosystem, many developers are building tools and services around it. Cloud providers are also racing to add connectivity support into their platforms—sometimes natively, often in beta, and occasionally requiring workarounds.

Microsoft Copilot Studio’s Agent Extend Action via MCP allows developers to plug in powerful remote tools.

However, integrating a remote MCP server with Copilot Studio doesn’t work out-of-the-box with the official Python SDK (modelcontextprotocol/python-sdk).

This blog highlights two critical issues and their corresponding fixes to make your integration work smoothly.


🛑 Known Issues

The default SseServerTransport class in the MCP Python SDK contains two core issues that break compatibility with Copilot Studio:

1. Incorrect Content-Type in SSE Response

Microsoft Copilot expects the following header:

Content-Type: text/event-stream

However, the SDK appends charset=utf-8, resulting in:

Content-Type: text/event-stream; charset=utf-8

This causes Copilot to fail parsing the SSE stream.


2. Relative Path in endpoint Event

The SDK sends a relative URI in the first SSE endpoint event like so:

{ "event": "endpoint", "data": "/messages/?session_id=..." }

But Copilot requires an absolute URL with protocol and domain:

{ "event": "endpoint", "data": "https://<your-domain>/messages/?session_id=..." }

✅ The Fix: Extend the Transport Class

We address both problems by subclassing SseServerTransport into a custom CoreSseServerTransport:

class CoreSseServerTransport(SseServerTransport):

    @asynccontextmanager
    async def connect_sse(self, scope: Scope, receive: Receive, send: Send):
        ...
        session_uri = f"{scope.get('x-message-path', quote(self._endpoint))}?session_id={session_id.hex}"
        ...
        response = EventSourceResponse(
            content=sse_stream_reader,
            data_sender_callable=sse_writer
        )

        headers = scope.get("x-headers", {})
        for key, value in headers.items():
            response.headers[key] = value

        ...
  • ✅ The x-message-path provides a fully qualified URL to satisfy the endpoint event requirement.
  • ✅ The x-headers inject the correct Content-Type header without the charset.

🧩 Middleware to Inject Scope Metadata

Use this Starlette middleware to dynamically compute the message path and required headers:

class AzureScopeMiddleware(BaseHTTPMiddleware):

    async def build_action_url(self, request: Request, path: str, **query_params):
        scheme = request.headers.get("x-forwarded-proto", request.url.scheme)
        host = request.headers["host"]
        return urlunparse((
            scheme, host, path, "", urlencode(query_params), ""
        ))

    async def dispatch(self, request: Request, call_next):
        request.scope["x-message-path"] = await self.build_action_url(request, "/messages/")
        request.scope["x-headers"] = {
            "Content-Type": "text/event-stream",
            "Access-Control-Allow-Origin": "*"
        }
        return await call_next(request)

✅ Final Notes

This workaround allows Copilot Studio to connect to your MCP server correctly:

  • 🛠️ Content-Type without charset
  • 🌐 Absolute URL in endpoint event

Once these fixes are adopted in the SDK or official tooling, this blog will serve as a legacy patch. Until then, happy hacking!

Team Cennest!

Leave a Reply

Your email address will not be published. Required fields are marked *