How to create Streamable HTTP server? #549
Replies: 15 comments
-
Looks like something we could add in the README. In the meanwhile, please take a look at the sample applications in the repo: |
Beta Was this translation helpful? Give feedback.
-
That's actually what caused me to raise this issue: the example relies on A minimal implementation should not have to be concerned with the routing; it should be able to take any HTTP endpoint (where the only guaranteed is that I took a look at TS SDK and it seems to have an example on this. I think C# SDK should follow that as well. |
Beta Was this translation helpful? Give feedback.
-
SSE required two different route patterns ("/sse" and "/message"), so providing a single
It's already in the ModelContextProtocol.AspNetCore README, but not the main one. If we merge #428, the main README will at least cross-reference the AspNetCore README, but it might be a good idea to include an AspNetCore sample right in the main README with a callout that you need the additional AspNetCore package.
The The This is at a slightly lower layer than the TS SDK since netstandard does not provide an HttpContext abstraction to handle things like headers, but fortunately, it doesn't take much code to handle the header stuff. You can look at the
I looked at the TS SDK as well, and I see the fact that it doesn't handle routing for you as a weakness not a strength of that API. We at least attempt to account for this in MapMcp with our IdleTrackingBackgroundService rather than leave that completely as an exercise to the reader. This isn't a problem for either the csharp-sdk or TS sdk in stateless mode where the transport does not outlive the request, but I think the way we do the stateful version at least is far more developer friendly. If you really, really want a RequestDelegate, here's the sample code: var app = builder.Build();
app.MapMcp();
var endpoints = ((IEndpointRouteBuilder)app).DataSources.SelectMany(d => d.Endpoints);
var getDelegate = endpoints.Single(e => e.DisplayName == "MCP Streamable HTTP | HTTP: GET /").RequestDelegate!;
var postDelegate = endpoints.Single(e => e.DisplayName == "MCP Streamable HTTP | HTTP: POST /").RequestDelegate!;
var deleteDelegate = endpoints.Single(e => e.DisplayName == "MCP Streamable HTTP | HTTP: DELETE /").RequestDelegate!;
Task MyMcpRequestDelegate(HttpContext context)
{
if (string.Equals(context.Request.Method, HttpMethods.Get, StringComparison.OrdinalIgnoreCase))
{
return getDelegate(context);
}
else if (string.Equals(context.Request.Method, HttpMethods.Post, StringComparison.OrdinalIgnoreCase))
{
return postDelegate(context);
}
else if (string.Equals(context.Request.Method, HttpMethods.Delete, StringComparison.OrdinalIgnoreCase))
{
return deleteDelegate(context);
}
context.Response.StatusCode = StatusCodes.Status405MethodNotAllowed;
return Task.CompletedTask;
}
app.Map("/mymcp", MyMcpRequestDelegate);
app.Run(); You'd be right to point out that the We could consider an API to make it easier to get this MyMcpRequestDelegate. It would have to come out of DI somehow, because the generated RequestDelegate would have to rely on singleton services like the IdleTrackingBackgroundService which we'd ideally avoid re-retrieving every request. I'm not convinced we need it though. In the above example, you could have just written the following instead. var app = builder.Build();
app.MapMcp("/mymcp");
app.Run(); Not everything will be that simple, but you might be surprised how flexible MapMcp can be already if paired with things like middleware and/or filters. Maybe if you share what you were trying to do with
|
Beta Was this translation helpful? Give feedback.
-
I think the whole disucssion is making the assumption that ASP.NET Core has to be used just to set up an HTTP MCP server. Currently if I want to set up an MCP server on C#, besides |
Beta Was this translation helpful? Give feedback.
-
Adding the RequestDelegate sample code to my HttpSSE server turned it into a Streamable Http server that can be used in Copilot Studio and it still works as a HttpSSE server in our other clients. |
Beta Was this translation helpful? Give feedback.
-
I'm surprised that the "/mymcp" endpoint in my sample works any differently than the "/" endpoint. MapMcp already supports both Streamable HTTP and SSE in a single call, just at different endpoints, "/" and "/sse" respectively.
@Agazoth Is it possible you we're trying the wrong endpoint with Copilot Studio? Maybe you were trying "/sse" instead. If you were trying the "/" endpoint and it wasn't working, do you know what was different about the "/mymcp" endpoint that made it work? |
Beta Was this translation helpful? Give feedback.
-
@halter73 you are right! I removed the delegation code and connected to the endpoint without anything in basepath (/). Streaming Http works as expected now. I think I got sidetracked because custom connectors take their sweet time to connect to the endpoint. Thanks for clearing this up. |
Beta Was this translation helpful? Give feedback.
-
@Agazoth, can you share the working sample that you used for MCS (Copilot Studio) and the swagger? I am attempting this and I am a little lost. I was assuming the code below:
With the swagger (for the Custom Connector):
The response I get when I test is:
What am I missing? [other than knowledge and experience] |
Beta Was this translation helpful? Give feedback.
-
I haven't personally tested integration with Copilot Studio, but it appears it's treating the MCP endpoint as a generic REST endpoint which makes sense given the swagger. Based on the docuemtnation, your swagger should probably look more like this: swagger: '2.0'
info:
title: MCP
description: Provides the ability to ******
version: '1.0'
host: g9******b.usw3.devtunnels.ms:8000
basePath: /
schemes:
- https
paths:
/:
post:
summary: Voice Agent Echo
x-ms-agentic-protocol: mcp-streamable-1.0
operationId: echo |
Beta Was this translation helpful? Give feedback.
-
@ksaye MCP your code is good. I tested it in both vs code and inspector. As @halter73 points out, the issue must be with your swagger configuration. I have tested the following swagger in Power Apps Custom Connections and can connect to the the Echo mcp with it: swagger: '2.0'
info:
title: MCP Server
description: >-
This MCP Server will work with Streamable HTTP and is meant to work with
Microsoft Copilot Studio
version: 1.0.0
host: h4dwtime-666s.euw.devtunnels.ms
basePath: /
schemes:
- https
consumes: []
produces: []
paths:
/:
post:
summary: MCP Server Streamable HTTP
x-ms-agentic-protocol: mcp-streamable-1.0
operationId: InvokeServer
responses: {}
definitions: {}
parameters: {}
responses: {}
tags: []
securityDefinitions: {} I use the MCP-Streamable-HTTP template and removed the security parameter (you do not use it in your code). All you need is to tell the connection where to find the MCP server. The port number is not required either. Post is what differentiates Streaming-HTTP from SSE - SSE uses Get. In Inspector you can also run your server code with the SSE transport type, you just have to append sse to your uri. The really cool thing in the MCP Server/Client setup is, that the server advertises its tools to the client, so devs don't have to specify the tools both in their code and in the configuration for the agents consuming the server. |
Beta Was this translation helpful? Give feedback.
-
Thanks both @Agazoth and @halter73 @Agazoth, thanks for the pointers and how did you test in Power Apps? I see the InvokeServer and see the error coming back, but how did you send a message and get a successful response? I am throwing errors: |
Beta Was this translation helpful? Give feedback.
-
Never got the test in the custom connector running, but if you test in swagger you get a less angry error (cors something ...). And then you can test successfully in Agent -> Tools |
Beta Was this translation helpful? Give feedback.
-
That was my next step (Copilot Studio). Using the Swagger:
I cannot enumerate or use the tools, as shown below. Are you able to see them and are you sure it is not using SSE? [on that topic, how can I enable SSE with the super simple code above]? |
Beta Was this translation helpful? Give feedback.
-
When executing from the custom connection swagger editor in Power Apps it looks like this: Then, when setting the tool up on an Agent it looks like this: It takes some time to refresh the first time, but the tool is available. Inspector is a great tool to test the server before proceeding to the Power universe. You can find it here: https://www.npmjs.com/package/@modelcontextprotocol/inspector All you need is node.js and then you can run It spins up a MCP Client that lets you test individual tools in a MCP Server: And you get a nice log in your terminal: |
Beta Was this translation helpful? Give feedback.
-
2 small updates.
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
All examples are based on STDIO transport. There are no examples that demonstrates how to expose an HTTP server with an endpoint serving an MCP server.
Beta Was this translation helpful? Give feedback.
All reactions