Skip to content

Commit df90543

Browse files
committed
updating round-trip to use real back-ends for demo
1 parent d37a972 commit df90543

3 files changed

Lines changed: 84 additions & 18 deletions

File tree

tests/integration/all-resource-types/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ The kitchen sink APIM instance includes **every resource type and API protocol v
1717
| `src-rest-versioned-v1` | REST (versioned) | OpenAPI |
1818
| `src-rest-revisioned` | REST (revisioned) | OpenAPI |
1919
| `src-mcp-from-api` | MCP (from existing API) | None |
20-
| `src-mcp-from-external` | MCP (from external MCP server) | None |
20+
| `src-mcp-existing-server` | MCP (working existing-server demo via Learn) | None |
2121
| `src-a2a-weather-agent` | A2A (JSON-RPC + agent card) | None |
2222

2323
### Backend Variations

tests/integration/all-resource-types/source-apim-post-activation.bicep

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,38 @@ var mcpApiPolicyXml = '''
363363
</policies>
364364
'''
365365

366+
var mcpExistingServerPolicyXml = '''
367+
<policies>
368+
<inbound>
369+
<base />
370+
<set-variable name="requestBody" value="@(context.Request.Body?.As&lt;string&gt;(preserveContent: true) ?? string.Empty)" />
371+
<set-variable name="sessionId" value="@(context.Request.Headers.GetValueOrDefault(&quot;Mcp-Session-Id&quot;, string.Empty))" />
372+
<set-variable name="protocolVersion" value="@(context.Request.Headers.GetValueOrDefault(&quot;MCP-Protocol-Version&quot;, &quot;2025-03-26&quot;))" />
373+
<send-request mode="new" response-variable-name="learnResp" timeout="60" ignore-error="false">
374+
<set-url>https://learn.microsoft.com/api/mcp</set-url>
375+
<set-method>@(context.Request.Method)</set-method>
376+
<set-header name="Accept" exists-action="override">
377+
<value>@(context.Request.Headers.GetValueOrDefault(&quot;Accept&quot;, &quot;text/event-stream&quot;))</value>
378+
</set-header>
379+
<set-header name="Content-Type" exists-action="override">
380+
<value>@(context.Request.Headers.GetValueOrDefault(&quot;Content-Type&quot;, &quot;application/json&quot;))</value>
381+
</set-header>
382+
<set-header name="MCP-Protocol-Version" exists-action="override">
383+
<value>@((string)context.Variables[&quot;protocolVersion&quot;])</value>
384+
</set-header>
385+
<set-header name="Mcp-Session-Id" exists-action="override">
386+
<value>@((string)context.Variables[&quot;sessionId&quot;])</value>
387+
</set-header>
388+
<set-body>@((string)context.Variables[&quot;requestBody&quot;])</set-body>
389+
</send-request>
390+
<return-response response-variable-name="learnResp" />
391+
</inbound>
392+
<backend><base /></backend>
393+
<outbound><base /></outbound>
394+
<on-error><base /></on-error>
395+
</policies>
396+
'''
397+
366398
var productPolicyXml = '''
367399
<policies>
368400
<inbound>
@@ -399,6 +431,11 @@ resource apiMcpFromApi 'Microsoft.ApiManagement/service/apis@2025-09-01-preview'
399431
name: 'src-mcp-from-api'
400432
}
401433

434+
resource apiMcpExistingServer 'Microsoft.ApiManagement/service/apis@2025-09-01-preview' existing = {
435+
parent: apim
436+
name: 'src-mcp-existing-server'
437+
}
438+
402439
resource servicePolicy 'Microsoft.ApiManagement/service/policies@2025-09-01-preview' = {
403440
parent: apim
404441
name: 'policy'
@@ -435,6 +472,15 @@ resource apiMcpFromApiPolicy 'Microsoft.ApiManagement/service/apis/policies@2025
435472
}
436473
}
437474

475+
resource apiMcpExistingServerPolicy 'Microsoft.ApiManagement/service/apis/policies@2025-09-01-preview' = {
476+
parent: apiMcpExistingServer
477+
name: 'policy'
478+
properties: {
479+
format: 'rawxml'
480+
value: mcpExistingServerPolicyXml
481+
}
482+
}
483+
438484
resource policyRestriction 'Microsoft.ApiManagement/service/policyRestrictions@2025-09-01-preview' = if (isClassicSku) {
439485
parent: apim
440486
name: 'src-restriction-ip'

tests/integration/all-resource-types/source-apim.bicep

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -892,35 +892,31 @@ resource apiMcpFromApi 'Microsoft.ApiManagement/service/apis@2025-09-01-preview'
892892
// non-MCP API \u2014 e.g. src-rest-openapi above). ApiOperation BVT coverage
893893
// is therefore provided by the REST APIs in this template, not by the MCP APIs.
894894

895-
// Backend for external MCP server (backendId pattern requires a backend resource)
896-
resource backendMcpExternal 'Microsoft.ApiManagement/service/backends@2025-09-01-preview' = {
895+
resource backendMcpLearn 'Microsoft.ApiManagement/service/backends@2025-09-01-preview' = {
897896
parent: apim
898-
name: 'src-backend-mcp-external'
897+
name: 'src-backend-mcp-learn'
899898
properties: {
900-
description: 'External MCP server backend (demo proxy to the MCP-from-API endpoint)'
901-
url: 'https://${apim.name}.azure-api.net'
899+
description: 'Backend for the public Microsoft Learn MCP server used by the existing-server demo'
900+
url: 'https://learn.microsoft.com'
902901
protocol: 'http'
903902
}
904903
}
905904

906-
// 9. MCP API created from an existing (external) public MCP server
907-
// External MCP uses backendId + mcpProperties; APIM accepts a non-empty path here.
908-
// any() used because backendId and mcpProperties.endpoints are valid at runtime but absent from Bicep type definitions (BCP037/BCP036)
909-
resource apiMcpFromExternal 'Microsoft.ApiManagement/service/apis@2025-09-01-preview' = {
905+
resource apiMcpExistingServer 'Microsoft.ApiManagement/service/apis@2025-09-01-preview' = {
910906
parent: apim
911-
name: 'src-mcp-from-external'
907+
name: 'src-mcp-existing-server'
912908
properties: any({
913-
displayName: 'KS MCP from External Server'
914-
description: 'MCP server that proxies another MCP endpoint via APIM backend routing'
915-
path: 'ks/mcp-external'
909+
displayName: 'KS MCP Existing Server Demo'
910+
description: 'Working demo that exposes the public Microsoft Learn MCP server through APIM using a policy-based MCP proxy'
911+
path: 'ks/mcp-existing'
916912
protocols: ['https']
917913
subscriptionRequired: false
918914
type: 'mcp'
919-
backendId: backendMcpExternal.name
915+
backendId: backendMcpLearn.name
920916
mcpProperties: {
921917
endpoints: {
922918
mcp: {
923-
uriTemplate: '/ks/mcp-from-api/mcp'
919+
uriTemplate: '/mcp'
924920
}
925921
}
926922
}
@@ -969,7 +965,7 @@ resource apiA2aRuntimeCardPolicy 'Microsoft.ApiManagement/service/apis/operation
969965
name: 'policy'
970966
properties: {
971967
format: 'rawxml'
972-
value: '<policies><inbound><base /><return-response><set-status code="200" reason="OK" /><set-header name="Content-Type" exists-action="override"><value>application/json</value></set-header><set-body>{"name":"KS A2A Weather Agent","description":"Demo agent card served by APIM","url":"https://${apim.name}.azure-api.net/ks/a2a-weather"}</set-body></return-response></inbound><backend><base /></backend><outbound><base /></outbound><on-error><base /></on-error></policies>'
968+
value: '<policies><inbound><base /><return-response><set-status code="200" reason="OK" /><set-header name="Content-Type" exists-action="override"><value>application/json</value></set-header><set-body>{"protocolVersion":"0.3.0","name":"KS A2A Weather Agent","description":"Demo A2A weather agent served entirely by APIM policies","url":"https://${apim.name}.azure-api.net/ks/a2a-weather","preferredTransport":"JSONRPC","version":"1.0.0","capabilities":{"streaming":false,"pushNotifications":false,"stateTransitionHistory":false},"defaultInputModes":["text/plain"],"defaultOutputModes":["text/plain"],"skills":[{"id":"get_weather","name":"Get weather","description":"Returns current weather conditions for a city","tags":["weather","demo"],"examples":["What is the weather in Seattle?","weather in Paris"],"inputModes":["text/plain"],"outputModes":["text/plain"]}]}</set-body></return-response></inbound><backend><base /></backend><outbound><base /></outbound><on-error><base /></on-error></policies>'
973969
}
974970
}
975971

@@ -1006,7 +1002,31 @@ resource apiA2aRuntimeJsonRpcPolicy 'Microsoft.ApiManagement/service/apis/operat
10061002
name: 'policy'
10071003
properties: {
10081004
format: 'rawxml'
1009-
value: '<policies><inbound><base /><return-response><set-status code="200" reason="OK" /><set-header name="Content-Type" exists-action="override"><value>application/json</value></set-header><set-body>{"jsonrpc":"2.0","id":"demo","result":{"message":"A2A runtime reachable","source":"apim-a2a-mock"}}</set-body></return-response></inbound><backend><base /></backend><outbound><base /></outbound><on-error><base /></on-error></policies>'
1005+
value: '''<policies><inbound><base /><return-response><set-status code="200" reason="OK" /><set-header name="Content-Type" exists-action="override"><value>application/json</value></set-header><set-body>@{
1006+
var reqBody = context.Request.Body.As<JObject>(preserveContent: true);
1007+
var idToken = reqBody["id"];
1008+
string idJson = idToken != null ? idToken.ToString(Newtonsoft.Json.Formatting.None) : "1";
1009+
string method = (string)reqBody["method"] ?? "";
1010+
if (method != "message/send") {
1011+
return "{\"jsonrpc\":\"2.0\",\"id\":" + idJson + ",\"error\":{\"code\":-32601,\"message\":\"Method not found: " + method + "\"}}";
1012+
}
1013+
var parts = reqBody.SelectToken("params.message.parts") as JArray;
1014+
string text = "";
1015+
if (parts != null) {
1016+
foreach (var p in parts) { if ((string)p["kind"] == "text") { text = (string)p["text"] ?? ""; break; } }
1017+
}
1018+
string city = "your location";
1019+
int idx = text.ToLowerInvariant().IndexOf(" in ");
1020+
if (idx >= 0) { city = text.Substring(idx + 4).Trim().TrimEnd(new[] { '?', '.', '!', ',' }); }
1021+
string reply = "Weather in " + city + ": 62°F, partly cloudy (demo response from APIM A2A policy).";
1022+
string replyJson = Newtonsoft.Json.JsonConvert.SerializeObject(reply);
1023+
string taskId = System.Guid.NewGuid().ToString();
1024+
string contextId = System.Guid.NewGuid().ToString();
1025+
string artifactId = System.Guid.NewGuid().ToString();
1026+
string msgId = System.Guid.NewGuid().ToString();
1027+
string ts = System.DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
1028+
return "{\"jsonrpc\":\"2.0\",\"id\":" + idJson + ",\"result\":{\"kind\":\"task\",\"id\":\"" + taskId + "\",\"contextId\":\"" + contextId + "\",\"status\":{\"state\":\"completed\",\"timestamp\":\"" + ts + "\"},\"artifacts\":[{\"artifactId\":\"" + artifactId + "\",\"name\":\"weather-reply\",\"parts\":[{\"kind\":\"text\",\"text\":" + replyJson + "}]}],\"history\":[{\"kind\":\"message\",\"role\":\"agent\",\"messageId\":\"" + msgId + "\",\"parts\":[{\"kind\":\"text\",\"text\":" + replyJson + "}]}]}}";
1029+
}</set-body></return-response></inbound><backend><base /></backend><outbound><base /></outbound><on-error><base /></on-error></policies>'''
10101030
}
10111031
}
10121032

0 commit comments

Comments
 (0)