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!