Skip to content

Add MCP output size measurement tooling - #3288

Open
Larry Osterman (LarryOsterman) wants to merge 16 commits into
mainfrom
larryosterman-mcp-output-size-measurement
Open

Add MCP output size measurement tooling#3288
Larry Osterman (LarryOsterman) wants to merge 16 commits into
mainfrom
larryosterman-mcp-output-size-measurement

Conversation

@LarryOsterman

@LarryOsterman Larry Osterman (LarryOsterman) commented Aug 14, 2026

Copy link
Copy Markdown
Member

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

  • Adds an opt-in stdio integration test that captures initialize, discovery, top-level learn, and per-command learn responses across both modes.
  • Writes structured reports and response artifacts, including decoded top learn descriptions and extracted inner-command schemas.
  • Adds PowerShell scripts to summarize JSON reports as console, JSON, and Markdown output, and to run build, measurement, and summarization end to end.

Validation

Ran the end-to-end measurement script successfully.

@azure-pipelines

Copy link
Copy Markdown
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

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 azmcp in 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

  • JsonDocument should 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

  • JsonDocument should 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

  • ServerTestOptions is 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.

Comment thread eng/tools/McpOutputSizeMeasurer/src/McpOutputSizeMeasurer.cs
Comment thread eng/scripts/Summarize-McpOutputSizes.ps1 Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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)

Comment thread eng/scripts/Measure-McpOutputSizes.ps1 Outdated
.DESCRIPTION
Runs the full measurement workflow end to end:

1. Builds the Azure.Mcp.Server.Tests project (which also builds the azmcp server).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Long term would be nice to expand this out to support any MCP server in this repo

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agreed.

Comment thread eng/scripts/Measure-McpOutputSizes.ps1 Outdated
}

New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null
$reportPath = Join-Path $OutputDirectory 'mcp-output-size.json'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we need $Clean if our implicit behavior is overwriting any previous result we captured?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.

Comment thread eng/scripts/Measure-McpOutputSizes.ps1 Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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>
@LarryOsterman
Larry Osterman (LarryOsterman) force-pushed the larryosterman-mcp-output-size-measurement branch from 200fe29 to 493d21d Compare August 20, 2026 22:52
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>
@LarryOsterman
Larry Osterman (LarryOsterman) force-pushed the larryosterman-mcp-output-size-measurement branch from 2a03e7d to d1eed68 Compare August 21, 2026 17:10
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@LarryOsterman

Copy link
Copy Markdown
Member Author

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

server-Azure.Mcp Azure.Mcp.Server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants