Skip to content

Added changes to set McpServerOptions.Instructions in HTTPS/SSE mode.#3423

Open
anushakolan wants to merge 1 commit intomainfrom
dev/anushakolan/server-instruction-not-set
Open

Added changes to set McpServerOptions.Instructions in HTTPS/SSE mode.#3423
anushakolan wants to merge 1 commit intomainfrom
dev/anushakolan/server-instruction-not-set

Conversation

@anushakolan
Copy link
Copy Markdown
Contributor

Why make this change?

Closes #3283

runtime.mcp.description was configured, but MCP HTTP/SSE initialize did not return it as instructions. This made the engine behavior inconsistent with expected MCP initialization output.

Related discussion: #3282

What is this change?

  • Wired runtime MCP description into MCP server options for HTTP/SSE initialization:

    • runtime.mcp.description -> ServerInstructions
    • Files updated:
      • src/Azure.DataApiBuilder.Mcp/Core/McpServiceCollectionExtensions.cs
      • src/Azure.DataApiBuilder.Mcp/Core/McpServerConfiguration.cs
  • Added/updated regression coverage for initialize response instructions:

    • src/Service.Tests/Configuration/ConfigurationTests.cs
    • Test: TestMcpInitializeIncludesInstructionsFromRuntimeDescription

How the bug was simulated:

  • Reverted the MCP wiring changes and called MCP initialize; result.instructions was missing.

How it was verified after fix:

  • Re-applied the wiring, called MCP initialize again, and confirmed result.instructions is present.

How was this tested?

  • Integration Tests
  • Unit Tests

Ran:

  • dotnet test src/Service.Tests/Azure.DataApiBuilder.Service.Tests.csproj --filter "FullyQualifiedName~ConfigurationTests.TestMcpInitializeIncludesInstructionsFromRuntimeDescription" -v minimal

Sample Request(s)

MCP initialize request (HTTP):

curl -i -X POST http://localhost:5000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc":"2.0",
    "id":1,
    "method":"initialize",
    "params":{
      "protocolVersion":"2025-03-26",
      "capabilities":{},
      "clientInfo":{"name":"manual-test","version":"1.0.0"}
    }
  }'

Expected response snippet after fix:

{
  "result": {
    "instructions": "Use SQL tools to query the database."
  }
}

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aligns MCP HTTP/SSE initialization behavior with the stdio transport by propagating runtime.mcp.description into the MCP initialize response’s instructions field, and adds a regression test to prevent regressions.

Changes:

  • Pass runtime.mcp.description into MCP server configuration during DI setup.
  • Set MCP server options (ServerInstructions) so HTTP/SSE initialize includes instructions.
  • Add an MSSQL integration test that calls MCP initialize and asserts result.instructions is present (with helper logic to parse JSON vs SSE responses).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/Azure.DataApiBuilder.Mcp/Core/McpServiceCollectionExtensions.cs Passes runtime MCP description into MCP server configuration during service registration.
src/Azure.DataApiBuilder.Mcp/Core/McpServerConfiguration.cs Adds an instructions parameter and wires it to MCP server options used by HTTP/SSE initialize.
src/Service.Tests/Configuration/ConfigurationTests.cs Adds a regression test + helper to invoke MCP initialize and validate returned instructions.

Comment on lines +6334 to +6354
while (retryCount < RETRY_COUNT)
{
object payload = new
{
jsonrpc = "2.0",
id = 1,
method = "initialize",
@params = new
{
protocolVersion = "2025-03-26",
capabilities = new { },
clientInfo = new { name = "dab-test", version = "1.0.0" }
}
};

HttpRequestMessage mcpRequest = new(HttpMethod.Post, mcp.Path)
{
Content = JsonContent.Create(payload)
};
mcpRequest.Headers.Add("Accept", "application/json, text/event-stream");

Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetMcpInitializeResponse duplicates the initialize request payload + retry loop logic already implemented in GetMcpResponse. This increases the chance the two helpers drift (e.g., protocolVersion/capabilities/headers/retry conditions). Consider extracting a shared helper for building/sending the MCP initialize request (and optionally returning the response body) to keep behavior consistent.

Copilot uses AI. Check for mistakes.
Comment on lines +6349 to +6355
HttpRequestMessage mcpRequest = new(HttpMethod.Post, mcp.Path)
{
Content = JsonContent.Create(payload)
};
mcpRequest.Headers.Add("Accept", "application/json, text/event-stream");

HttpResponseMessage mcpResponse = await httpClient.SendAsync(mcpRequest);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside the retry loop, HttpRequestMessage/HttpResponseMessage are created repeatedly but never disposed. In long/parallel test runs this can retain resources longer than needed; prefer wrapping both in using (and reading content before disposal) within each iteration.

Suggested change
HttpRequestMessage mcpRequest = new(HttpMethod.Post, mcp.Path)
{
Content = JsonContent.Create(payload)
};
mcpRequest.Headers.Add("Accept", "application/json, text/event-stream");
HttpResponseMessage mcpResponse = await httpClient.SendAsync(mcpRequest);
using HttpRequestMessage mcpRequest = new(HttpMethod.Post, mcp.Path)
{
Content = JsonContent.Create(payload)
};
mcpRequest.Headers.Add("Accept", "application/json, text/event-stream");
using HttpResponseMessage mcpResponse = await httpClient.SendAsync(mcpRequest);

Copilot uses AI. Check for mistakes.
Comment on lines +6386 to +6399
foreach (string line in ssePayload.Split('\n'))
{
string trimmed = line.Trim();
if (trimmed.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
{
string data = trimmed.Substring("data:".Length).Trim();
if (!string.IsNullOrWhiteSpace(data) && data.StartsWith('{'))
{
return data;
}
}
}

return string.Empty;
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExtractJsonFromSsePayload only returns a single data: line that starts with {. SSE events can legally split a JSON payload across multiple data: lines (which should be concatenated with newlines). Consider accumulating consecutive data: lines for the event and then parsing the combined string to avoid flaky parsing if the transport changes formatting/chunking.

Suggested change
foreach (string line in ssePayload.Split('\n'))
{
string trimmed = line.Trim();
if (trimmed.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
{
string data = trimmed.Substring("data:".Length).Trim();
if (!string.IsNullOrWhiteSpace(data) && data.StartsWith('{'))
{
return data;
}
}
}
return string.Empty;
List<string> eventDataLines = new();
static string GetJsonPayload(List<string> dataLines)
{
if (dataLines.Count == 0)
{
return string.Empty;
}
string combinedPayload = string.Join("\n", dataLines);
return !string.IsNullOrWhiteSpace(combinedPayload) && combinedPayload.TrimStart().StartsWith('{')
? combinedPayload
: string.Empty;
}
foreach (string rawLine in ssePayload.Split('\n'))
{
string line = rawLine.TrimEnd('\r');
if (string.IsNullOrWhiteSpace(line))
{
string jsonPayload = GetJsonPayload(eventDataLines);
if (!string.IsNullOrEmpty(jsonPayload))
{
return jsonPayload;
}
eventDataLines.Clear();
continue;
}
if (line.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
{
string data = line.Substring("data:".Length);
if (data.StartsWith(' '))
{
data = data.Substring(1);
}
eventDataLines.Add(data);
}
}
return GetJsonPayload(eventDataLines);

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Server instruction not set properly in the engine.

4 participants