@@ -262,6 +262,331 @@ var mcpApiPolicyXml = '''
262262</policies>
263263'''
264264
265+ var mcpTodosPolicyXml = '''
266+ <policies>
267+ <inbound>
268+ <base />
269+ <set-variable name="rpcMethod" value="@((context.Request.Body?.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true)?["method"]?.ToString()) ?? string.Empty)" />
270+ <set-variable name="rpcId" value="@((context.Request.Body?.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true)?["id"]?.ToString()) ?? "1")" />
271+ <set-variable name="toolName" value="@((context.Request.Body?.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true)?.SelectToken("params.name")?.ToString()) ?? string.Empty)" />
272+ <set-variable name="toolId" value="@((context.Request.Body?.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true)?.SelectToken("params.arguments.id")?.ToString()) ?? "1")" />
273+ <set-variable name="createTodoText" value="@((context.Request.Body?.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true)?.SelectToken("params.arguments.text")?.ToString()) ?? "Prepare APIOps demo")" />
274+ <set-variable name="createCompleted" value="@((context.Request.Body?.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true)?.SelectToken("params.arguments.completed")?.ToString()) ?? "false")" />
275+ <choose>
276+ <when condition="@((string)context.Variables["rpcMethod"] == "initialize")">
277+ <return-response>
278+ <set-status code="200" reason="OK" />
279+ <set-header name="Content-Type" exists-action="override">
280+ <value>application/json</value>
281+ </set-header>
282+ <set-header name="MCP-Protocol-Version" exists-action="override">
283+ <value>2025-03-26</value>
284+ </set-header>
285+ <set-body>@{
286+ var req = context.Request.Body?.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true) ?? new Newtonsoft.Json.Linq.JObject();
287+ var idRaw = req["id"]?.ToString() ?? "1";
288+ long idLong;
289+ Newtonsoft.Json.Linq.JToken idToken = long.TryParse(idRaw, out idLong) ? (Newtonsoft.Json.Linq.JToken)new Newtonsoft.Json.Linq.JValue(idLong) : new Newtonsoft.Json.Linq.JValue(idRaw);
290+ var response = new Newtonsoft.Json.Linq.JObject
291+ {
292+ ["jsonrpc"] = "2.0",
293+ ["id"] = idToken,
294+ ["result"] = new Newtonsoft.Json.Linq.JObject
295+ {
296+ ["protocolVersion"] = "2025-03-26",
297+ ["serverInfo"] = new Newtonsoft.Json.Linq.JObject
298+ {
299+ ["name"] = "apim-mcp-demo",
300+ ["version"] = "1.1.0"
301+ },
302+ ["capabilities"] = new Newtonsoft.Json.Linq.JObject
303+ {
304+ ["tools"] = new Newtonsoft.Json.Linq.JObject()
305+ }
306+ }
307+ };
308+ return response.ToString(Newtonsoft.Json.Formatting.None);
309+ }</set-body>
310+ </return-response>
311+ </when>
312+ <when condition="@((string)context.Variables["rpcMethod"] == "tools/list")">
313+ <return-response>
314+ <set-status code="200" reason="OK" />
315+ <set-header name="Content-Type" exists-action="override">
316+ <value>application/json</value>
317+ </set-header>
318+ <set-body>@{
319+ var req = context.Request.Body?.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true) ?? new Newtonsoft.Json.Linq.JObject();
320+ var idRaw = req["id"]?.ToString() ?? "1";
321+ long idLong;
322+ Newtonsoft.Json.Linq.JToken idToken = long.TryParse(idRaw, out idLong) ? (Newtonsoft.Json.Linq.JToken)new Newtonsoft.Json.Linq.JValue(idLong) : new Newtonsoft.Json.Linq.JValue(idRaw);
323+ var tools = new Newtonsoft.Json.Linq.JArray
324+ {
325+ new Newtonsoft.Json.Linq.JObject
326+ {
327+ ["name"] = "healthCheck",
328+ ["description"] = "Return APIM MCP server health",
329+ ["inputSchema"] = new Newtonsoft.Json.Linq.JObject
330+ {
331+ ["type"] = "object",
332+ ["properties"] = new Newtonsoft.Json.Linq.JObject()
333+ }
334+ },
335+ new Newtonsoft.Json.Linq.JObject
336+ {
337+ ["name"] = "listItems",
338+ ["description"] = "List todo items from DummyJSON",
339+ ["inputSchema"] = new Newtonsoft.Json.Linq.JObject
340+ {
341+ ["type"] = "object",
342+ ["properties"] = new Newtonsoft.Json.Linq.JObject()
343+ }
344+ },
345+ new Newtonsoft.Json.Linq.JObject
346+ {
347+ ["name"] = "getItem",
348+ ["description"] = "Get a todo item by id from DummyJSON",
349+ ["inputSchema"] = new Newtonsoft.Json.Linq.JObject
350+ {
351+ ["type"] = "object",
352+ ["properties"] = new Newtonsoft.Json.Linq.JObject
353+ {
354+ ["id"] = new Newtonsoft.Json.Linq.JObject
355+ {
356+ ["type"] = "integer"
357+ }
358+ },
359+ ["required"] = new Newtonsoft.Json.Linq.JArray { "id" }
360+ }
361+ },
362+ new Newtonsoft.Json.Linq.JObject
363+ {
364+ ["name"] = "createItem",
365+ ["description"] = "Create a todo item in DummyJSON",
366+ ["inputSchema"] = new Newtonsoft.Json.Linq.JObject
367+ {
368+ ["type"] = "object",
369+ ["properties"] = new Newtonsoft.Json.Linq.JObject
370+ {
371+ ["text"] = new Newtonsoft.Json.Linq.JObject { ["type"] = "string" },
372+ ["completed"] = new Newtonsoft.Json.Linq.JObject { ["type"] = "boolean" }
373+ },
374+ ["required"] = new Newtonsoft.Json.Linq.JArray { "text" }
375+ }
376+ }
377+ };
378+
379+ var response = new Newtonsoft.Json.Linq.JObject
380+ {
381+ ["jsonrpc"] = "2.0",
382+ ["id"] = idToken,
383+ ["result"] = new Newtonsoft.Json.Linq.JObject { ["tools"] = tools }
384+ };
385+ return response.ToString(Newtonsoft.Json.Formatting.None);
386+ }</set-body>
387+ </return-response>
388+ </when>
389+ <when condition="@((string)context.Variables["rpcMethod"] == "tools/call" && (string)context.Variables["toolName"] == "healthCheck")">
390+ <return-response>
391+ <set-status code="200" reason="OK" />
392+ <set-header name="Content-Type" exists-action="override">
393+ <value>application/json</value>
394+ </set-header>
395+ <set-body>@{
396+ var req = context.Request.Body?.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true) ?? new Newtonsoft.Json.Linq.JObject();
397+ var idRaw = req["id"]?.ToString() ?? "1";
398+ long idLong;
399+ Newtonsoft.Json.Linq.JToken idToken = long.TryParse(idRaw, out idLong) ? (Newtonsoft.Json.Linq.JToken)new Newtonsoft.Json.Linq.JValue(idLong) : new Newtonsoft.Json.Linq.JValue(idRaw);
400+ var payload = new Newtonsoft.Json.Linq.JObject
401+ {
402+ ["status"] = "ok",
403+ ["source"] = "apim",
404+ ["timestampUtc"] = System.DateTime.UtcNow.ToString("o")
405+ };
406+ var response = new Newtonsoft.Json.Linq.JObject
407+ {
408+ ["jsonrpc"] = "2.0",
409+ ["id"] = idToken,
410+ ["result"] = new Newtonsoft.Json.Linq.JObject
411+ {
412+ ["content"] = new Newtonsoft.Json.Linq.JArray
413+ {
414+ new Newtonsoft.Json.Linq.JObject
415+ {
416+ ["type"] = "text",
417+ ["text"] = payload.ToString(Newtonsoft.Json.Formatting.None)
418+ }
419+ }
420+ }
421+ };
422+ return response.ToString(Newtonsoft.Json.Formatting.None);
423+ }</set-body>
424+ </return-response>
425+ </when>
426+ <when condition="@((string)context.Variables["rpcMethod"] == "tools/call" && (string)context.Variables["toolName"] == "listItems")">
427+ <send-request mode="new" response-variable-name="dummyResp" timeout="20" ignore-error="false">
428+ <set-url>https://dummyjson.com/todos?limit=10</set-url>
429+ <set-method>GET</set-method>
430+ </send-request>
431+ <set-variable name="dummyBody" value="@(((IResponse)context.Variables["dummyResp"]).Body.As<string>(preserveContent: true))" />
432+ <return-response>
433+ <set-status code="200" reason="OK" />
434+ <set-header name="Content-Type" exists-action="override">
435+ <value>application/json</value>
436+ </set-header>
437+ <set-body>@{
438+ var req = context.Request.Body?.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true) ?? new Newtonsoft.Json.Linq.JObject();
439+ var idRaw = req["id"]?.ToString() ?? "1";
440+ long idLong;
441+ Newtonsoft.Json.Linq.JToken idToken = long.TryParse(idRaw, out idLong) ? (Newtonsoft.Json.Linq.JToken)new Newtonsoft.Json.Linq.JValue(idLong) : new Newtonsoft.Json.Linq.JValue(idRaw);
442+ var text = (string)context.Variables["dummyBody"];
443+ var response = new Newtonsoft.Json.Linq.JObject
444+ {
445+ ["jsonrpc"] = "2.0",
446+ ["id"] = idToken,
447+ ["result"] = new Newtonsoft.Json.Linq.JObject
448+ {
449+ ["content"] = new Newtonsoft.Json.Linq.JArray
450+ {
451+ new Newtonsoft.Json.Linq.JObject
452+ {
453+ ["type"] = "text",
454+ ["text"] = text
455+ }
456+ }
457+ }
458+ };
459+ return response.ToString(Newtonsoft.Json.Formatting.None);
460+ }</set-body>
461+ </return-response>
462+ </when>
463+ <when condition="@((string)context.Variables["rpcMethod"] == "tools/call" && (string)context.Variables["toolName"] == "getItem")">
464+ <send-request mode="new" response-variable-name="dummyResp" timeout="20" ignore-error="false">
465+ <set-url>@($"https://dummyjson.com/todos/{(string)context.Variables["toolId"]}")</set-url>
466+ <set-method>GET</set-method>
467+ </send-request>
468+ <set-variable name="dummyBody" value="@(((IResponse)context.Variables["dummyResp"]).Body.As<string>(preserveContent: true))" />
469+ <return-response>
470+ <set-status code="200" reason="OK" />
471+ <set-header name="Content-Type" exists-action="override">
472+ <value>application/json</value>
473+ </set-header>
474+ <set-body>@{
475+ var req = context.Request.Body?.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true) ?? new Newtonsoft.Json.Linq.JObject();
476+ var idRaw = req["id"]?.ToString() ?? "1";
477+ long idLong;
478+ Newtonsoft.Json.Linq.JToken idToken = long.TryParse(idRaw, out idLong) ? (Newtonsoft.Json.Linq.JToken)new Newtonsoft.Json.Linq.JValue(idLong) : new Newtonsoft.Json.Linq.JValue(idRaw);
479+ var text = (string)context.Variables["dummyBody"];
480+ var response = new Newtonsoft.Json.Linq.JObject
481+ {
482+ ["jsonrpc"] = "2.0",
483+ ["id"] = idToken,
484+ ["result"] = new Newtonsoft.Json.Linq.JObject
485+ {
486+ ["content"] = new Newtonsoft.Json.Linq.JArray
487+ {
488+ new Newtonsoft.Json.Linq.JObject
489+ {
490+ ["type"] = "text",
491+ ["text"] = text
492+ }
493+ }
494+ }
495+ };
496+ return response.ToString(Newtonsoft.Json.Formatting.None);
497+ }</set-body>
498+ </return-response>
499+ </when>
500+ <when condition="@((string)context.Variables["rpcMethod"] == "tools/call" && (string)context.Variables["toolName"] == "createItem")">
501+ <send-request mode="new" response-variable-name="dummyResp" timeout="20" ignore-error="false">
502+ <set-url>https://dummyjson.com/todos/add</set-url>
503+ <set-method>POST</set-method>
504+ <set-header name="Content-Type" exists-action="override">
505+ <value>application/json</value>
506+ </set-header>
507+ <set-body>@{
508+ var text = ((string)context.Variables["createTodoText"] ?? "Prepare APIOps demo").Replace("\\", "\\\\").Replace("\"", "\\\"");
509+ var completedRaw = ((string)context.Variables["createCompleted"] ?? "false").ToLowerInvariant();
510+ var completed = (completedRaw == "true") ? "true" : "false";
511+ return "{\"todo\":\"" + text + "\",\"completed\":" + completed + ",\"userId\":1}";
512+ }</set-body>
513+ </send-request>
514+ <set-variable name="dummyBody" value="@(((IResponse)context.Variables["dummyResp"]).Body.As<string>(preserveContent: true))" />
515+ <return-response>
516+ <set-status code="200" reason="OK" />
517+ <set-header name="Content-Type" exists-action="override">
518+ <value>application/json</value>
519+ </set-header>
520+ <set-body>@{
521+ var req = context.Request.Body?.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true) ?? new Newtonsoft.Json.Linq.JObject();
522+ var idRaw = req["id"]?.ToString() ?? "1";
523+ long idLong;
524+ Newtonsoft.Json.Linq.JToken idToken = long.TryParse(idRaw, out idLong) ? (Newtonsoft.Json.Linq.JToken)new Newtonsoft.Json.Linq.JValue(idLong) : new Newtonsoft.Json.Linq.JValue(idRaw);
525+ var text = (string)context.Variables["dummyBody"];
526+ var response = new Newtonsoft.Json.Linq.JObject
527+ {
528+ ["jsonrpc"] = "2.0",
529+ ["id"] = idToken,
530+ ["result"] = new Newtonsoft.Json.Linq.JObject
531+ {
532+ ["content"] = new Newtonsoft.Json.Linq.JArray
533+ {
534+ new Newtonsoft.Json.Linq.JObject
535+ {
536+ ["type"] = "text",
537+ ["text"] = text
538+ }
539+ }
540+ }
541+ };
542+ return response.ToString(Newtonsoft.Json.Formatting.None);
543+ }</set-body>
544+ </return-response>
545+ </when>
546+ <when condition="@((string)context.Variables["rpcMethod"] == "notifications/initialized")">
547+ <return-response>
548+ <set-status code="202" reason="Accepted" />
549+ </return-response>
550+ </when>
551+ <otherwise>
552+ <return-response>
553+ <set-status code="200" reason="OK" />
554+ <set-header name="Content-Type" exists-action="override">
555+ <value>application/json</value>
556+ </set-header>
557+ <set-body>@{
558+ var req = context.Request.Body?.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true) ?? new Newtonsoft.Json.Linq.JObject();
559+ var idRaw = req["id"]?.ToString() ?? "1";
560+ long idLong;
561+ Newtonsoft.Json.Linq.JToken idToken = long.TryParse(idRaw, out idLong) ? (Newtonsoft.Json.Linq.JToken)new Newtonsoft.Json.Linq.JValue(idLong) : new Newtonsoft.Json.Linq.JValue(idRaw);
562+ var response = new Newtonsoft.Json.Linq.JObject
563+ {
564+ ["jsonrpc"] = "2.0",
565+ ["id"] = idToken,
566+ ["error"] = new Newtonsoft.Json.Linq.JObject
567+ {
568+ ["code"] = -32601,
569+ ["message"] = "Method not found"
570+ }
571+ };
572+ return response.ToString(Newtonsoft.Json.Formatting.None);
573+ }</set-body>
574+ </return-response>
575+ </otherwise>
576+ </choose>
577+ </inbound>
578+ <backend>
579+ <base />
580+ </backend>
581+ <outbound>
582+ <base />
583+ </outbound>
584+ <on-error>
585+ <base />
586+ </on-error>
587+ </policies>
588+ '''
589+
265590var mcpExistingServerPolicyXml = '''
266591<policies>
267592 <inbound>
@@ -330,6 +655,11 @@ resource apiMcpFromApi 'Microsoft.ApiManagement/service/apis@2025-09-01-preview'
330655 name : 'src-mcp-from-api'
331656}
332657
658+ resource apiMcpTodos 'Microsoft.ApiManagement/service/apis@2025-09-01-preview' existing = {
659+ parent : apim
660+ name : 'src-mcp-todos'
661+ }
662+
333663resource apiMcpExistingServer 'Microsoft.ApiManagement/service/apis@2025-09-01-preview' existing = {
334664 parent : apim
335665 name : 'src-mcp-existing-server'
@@ -371,6 +701,15 @@ resource apiMcpFromApiPolicy 'Microsoft.ApiManagement/service/apis/policies@2025
371701 }
372702}
373703
704+ resource apiMcpTodosPolicy 'Microsoft.ApiManagement/service/apis/policies@2025-09-01-preview' = {
705+ parent : apiMcpTodos
706+ name : 'policy'
707+ properties : {
708+ format : 'rawxml'
709+ value : mcpTodosPolicyXml
710+ }
711+ }
712+
374713resource apiMcpExistingServerPolicy 'Microsoft.ApiManagement/service/apis/policies@2025-09-01-preview' = {
375714 parent : apiMcpExistingServer
376715 name : 'policy'
0 commit comments