Add MCP output size measurement tooling - #3288
Add MCP output size measurement tooling#3288Larry Osterman (LarryOsterman) wants to merge 16 commits into
Conversation
|
Azure Pipelines: Successfully started running 1 pipeline(s). There may be pipelines that require an authorized user to comment /azp run to run. |
|
Azure Pipelines: Successfully started running 1 pipeline(s). There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Adds an opt-in measurement workflow to quantify MCP server response sizes (initialize, tools/list discovery, and learn-mode tool/command responses) to help compare protocol cost between consolidated and namespace modes.
Changes:
- Adds an opt-in stdio integration test that starts
azmcpin both modes and records response sizes + artifacts. - Adds a summarization script to compare modes, emit console/JSON/Markdown summaries, and extract learn text / inner-command schemas.
- Adds an end-to-end runner script that builds, runs the measurement test, and then summarizes results.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| servers/Azure.Mcp.Server/tests/Azure.Mcp.Server.Tests/Infrastructure/McpOutputSizeTests.cs | New opt-in integration test that runs stdio discovery/learn flows and writes structured measurement artifacts. |
| eng/scripts/Summarize-McpOutputSizes.ps1 | Summarizes measurement report and extracts per-tool learn text and inner-command artifacts. |
| eng/scripts/Measure-McpOutputSizes.ps1 | Orchestrates build → measurement test run → summarization into a single workflow. |
Suppressed comments (3)
servers/Azure.Mcp.Server/tests/Azure.Mcp.Server.Tests/Infrastructure/McpOutputSizeTests.cs:191
JsonDocumentshould be disposed (it uses pooled buffers). This loop can run for hundreds of tools, so leaving documents undisposed can significantly increase transient memory usage.
});
var response = await SendRequestAsync(process, request);
var document = JsonDocument.Parse(response);
Assert.True(
document.RootElement.TryGetProperty("result", out _),
servers/Azure.Mcp.Server/tests/Azure.Mcp.Server.Tests/Infrastructure/McpOutputSizeTests.cs:246
JsonDocumentshould be disposed here as well (pooled buffers). This inner loop can run many times (tools × commands) so it’s worth keeping allocations bounded.
});
var response = await SendRequestAsync(process, request);
var document = JsonDocument.Parse(response);
Assert.True(
document.RootElement.TryGetProperty("result", out var commandResult),
$"The learn response for '{tool}.{command}' did not contain a result.");
servers/Azure.Mcp.Server/tests/Azure.Mcp.Server.Tests/Infrastructure/McpOutputSizeTests.cs:75
ServerTestOptionsis referenced here, but no such type exists in the repo, so the test project won’t compile. Use the same direct server-start argument pattern as other infrastructure tests.
using var process = Process.Start(new ProcessStartInfo
{
FileName = executablePath,
Arguments = ServerTestOptions.GetServerStartArguments($"server start --mode {mode}"),
UseShellExecute = false,
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
274fabc to
f6315c6
Compare
a796a4e to
200fe29
Compare
Alan Zimmer (alzimmermsft)
left a comment
There was a problem hiding this comment.
Will review the CSharp portion after we determine if we want it as a test or a standalone application like many of our other outer loop tooling (like Vally)
| .DESCRIPTION | ||
| Runs the full measurement workflow end to end: | ||
|
|
||
| 1. Builds the Azure.Mcp.Server.Tests project (which also builds the azmcp server). |
There was a problem hiding this comment.
Long term would be nice to expand this out to support any MCP server in this repo
There was a problem hiding this comment.
Agreed.
| } | ||
|
|
||
| New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null | ||
| $reportPath = Join-Path $OutputDirectory 'mcp-output-size.json' |
There was a problem hiding this comment.
Do we need $Clean if our implicit behavior is overwriting any previous result we captured?
There was a problem hiding this comment.
I added the -clean because the tool outputs to the same directory all the time and sometimes there are artifacts from previous runs (this happened yesterday while I was preparing for office hours - I had some artifacts from last week which confused me).
So when things change, there can be artifacts left over from previous runs that no longer apply.
There was a problem hiding this comment.
Does this need to be a separate script? Should the other script and where we land on how this works just write this report always?
There was a problem hiding this comment.
No it doesn't, but in other code reviews people objected to the large size of the scripts so I broke it up into a script runner and an analysis/output generator.
There was a problem hiding this comment.
FWIW, it's now a single script.
Adds an opt-in stdio integration test (McpOutputSizeTests) that measures the MCP server's initialize greeting, tools/list discovery response, and per-tool learn-mode responses in both consolidated and namespace server modes, including tool counts and inner-command schema verification. Adds Summarize-McpOutputSizes.ps1 to produce console, JSON, and Markdown summaries comparing the two modes, extract readable top learn-response text, split inner commands into per-tool files, and report every learn response over a configurable UTF-8 byte threshold (default 45000) in addition to the top 10 largest. Adds Measure-McpOutputSizes.ps1 to chain build, test, and summarize into a single command, with comment-based help and CmdletBinding support on both scripts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: LarryOsterman <8220172+LarryOsterman@users.noreply.github.com>
Co-authored-by: LarryOsterman <8220172+LarryOsterman@users.noreply.github.com>
200fe29 to
493d21d
Compare
Extract the raw JSON-RPC/stdio output-size measurement logic out of the xunit test McpOutputSizeTests.cs into a new standalone console app, eng/tools/McpOutputSizeMeasurer, following existing eng/tools/* conventions (Exe project, AOT-compatible where possible, InternalsVisibleTo test project, reuses McpToolEvaluator.Core.Utilities.FindRepoRoot). - New eng/tools/McpOutputSizeMeasurer/src: ported MeasureAsync/MeasureModeAsync, raw stdio JSON-RPC handshake/discovery/learn measurement, with Assert-based validation replaced by thrown exceptions since it's no longer test code. Disabled AOT-compat (anonymous-type JSON serialization is incompatible with trimming analysis for this dev/CI-only tool). - New Program.cs CLI: --executable/--report/--mode/--verbose/--help, defaulting to the built azmcp binary and TestResults/mcp-output-size.json. - New McpOutputSizeMeasurer.Tests: 3 unit tests for GetInnerCommandNames. - Registered both projects in Microsoft.Mcp.slnx and eng/tools/Tools.sln. - Updated Measure-McpOutputSizes.ps1 to build/run the new standalone tool directly instead of the (now removed) xunit test via env vars. - Removed servers/Azure.Mcp.Server/tests/.../Infrastructure/McpOutputSizeTests.cs entirely; its functionality is fully absorbed by the new tool. - Minor doc comment updates in Summarize-McpOutputSizes.ps1 / the new tool referencing the old test name. Validated: dotnet build for the new src/tests projects succeeds; the 3 new unit tests pass; Azure.Mcp.Server.Tests still builds after the file removal; ran the new McpOutputSizeMeasurer.exe end-to-end against the built azmcp.exe and fed the report into Summarize-McpOutputSizes.ps1, confirming console/JSON output (including the >45000-byte threshold list) is produced correctly. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Allows measuring a pre-built or published azmcp binary (e.g. extracted from a GitHub release) instead of always building the local servers/Azure.Mcp.Server/src project. When -ServerExecutable is supplied, the local server build step is skipped (the measurer tool is still built unless -SkipBuild is also passed), enabling side-by-side comparisons of current-source vs. a previously released server by running the script twice with different -OutputDirectory values. Validated by running the script against the locally built azmcp.exe as a stand-in released binary, both with and without -SkipBuild, confirming the measurement/summarize pipeline completes and produces correct output. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Measure-McpOutputSizes.ps1 changes: - Default -OutputDirectory moved from <repo-root>/TestResults to the repo-standard <repo-root>/.work/mcp-output-size (already gitignored via .work/, consistent with Pack-Zip.ps1/VallyEvaluator/ToolMetadataExporter conventions). - New -ReleaseTag parameter: given a microsoft/mcp GitHub release tag (e.g. Azure.Mcp.Server-3.0.0-beta.36), downloads the matching platform asset (Azure.Mcp.Server-<os>-<arch>.zip, matching Pack-Zip.ps1's naming), extracts it under <OutputDirectory>/release-download/<tag>, and measures the extracted azmcp binary instead of building the local source tree. Mutually exclusive with -ServerExecutable. - New -GitHubRepository parameter (defaults to microsoft/mcp) to resolve the release download URL, for forks/mirrors. This lets a previously released server version be measured without it being present locally, so its output sizes can be diffed against the current source tree by running the script twice with different -OutputDirectory values. Validated end-to-end: downloaded and measured the win-arm64 asset for Azure.Mcp.Server-3.0.0-beta.36, confirmed the report and console/JSON/ Markdown summaries were produced correctly under the new default .work/mcp-output-size location. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Adds an opt-in stdio integration test (McpOutputSizeTests) that measures the MCP server's initialize greeting, tools/list discovery response, and per-tool learn-mode responses in both consolidated and namespace server modes, including tool counts and inner-command schema verification. Adds Summarize-McpOutputSizes.ps1 to produce console, JSON, and Markdown summaries comparing the two modes, extract readable top learn-response text, split inner commands into per-tool files, and report every learn response over a configurable UTF-8 byte threshold (default 45000) in addition to the top 10 largest. Adds Measure-McpOutputSizes.ps1 to chain build, test, and summarize into a single command, with comment-based help and CmdletBinding support on both scripts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: LarryOsterman <8220172+LarryOsterman@users.noreply.github.com>
Co-authored-by: LarryOsterman <8220172+LarryOsterman@users.noreply.github.com>
Extract the raw JSON-RPC/stdio output-size measurement logic out of the xunit test McpOutputSizeTests.cs into a new standalone console app, eng/tools/McpOutputSizeMeasurer, following existing eng/tools/* conventions (Exe project, AOT-compatible where possible, InternalsVisibleTo test project, reuses McpToolEvaluator.Core.Utilities.FindRepoRoot). - New eng/tools/McpOutputSizeMeasurer/src: ported MeasureAsync/MeasureModeAsync, raw stdio JSON-RPC handshake/discovery/learn measurement, with Assert-based validation replaced by thrown exceptions since it's no longer test code. Disabled AOT-compat (anonymous-type JSON serialization is incompatible with trimming analysis for this dev/CI-only tool). - New Program.cs CLI: --executable/--report/--mode/--verbose/--help, defaulting to the built azmcp binary and TestResults/mcp-output-size.json. - New McpOutputSizeMeasurer.Tests: 3 unit tests for GetInnerCommandNames. - Registered both projects in Microsoft.Mcp.slnx and eng/tools/Tools.sln. - Updated Measure-McpOutputSizes.ps1 to build/run the new standalone tool directly instead of the (now removed) xunit test via env vars. - Removed servers/Azure.Mcp.Server/tests/.../Infrastructure/McpOutputSizeTests.cs entirely; its functionality is fully absorbed by the new tool. - Minor doc comment updates in Summarize-McpOutputSizes.ps1 / the new tool referencing the old test name. Validated: dotnet build for the new src/tests projects succeeds; the 3 new unit tests pass; Azure.Mcp.Server.Tests still builds after the file removal; ran the new McpOutputSizeMeasurer.exe end-to-end against the built azmcp.exe and fed the report into Summarize-McpOutputSizes.ps1, confirming console/JSON output (including the >45000-byte threshold list) is produced correctly. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Allows measuring a pre-built or published azmcp binary (e.g. extracted from a GitHub release) instead of always building the local servers/Azure.Mcp.Server/src project. When -ServerExecutable is supplied, the local server build step is skipped (the measurer tool is still built unless -SkipBuild is also passed), enabling side-by-side comparisons of current-source vs. a previously released server by running the script twice with different -OutputDirectory values. Validated by running the script against the locally built azmcp.exe as a stand-in released binary, both with and without -SkipBuild, confirming the measurement/summarize pipeline completes and produces correct output. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Measure-McpOutputSizes.ps1 changes: - Default -OutputDirectory moved from <repo-root>/TestResults to the repo-standard <repo-root>/.work/mcp-output-size (already gitignored via .work/, consistent with Pack-Zip.ps1/VallyEvaluator/ToolMetadataExporter conventions). - New -ReleaseTag parameter: given a microsoft/mcp GitHub release tag (e.g. Azure.Mcp.Server-3.0.0-beta.36), downloads the matching platform asset (Azure.Mcp.Server-<os>-<arch>.zip, matching Pack-Zip.ps1's naming), extracts it under <OutputDirectory>/release-download/<tag>, and measures the extracted azmcp binary instead of building the local source tree. Mutually exclusive with -ServerExecutable. - New -GitHubRepository parameter (defaults to microsoft/mcp) to resolve the release download URL, for forks/mirrors. This lets a previously released server version be measured without it being present locally, so its output sizes can be diffed against the current source tree by running the script twice with different -OutputDirectory values. Validated end-to-end: downloaded and measured the win-arm64 asset for Azure.Mcp.Server-3.0.0-beta.36, confirmed the report and console/JSON/ Markdown summaries were produced correctly under the new default .work/mcp-output-size location. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2a03e7d to
d1eed68
Compare
…ithub.com/microsoft/mcp into larryosterman-mcp-output-size-measurement
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Alan Zimmer (@alzimmermsft) It's now a standalone tool, and a single script :). |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Why
MCP clients benefit from understanding the protocol cost of tool discovery and learn-mode responses. This adds a repeatable, opt-in measurement workflow to compare consolidated and namespace server modes.
This tool is written as a test to take advantage of the existing MCP test infrastructure that invokes the MCP tool so that the metrics can be generated based on the actual output of the MCP server.
Approach
Validation
Ran the end-to-end measurement script successfully.