diff --git a/src/Azure.DataApiBuilder.Mcp/BuiltInTools/ExecuteEntityTool.cs b/src/Azure.DataApiBuilder.Mcp/BuiltInTools/ExecuteEntityTool.cs
index be2fa7af36..f6d2413927 100644
--- a/src/Azure.DataApiBuilder.Mcp/BuiltInTools/ExecuteEntityTool.cs
+++ b/src/Azure.DataApiBuilder.Mcp/BuiltInTools/ExecuteEntityTool.cs
@@ -351,8 +351,16 @@ private static bool TryParseExecuteArguments(
entity = entityElement.GetString() ?? string.Empty;
// Extract parameters if provided (optional)
- if (rootElement.TryGetProperty("parameters", out JsonElement parametersElement) &&
- parametersElement.ValueKind == JsonValueKind.Object)
+ if (rootElement.TryGetProperty("arguments", out JsonElement argumentsElement) &&
+ argumentsElement.ValueKind == JsonValueKind.Object)
+ {
+ foreach (JsonProperty property in argumentsElement.EnumerateObject())
+ {
+ parameters[property.Name] = GetParameterValue(property.Value);
+ }
+ }
+ else if (rootElement.TryGetProperty("parameters", out JsonElement parametersElement) &&
+ parametersElement.ValueKind == JsonValueKind.Object)
{
foreach (JsonProperty property in parametersElement.EnumerateObject())
{
diff --git a/src/Azure.DataApiBuilder.Mcp/Core/McpProtocolDefaults.cs b/src/Azure.DataApiBuilder.Mcp/Core/McpProtocolDefaults.cs
new file mode 100644
index 0000000000..8e307a7c0e
--- /dev/null
+++ b/src/Azure.DataApiBuilder.Mcp/Core/McpProtocolDefaults.cs
@@ -0,0 +1,30 @@
+using Microsoft.Extensions.Configuration;
+
+namespace Azure.DataApiBuilder.Mcp.Core
+{
+ ///
+ /// Centralized defaults and configuration keys for MCP protocol settings.
+ ///
+ public static class McpProtocolDefaults
+ {
+ ///
+ /// Default MCP protocol version advertised when no configuration override is provided.
+ ///
+ public const string DEFAULT_PROTOCOL_VERSION = "2025-06-18";
+
+ ///
+ /// Configuration key used to override the MCP protocol version.
+ ///
+ public const string PROTOCOL_VERSION_CONFIG_KEY = "MCP:ProtocolVersion";
+
+ ///
+ /// Helper to resolve the effective protocol version from configuration.
+ /// Falls back to when the key is not set.
+ ///
+ public static string ResolveProtocolVersion(IConfiguration? configuration)
+ {
+ return configuration?.GetValue(PROTOCOL_VERSION_CONFIG_KEY) ?? DEFAULT_PROTOCOL_VERSION;
+ }
+ }
+}
+
diff --git a/src/Azure.DataApiBuilder.Mcp/Core/McpStdioServer.cs b/src/Azure.DataApiBuilder.Mcp/Core/McpStdioServer.cs
new file mode 100644
index 0000000000..79ccf39356
--- /dev/null
+++ b/src/Azure.DataApiBuilder.Mcp/Core/McpStdioServer.cs
@@ -0,0 +1,486 @@
+using System.Collections;
+using System.Reflection;
+using System.Security.Claims;
+using System.Text;
+using System.Text.Json;
+using Azure.DataApiBuilder.Core.AuthenticationHelpers.AuthenticationSimulator;
+using Azure.DataApiBuilder.Mcp.Model;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using ModelContextProtocol.Protocol;
+
+namespace Azure.DataApiBuilder.Mcp.Core
+{
+ ///
+ /// MCP stdio server:
+ /// - Reads JSON-RPC requests (initialize, listTools, callTool) from STDIN
+ /// - Writes ONLY MCP JSON responses to STDOUT
+ /// - Writes diagnostics to STDERR (so STDOUT remains “pure MCP”)
+ ///
+ public class McpStdioServer : IMcpStdioServer
+ {
+ private readonly McpToolRegistry _toolRegistry;
+ private readonly IServiceProvider _serviceProvider;
+ private readonly string _protocolVersion;
+
+ private const int MAX_LINE_LENGTH = 1024 * 1024; // 1 MB limit for incoming JSON-RPC requests
+
+ public McpStdioServer(McpToolRegistry toolRegistry, IServiceProvider serviceProvider)
+ {
+ _toolRegistry = toolRegistry ?? throw new ArgumentNullException(nameof(toolRegistry));
+ _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+
+ // Allow protocol version to be configured via IConfiguration, using centralized defaults.
+ IConfiguration? configuration = _serviceProvider.GetService();
+ _protocolVersion = McpProtocolDefaults.ResolveProtocolVersion(configuration);
+ }
+
+ ///
+ /// Runs the MCP stdio server loop, reading JSON-RPC requests from STDIN and writing MCP JSON responses to STDOUT.
+ ///
+ /// Token to signal cancellation of the server loop.
+ /// A task representing the asynchronous operation.
+ public async Task RunAsync(CancellationToken cancellationToken)
+ {
+ Console.Error.WriteLine("[MCP DEBUG] MCP stdio server started.");
+
+ // Use UTF-8 WITHOUT BOM
+ UTF8Encoding utf8NoBom = new(encoderShouldEmitUTF8Identifier: false);
+
+ using Stream stdin = Console.OpenStandardInput();
+ using Stream stdout = Console.OpenStandardOutput();
+ using StreamReader reader = new(stdin, utf8NoBom);
+ using StreamWriter writer = new(stdout, utf8NoBom) { AutoFlush = true };
+
+ // Redirect Console.Out to use our writer
+ Console.SetOut(writer);
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ string? line = await reader.ReadLineAsync(cancellationToken);
+ if (string.IsNullOrWhiteSpace(line))
+ {
+ continue;
+ }
+
+ if (line.Length > MAX_LINE_LENGTH)
+ {
+ WriteError(id: null, code: -32600, message: "Request too large");
+ continue;
+ }
+
+ JsonDocument doc;
+ try
+ {
+ doc = JsonDocument.Parse(line);
+ }
+ catch (JsonException jsonEx)
+ {
+ Console.Error.WriteLine($"[MCP DEBUG] JSON parse error: {jsonEx.Message}");
+ WriteError(id: null, code: -32700, message: "Parse error");
+ continue;
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine($"[MCP DEBUG] Unexpected error parsing request: {ex.Message}");
+ WriteError(id: null, code: -32603, message: "Internal error");
+ continue;
+ }
+
+ using (doc)
+ {
+ JsonElement root = doc.RootElement;
+
+ JsonElement? id = null;
+ if (root.TryGetProperty("id", out JsonElement idEl))
+ {
+ id = idEl; // preserve original type (string or number)
+ }
+
+ if (!root.TryGetProperty("method", out JsonElement methodEl))
+ {
+ WriteError(id, -32600, "Invalid Request");
+ continue;
+ }
+
+ string method = methodEl.GetString() ?? string.Empty;
+
+ try
+ {
+ switch (method)
+ {
+ case "initialize":
+ HandleInitialize(id);
+ break;
+
+ case "notifications/initialized":
+ break;
+
+ case "tools/list":
+ HandleListTools(id);
+ break;
+
+ case "tools/call":
+ await HandleCallToolAsync(id, root, cancellationToken);
+ break;
+
+ case "ping":
+ WriteResult(id, new { ok = true });
+ break;
+
+ case "shutdown":
+ WriteResult(id, new { ok = true });
+ return;
+
+ default:
+ WriteError(id, -32601, $"Method not found: {method}");
+ break;
+ }
+ }
+ catch (Exception)
+ {
+ WriteError(id, -32603, "Internal error");
+ }
+ }
+ }
+ }
+
+ ///
+ /// Handles the "initialize" JSON-RPC method by sending the MCP protocol version, server capabilities, and server info to the client.
+ ///
+ ///
+ /// The request identifier extracted from the incoming JSON-RPC request. Used to correlate the response with the request.
+ ///
+ ///
+ /// This method constructs and writes the MCP "initialize" response to STDOUT. It uses the protocol version defined by PROTOCOL_VERSION
+ /// and includes supported capabilities and server information. No notifications are sent here; the server waits for the client to send
+ /// "notifications/initialized" before sending any notifications.
+ ///
+ private void HandleInitialize(JsonElement? id)
+ {
+ // Extract the actual id value from the request
+ object? requestId = id.HasValue ? GetIdValue(id.Value) : null;
+
+ // Create the initialize response
+ var response = new
+ {
+ jsonrpc = "2.0",
+ id = requestId,
+ result = new
+ {
+ protocolVersion = _protocolVersion,
+ capabilities = new
+ {
+ tools = new { listChanged = true },
+ logging = new { }
+ },
+ serverInfo = new
+ {
+ name = "Data API Builder",
+ version = "1.0.0"
+ }
+ }
+ };
+
+ string json = JsonSerializer.Serialize(response);
+ Console.Out.WriteLine(json);
+ }
+
+ ///
+ /// Handles the "tools/list" JSON-RPC method by sending the list of available tools to the client.
+ ///
+ ///
+ /// The request identifier extracted from the incoming JSON-RPC request. Used to correlate the response with the request.
+ ///
+ private void HandleListTools(JsonElement? id)
+ {
+ List