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!