MSBuild Binlog Analyzer for VS Code
Analyze MSBuild binary logs (.binlog) with GitHub Copilot Chat and MCP tools — right from VS Code.
Preview — This extension is under active development. Feedback welcome — please report issues at dotnet/skills.
Quick Start
- Install this extension (requires VS Code with the GitHub Copilot Chat extension and the .NET SDK; VS Code enforces the minimum version from the extension manifest)
- Open a
.binlog — via Binlog: Load File (Ctrl+Shift+P), Build & Collect Binlog, or from Structured Log Viewer → Open in VS Code
- Use
@binlog in Copilot Chat:
@binlog why did the build fail?
@binlog what are the slowest targets?
@binlog /perf
The Microsoft.AITools.BinlogMcp server (the MCP binlog analysis tools) is auto-installed on first use.
What You Get
| Feature |
Description |
| @binlog Chat |
Ask Copilot about errors, performance, targets, imports, NuGet issues — with slash commands like /errors, /perf, /timeline, /compare, /summary, /incremental, /buildcheck, /search, /targets, /properties, /items, /propertyhistory |
| Build & Collect |
Build a project and capture a .binlog in one step — only for a solution/project open in a trusted workspace |
| Binlog Explorer |
Sidebar tree with project → target → task hierarchy, errors, warnings, performance |
| Build Timeline |
Visual bar charts of target/task durations with click-to-analyze in Copilot |
| Fix All Issues |
Copilot fixes all build errors/warnings, rebuilds, and loads before/after for comparison |
| Auto-fix Diagnostic |
Right-click any error/warning in the tree → "Auto-fix with Copilot" to fix it directly |
| Optimize Build |
Pick optimizations, Copilot applies changes, verify with A/B comparison |
| Build Analysis Mode |
Chat mode pre-configured with Microsoft.AITools.BinlogMcp MCP tools — works with any agent |
| Language Model Tools |
binlog_lm_overview, binlog_lm_errors, binlog_lm_search, binlog_lm_perf, binlog_lm_compare — available to @workspace, agent mode, and custom chat modes |
| CI/CD Integration |
Download binlogs from Azure DevOps Pipelines and GitHub Actions — filter by branch or PR. See CI/CD Integration for how it authenticates and what it stores |
| Cross-machine binlogs |
Match a binlog recorded elsewhere (CI, a coworker, another clone) to the solution open in your workspace — Binlog: Map Binlog Paths to This Workspace infers the remote → local root mapping |
| Problems Panel |
Build diagnostics as native VS Code errors/warnings with per-project CodeLens and "Ask @binlog" CodeActions |
| Search |
Search across all build events — targets, tasks, messages, properties |
This extension treats a .binlog as trusted input. Loading one is not a
sandboxed, read-only peek at an opaque file: a binlog is a complete recording of
a build, and the whole point of opening it is to read that recording — project
paths, command lines, properties, environment values, diagnostics — display it,
resolve it against files on your disk, and send whatever you ask about to GitHub
Copilot. There is no meaningful way to analyze a build log while defending
against its contents.
So the requirement is on you, not on the file:
Open a binlog only when you trust the build, the machine and the pipeline
that produced it — and, for CI, everyone who can edit or queue that
pipeline.
The extension makes that decision explicit rather than implicit:
- Binlogs inside an open, trusted workspace folder load with no extra
prompt. Trusting the folder in VS Code is the decision; the extension is
disabled outright in Restricted Mode
(
untrustedWorkspaces.supported: false) and in virtual workspaces.
- Anything else is confirmed every time it is loaded. A file in
Downloads, a CI artifact in the download cache, a path carried by a
vscode://binlog-analyzer/open?path=… link — nothing in the window vouches
for it, so a modal names the files, states the trust requirement, and asks
you to confirm. The dialog also offers Add Folder to Workspace…, which
turns the repeated confirmation into a durable workspace-trust decision.
- Downloading from CI asks you to trust the source, once per GitHub
repository or Azure DevOps project, per workspace, before the first artifact
is fetched. See CI/CD Integration.
We do not ask you to redact binlogs before opening them. Redaction is not
what makes a binlog safe to analyze, and treating it as a prerequisite would
imply the extension can cope with a binlog you do not trust. It cannot, and
neither can redaction — provenance is the control. What you should still weigh
is disclosure: a binlog can contain secrets, environment values, embedded source
and PII/EUII, and using Copilot deliberately sends binlog-derived content
off-device. That is a privacy decision under your organization's policy and
your Copilot plan's data-handling terms, not a defence against a hostile file.
See Telemetry for exactly what does and does not leave your
machine.
Confining binlog paths to your workspace is about correctness, not security
Several features refuse to act on a path recorded in the binlog when it points
outside the folders you have open: path mappings must target a workspace folder,
"open this file" resolves inside your workspace, and builds are refused for a
target found anywhere else.
For path resolution this is a correctness and convenience feature, not a
security measure. A binlog records the paths of the machine that produced it,
and nothing in the file says which local directory corresponds to a CI agent's
checkout. Resolving those paths anywhere other than the folders you actually
opened gives you the wrong file, the wrong diagnostics, and analysis of code you
were not looking at. Confining them is what makes the answer meaningful. It is
not a barrier against a malicious binlog — the binlog is trusted before it is
opened, so there is no such adversary in scope.
Running a build is the exception, and it is a genuine security control. A
build executes arbitrary MSBuild code from wherever the target lives, which is a
risk regardless of where the binlog came from — see below.
Builds and Workspace Trust
Loading and analyzing a .binlog is read-only — it needs no more than the
binlog trust confirmation described above.
Building is not: a build executes MSBuild targets, custom tasks,
pre/post-build scripts and NuGet build extensions from the location being built,
which is arbitrary code execution. The commands that start a build — Build &
Collect Binlog, Rebuild with Property Tracking, Fix All Build Issues
and Optimize Build — therefore run only when all of the following hold:
- A folder is open. There is no "just build in the current directory"
fallback; with no workspace folder open there is nothing the extension is
willing to build.
- The workspace is trusted. In VS Code's Restricted Mode this extension is
disabled entirely (
untrustedWorkspaces.supported: false), so no build
command can run. Use Workspaces: Manage Workspace Trust to trust a folder.
- The target is inside an open workspace folder. A solution or project
found anywhere else — including one named by a binlog that was produced on
another machine — is refused.
- You confirm the location. Every build shows a modal naming the exact file
and asks you to confirm with I Trust This Location — Build. The same
wording appears when picking a solution and in Set Workspace Folder.
Solutions (.sln, .slnx, .slnf) and projects (.csproj, .vbproj,
.fsproj, .vcxproj, .sqlproj, .proj, .msbuildproj and similar) are
discovered automatically. If your repository builds through something else — a
dirs.proj, a custom entry point — choose Select Build File... and pick it;
it still has to live inside the open workspace.
Analyzing a binlog from elsewhere is unaffected. If you want to build it
too, open your local clone of those sources as the workspace folder and use
Binlog: Map Binlog Paths to This Workspace so the binlog's recorded paths
resolve to your copy. The build then targets your local file, never the path
recorded on the machine that produced the binlog.
Build safety: MSBuild project and solution files can execute arbitrary
logic. Use build, fix and optimization features only with trusted source in
a trusted workspace, and review Copilot-proposed edits and commands before
approving them.
With no folder open — or with a folder open that contains no solution or
project — the build actions in the Binlog Explorer's Actions list stay
visible but inert: the icon is dimmed, the description reads set workspace
folder first, and clicking one explains which of the two problems it is and
offers Open Folder… or Set Workspace Folder…. They are shown rather
than removed so the capability stays discoverable; an action that only appears
once the workspace is already right teaches nobody that it exists. In the
Command Palette, where there is no dimmed state to render, the same commands
are hidden instead. Opening the folder that merely holds the .binlog is not
enough; open the sources.
Build Baseline & Regressions
A build baseline is a saved .binlog snapshot that newly-loaded builds are automatically compared against, so you can spot regressions (slower targets, new errors/warnings, changed properties). If you see "No build baseline is set.", pick one of these:
Set a baseline (any one):
- Binlog Explorer → right-click a loaded
.binlog → Set This Binlog as Baseline (baselines the file you right-clicked).
- Command Palette (
Ctrl+Shift+P) → Binlog: Set This Binlog as Baseline — when invoked without a tree-item context, baselines the currently active binlog.
Once set, the active binlog is compared to the baseline automatically and a Build: status-bar item (bottom-left) reflects the result.
View regressions:
- Click the Build: status-bar item, or
- Command Palette → Binlog: Show Build Regressions (focuses the regression results in the Binlog Explorer).
Clear a baseline:
- Command Palette → Binlog: Clear Build Baseline, or right-click the baseline/regressions node in the explorer → Clear Build Baseline.
Tip: a typical loop is — load a known-good build and Set This Binlog as Baseline, make a change, rebuild via Build & Collect Binlog, then read the regressions that surface automatically.
CI/CD Integration
Binlog: Download Binlog from CI/CD lists recent runs for the repository open
in your workspace and downloads the .binlog files from their published
artifacts. The provider is inferred from your git remote (Azure DevOps
Pipelines or GitHub Actions); you can also pick org/project or owner/repo
manually.
The pipeline you investigate must be one you trust
A downloaded binlog is loaded like any other, and the extension
treats a binlog as trusted input. Choosing a
pipeline is therefore a trust decision, and a broader one than trusting a single
file: it covers the pipeline definition, the repository it builds, and everyone
who can edit or queue it.
Before the first artifact is fetched from a given repository or project the
extension shows a modal naming the provider and the owner/repo or
org/project coordinate and asks you to confirm. The answer is remembered
against that coordinate, per workspace, so a run publishing several artifacts
asks once rather than once per file.
Note the scope this grants, which the dialog also states: the answer covers
every workflow in that GitHub repository, or every pipeline in that Azure
DevOps project — including pipelines that build other repositories. On GitHub
that is the honest boundary anyway, since workflows live in the repository and
anyone who can add one can edit another. On Azure DevOps it is genuinely wider
than the single pipeline you picked, so confirm only for a project whose
pipelines you would all be willing to investigate.
Because downloads land in a private cache outside your workspace, the
binlog trust confirmation is then shown when the
downloaded file is loaded.
How the extension authenticates
The extension does not implement its own sign-in, and never asks you for a
password or a Personal Access Token. It has no credential store of its own and
does not read .npmrc, .netrc, git credential, or any PAT you may have
configured elsewhere.
- Azure DevOps — anonymous first. Run/pipeline listing and the artifact ZIP
are requested with no credentials at all, so public projects work with no
token and no
az CLI. If that fails, the extension shells out to
az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798
(the well-known Azure DevOps resource ID) and retries the same URL with an
Authorization: Bearer header. Listing falls back to az rest /
az pipelines the same way.
- GitHub Actions — always via
gh. There is no anonymous path: listing runs
and artifacts uses gh run list / gh api, and the artifact download calls
gh auth token and sends the result as an Authorization: Bearer header.
If the relevant CLI is missing or not logged in, the operation fails with an
explanatory error. The extension never prompts for a credential to fill the gap —
sign in yourself with az login or gh auth login and retry. A response that
redirects to a sign-in page is treated as a failure rather than followed.
What is stored, and where
| Item |
Stored? |
Where |
Lifetime |
| Access token / PAT |
No |
Held in a local variable for the duration of one HTTPS request |
Discarded when the download finishes or fails |
| Your CLI credentials |
No |
Owned entirely by az / gh |
Managed by those tools |
Downloaded artifacts and extracted .binlog files |
Yes |
os.tmpdir()/binlog-analyzer-ci/, created mode 0700 (owner-only) |
Pruned on activation: deleted after 7 days, or oldest-first once the cache exceeds 2 GB. Directories younger than 1 hour, and those backing a currently loaded binlog, are never pruned |
| Recently used AzDO orgs / GitHub repos |
Yes |
VS Code workspaceState (names only, no credentials) |
Last 5, per workspace |
| Pipelines you confirmed you trust |
Yes |
VS Code workspaceState (owner/repo or org/project names only) |
Until the workspace state is cleared |
| Path mappings for CI/remote binlogs |
Yes |
binlogAnalyzer.ciPathMappings setting (Workspace scope when a folder is open, otherwise User) |
Until you remove them |
The download cache is deliberately owner-only rather than a world-readable temp
directory: artifacts and the binlogs inside them carry environment values,
command lines and source paths from the build machine. The extension refuses to
adopt a binlog-analyzer-ci directory it cannot prove it owns, and re-asserts
0700 on one left behind by an earlier version.
Network boundaries
- HTTPS only. A redirect that downgrades to
http: is rejected — the server
chooses the redirect, so you would never see the downgrade.
- Host allowlist. Redirects are followed only to
github.com,
githubusercontent.com, actions.githubusercontent.com,
pipelines.actions.githubusercontent.com, dev.azure.com,
visualstudio.com, core.windows.net (Azure storage backing artifacts) and
azureedge.net (Azure CDN) — and their subdomains. Anything else is refused
by name.
- Credentials are not replayed across redirects. The
Authorization header
is sent on the initial request only, so a redirect to a storage/CDN endpoint
cannot harvest your token.
- Transfer limits. Downloads are capped by
binlogAnalyzer.ci.maxArtifactMb
(declared and actual size), a per-hop idle timeout, and an absolute
wall-clock ceiling for the whole transfer. Extraction is zip-bomb and
path-traversal checked, and everything is cancellable.
- Tokens are never written to the output channel, an error message, telemetry,
or disk.
Optional CI watch
binlogAnalyzer.ci.watch (off by default) polls the latest run for your current
branch or PR and offers to analyze the binlogs when it fails. It uses exactly the
same access path described above, at the interval set by
binlogAnalyzer.ci.pollSeconds, backing off while VS Code is idle or unfocused.
Configuration
| Setting |
Default |
Description |
binlogAnalyzer.mcpServerPath |
"" |
Custom path to the MCP server executable |
binlogAnalyzer.mcpServerArgs |
"--binlog ${binlog}" |
Argument template for the MCP server. ${binlog} is replaced with each binlog path |
binlogAnalyzer.autoLoad |
true |
Auto-load binlog diagnostics on activation |
binlogAnalyzer.diagnosticsSeverityFilter |
"Warning" |
Min severity for Problems panel |
binlogAnalyzer.inlineDecorations |
true |
Show build errors as inline decorations in source files |
binlogAnalyzer.chat.includeAllTools |
false |
Expose all available tools (file editing, terminal, other MCPs) to the @binlog chat participant |
binlogAnalyzer.chat.additionalToolPatterns |
[] |
Additional tool name patterns to include alongside binlog tools (e.g. ["copilot_codebase", "terminal"]) |
binlogAnalyzer.tree.topItemsLimit |
10 |
Number of expensive targets/tasks/items to request and show before "Load more" |
binlogAnalyzer.mcp.requestTimeoutMs |
30000 |
Base MCP request timeout; large binlogs get an adaptive size-based extension |
binlogAnalyzer.buildCheck.timeoutMs |
120000 |
BuildCheck analysis timeout |
binlogAnalyzer.diagnostics.autoRunBuildCheck |
false |
Automatically run BuildCheck analysis when a binlog is loaded (requires .NET SDK 9.0.100+) |
binlogAnalyzer.diagnostics.propertyTrackingLevel |
15 |
MSBuild property-tracking level used when rebuilding with property tracking. 15 = all events |
binlogAnalyzer.ci.maxRuns |
20 |
Maximum recent CI runs to fetch |
binlogAnalyzer.ci.maxArtifactMb |
1536 |
Maximum size in MiB (1.5 GiB by default) of a CI artifact to download; larger transfers are aborted |
binlogAnalyzer.ci.watch |
false |
Watch the current branch or PR's latest CI run and offer to analyze binlogs when it fails |
binlogAnalyzer.ci.pollSeconds |
60 |
Base polling interval (seconds) for CI watch; the watcher backs off when idle or unfocused |
binlogAnalyzer.ciPathMappings |
[] |
Path-prefix mappings for CI/remote binlogs, e.g. D:\a\1\s → the local workspace root |
binlogAnalyzer.regression.minDeltaMs |
200 |
Minimum target duration increase (ms) to flag as a build regression |
binlogAnalyzer.regression.minDeltaPct |
15 |
Minimum target duration increase (%) to flag as a build regression |
binlogAnalyzer.regression.failOnNewWarnings |
false |
Treat newly added warnings as build regressions |
Telemetry
This extension collects usage data and sends it to Microsoft to help improve
our products and services. The extension respects the
telemetry.telemetryLevel setting — learn more at
https://code.visualstudio.com/docs/getstarted/telemetry. To disable
telemetry in VS Code, including telemetry from this extension, set:
"telemetry.telemetryLevel": "off"
What is sent. Every extension-defined event and property is enumerated
in the bundled telemetry.json using the standard 1DS classification
schema (classification, endPoint, purpose); the VS Code telemetry
library also adds its standard common properties (including pseudonymized
machine and session identifiers). At a glance:
- Feature insight — extension activation, command invocation, chat
participant / slash-command usage, tree navigation, binlog-loaded count.
- Performance & reliability — MCP tool-install success/failure, build
check duration, error counts. Error messages are never sent as raw text:
the user's home directory and username are first replaced with
~ and
<user>, then the result is hashed with HMAC-SHA256 (16-char hex) —
identical to the C# Binlog MCP server's Hash — and only the error kind
(exception.type, e.g. TypeError) is sent in clear. Every event also
carries binlog.build_version, matching the MCP server's conventions.
What is not sent in telemetry. This paragraph is about the telemetry
pipeline described above and nothing else. No .binlog content, no file
system paths, no source-code snippets, no Copilot Chat conversation text, and
no user identifiers beyond VS Code's pseudonymized machine- and session-id
hashes are included in any telemetry event.
This is not a claim that binlog content stays on your machine. When you
use the @binlog chat participant, or any other Copilot feature backed by
this extension's MCP tools, binlog-derived content — including build errors,
target and task names, command lines and file paths — is deliberately sent to
GitHub Copilot as part of the model request. That path is a product feature,
is separate from telemetry, and is governed by your Copilot plan's data
handling terms rather than by the telemetry statements above.
Binlogs can contain secrets (such as tokens, passwords and connection
strings), environment-variable values, embedded source or project content,
and PII/EUII in user names, paths, command lines and build properties. Sending
that to Copilot is a privacy decision — what your organization permits to be
processed off-device, under your Copilot plan's data-handling terms — and it is
yours to make before you load a binlog and start asking about it.
It is deliberately not framed as a safety prerequisite, and the extension does
not ask you to scrub or redact a binlog first. Redaction is not what makes a
binlog safe to analyze: provenance is. Load
binlogs from builds and pipelines you trust, and load only data you and your
organization permit GitHub Copilot to process.
Read the Microsoft privacy statement
to learn more.
Development: MCP Contract
The extension vendors the Binlog MCP JSON Schemas under src/contract/schemas/ and commits generated TypeScript declarations in src/contract/.
npm run gen:contract # regenerate src/contract/*.d.ts from vendored schemas
npm run check:contract # regenerate and fail if generated files drift
When server schemas change, copy the new *.schema.json files into src/contract/schemas/, run npm run gen:contract, and commit both schemas and generated types.
The binlog_capabilities response is validated against the vendored schema at runtime (src/contractRuntime.ts); a malformed payload falls back to the legacy compatibility path with a specific diagnostic instead of failing later with a type error. src/test/capabilitiesValidation.test.ts asserts the validator's required-field list and enums still match capabilities.schema.json.
Development: Linting
npm run lint # oxlint src
The linter is oxlint, configured in .oxlintrc.json.
ESLint is deliberately not used. Linting TypeScript with ESLint requires
typescript-eslint, which refuses to load against the TypeScript 7 API this
project compiles with — it throws typescript-eslint does not support TS 7.0
at import time (upstream tracking issue:
typescript-eslint#10940).
oxlint parses TypeScript itself and does not depend on the typescript
package, so it is the only linter that works on this tree today. If
typescript-eslint gains TS 7 support, switching back is a config-only change.
Severity policy: correctness rules fail the build; advisory categories are
warnings so pre-existing findings stay visible without blocking CI.
Troubleshooting: MCP Server Installation
The extension auto-installs Microsoft.AITools.BinlogMcp via dotnet tool install -g. In corporate environments with restricted NuGet feeds, this may fail. Here are the workarounds:
1. Install with explicit feed source
dotnet tool install -g Microsoft.AITools.BinlogMcp --prerelease --add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json
2. Diagnose NuGet issues
dotnet nuget list source
Common problems:
- dotnet-public feed not configured — the tool is published on the dotnet-public Azure DevOps feed
- Authenticated feed requires credentials — may block access to the feed
- Package source mapping excludes the dotnet-public feed for this package
3. Verify installation
dotnet tool list -g | Select-String Microsoft.AITools.BinlogMcp
binlog-mcp --help
License
See the bundled LICENSE.md for the Microsoft Software License Terms.
When you hit a problem and want to share details with us, all relevant
diagnostic information lives in VS Code's Output panel. Open
View → Output and switch the dropdown to one of:
| Output channel |
What it contains |
Binlog Analyzer |
Extension activity: file loads, tree refreshes, command execution. |
Binlog Analyzer Telemetry |
A local mirror of every telemetry event the extension emits (with names + properties), printed before the event is sent. Useful for verifying exactly what would be reported. |
MCP: Microsoft.AITools.BinlogMcp |
The MCP server's stdout/stderr — protocol handshake, tool invocations, any server-side errors. |
Before sharing logs publicly
Output channels may contain file paths, project property values, and
environment-variable values that MSBuild surfaces into the log. Before
posting a log on a GitHub issue or chat:
- Search for and redact secrets (tokens, connection strings, passwords).
- Search-and-replace your user home directory with
~ (the extension
already does this for error strings it sends as telemetry, but the
on-disk Output panel still contains the raw values).
Both points are about the output-channel text you are about to publish, not
about preparing a binlog for the extension — see
Binlogs Are Trusted Input. A .binlog records
everything the build touched, so treat attaching one to a public issue as
publishing that build in full, and share it only when that is acceptable.
For private reports (security vulnerabilities or anything you'd rather
not put on a public issue), follow the process in the bundled SECURITY.md.