mirror of
https://github.com/svnscha/mcp-windbg.git
synced 2026-10-16 20:32:50 +08:00
90457306fe
Installing mcp-windbg into Claude Code meant a `pip install` plus a hand-written server registration with an env var. It becomes two lines: ``` /plugin marketplace add svnscha/mcp-windbg /plugin install mcp-windbg@mcp-windbg ``` This repo hosts the marketplace itself, so there is no second repository to keep in step. ## Why there is no install step at all The plugin launches the server with `uvx`, pinned to the matching release, so uv fetches it from PyPI on first use. That is what makes the two lines *complete* rather than the last two of five. `server.json` already declared `runtimeHint: uvx`, so this is how the package presented itself anyway. ## What ships - The ten tools, through the plugin's `.mcp.json`. - **`/mcp-windbg:analyze-dump`** - crash-dump triage that separates fact from inference and calls out missing symbols rather than reading meaning into `module+0x1234`. - **`/mcp-windbg:debug-remote`** - attaching to a user-mode debug server, and the part that is easy to get wrong by hand: saying plainly whether the target was left running or frozen. - **`/mcp-windbg:kernel-debug`** - driving a live kernel target, including the `g` / `wait_for_break` / `send_ctrl_break` handling from 1.1.0, and closing with `resume: true` so nobody leaves a machine halted. - **`/mcp-windbg:windbg-doctor`** - the first thing to run when nothing works: checks platform, CDB, uv, the `uvx` round trip, and symbols, then reports what to fix rather than one failure at a time. - **`crash-analyst`** agent - hand it a dump and a question and it investigates on its own, returning a verdict, the evidence behind it, what it ruled out, and what to do next. Its `tools:` list uses the scoped `mcp__plugin_mcp-windbg_mcp-windbg__*` names. - A [reference page](https://github.com/svnscha/mcp-windbg/blob/feat/claude-plugin/docs/reference/plugin.md) in the docs site, plus plugin sections in the README, getting-started, and client configuration. Always-on cost is about 400 tokens; a skill or the agent costs more only when it fires. ## Symbols are configured, without overriding yours The plugin sets `_NT_SYMBOL_PATH` as a **default**, not a value: ```json "_NT_SYMBOL_PATH": "${_NT_SYMBOL_PATH:-SRV*C:\Symbols*https://msdl.microsoft.com/download/symbols}" ``` Claude Code's `.mcp.json` expansion supports `${VAR:-default}`, so an existing value wins and a symbol path someone tuned themselves is never overwritten. Without this the out-of-the-box experience is a stack of `module+0x1234` offsets, which is exactly the case where triage is guesswork. ## One thing that cannot be declared **Windows-only.** There is no manifest field for a platform constraint, so installing on macOS or Linux loads the plugin and the server then fails to find a debugger. Stated in the plugin README, the marketplace description, and the reference page; `windbg-doctor` catches it at the first check and stops there. ## Version drift, closed release-please now bumps the plugin manifest, the marketplace entry, and the `uvx` pin along with `pyproject.toml` and `server.json`, and `check-version-consistency.ps1` checks all three. That is the guard that a silently-broken `extra-files` entry does not ship - the exact failure caught by hand during the 1.1.0 release. The `uvx` pin survives the bump because release-please's `generic-json` updater does a substring replace against its version regex rather than overwriting the whole value, so `mcp-windbg@1.1.0` becomes `mcp-windbg@1.2.0` and not a bare `1.2.0`. ## A bug fixed on the way `check-version-consistency.ps1` looped with `foreach ($error in $errors)`. `$error` is a **read-only PowerShell automatic variable**, so the first time it had a mismatch to report it threw `Cannot overwrite variable Error` instead of naming the files. The build still failed, so this was never dangerous - just useless exactly when you needed it. Verified both ways now: it names the mismatching file, and still exits 1. ## Dependencies Floors raised to current releases with the major caps unchanged: `mcp` 2.1.1, `starlette` 1.6.0, `uvicorn` 0.52.4, and `pymdown-extensions` 11.0.2 for the docs build. `uv.lock` is regenerated; it had been stranded at 1.0.1 since the 1.1.0 release, because release-please has no updater for it. A `uv lock --check` step in CI would catch that drift and is worth a follow-up. ## Verification - `claude plugin validate` passes for both the marketplace manifest and the plugin manifest. - Full suite green against the upgraded dependencies: 99 passed, 2 skipped (the kernel scenarios, no target attached). Live CDB scenarios ran. - `validate-server-schema.py` passes; `uv lock --check` reports the lock in sync. - The `.mcp.json` snippet on the reference page is asserted to parse and to equal the real file, after it shipped with a single-backslash `C:\Symbols` that did neither. - The earlier end-to-end check (`claude plugin marketplace add` -> `install` -> `details`, and a headless session seeing the tools through `uvx` with nothing pre-installed) was run when the plugin had two skills and no agent, so it predates the current surface. ## Note You already have a `svnscha` marketplace pointing at `svnscha/claude-plugins`. This adds a second, repo-local one. If you would rather have the plugin listed there instead, say so - the plugin directory moves as-is and only the marketplace manifest changes.
112 lines
4.5 KiB
PowerShell
112 lines
4.5 KiB
PowerShell
#!/usr/bin/env pwsh
|
|
<#
|
|
.SYNOPSIS
|
|
Checks version consistency between pyproject.toml, server.json, and CHANGELOG.md
|
|
|
|
.DESCRIPTION
|
|
This script extracts version information from pyproject.toml, server.json, and CHANGELOG.md
|
|
and verifies that all versions are consistent across these files.
|
|
|
|
.EXAMPLE
|
|
.\check-version-consistency.ps1
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
param()
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
try {
|
|
Write-Host "Checking version consistency..." -ForegroundColor Cyan
|
|
|
|
# Extract version from pyproject.toml
|
|
if (-not (Test-Path "pyproject.toml")) {
|
|
throw "pyproject.toml not found in current directory"
|
|
}
|
|
|
|
$pyprojectMatch = Select-String -Path "pyproject.toml" -Pattern 'version = "([^"]+)"'
|
|
if (-not $pyprojectMatch) {
|
|
throw "Could not find version in pyproject.toml"
|
|
}
|
|
$PYPROJECT_VERSION = $pyprojectMatch.Matches[0].Groups[1].Value
|
|
Write-Host "INFO: pyproject.toml version: $PYPROJECT_VERSION" -ForegroundColor Green
|
|
|
|
# Extract version from server.json
|
|
if (-not (Test-Path "server.json")) {
|
|
throw "server.json not found in current directory"
|
|
}
|
|
|
|
$serverJsonContent = Get-Content "server.json" -Raw | ConvertFrom-Json
|
|
$SERVER_VERSION = $serverJsonContent.version
|
|
$PACKAGE_VERSION = $serverJsonContent.packages[0].version
|
|
Write-Host "INFO: server.json version: $SERVER_VERSION" -ForegroundColor Green
|
|
Write-Host "INFO: server.json package version: $PACKAGE_VERSION" -ForegroundColor Green
|
|
|
|
# Extract version from CHANGELOG.md
|
|
if (-not (Test-Path "CHANGELOG.md")) {
|
|
throw "CHANGELOG.md not found in current directory"
|
|
}
|
|
|
|
$changelogMatch = Select-String -Path "CHANGELOG.md" -Pattern '## \[([^\]]+)\]' | Select-Object -First 1
|
|
if (-not $changelogMatch) {
|
|
throw "Could not find version in CHANGELOG.md"
|
|
}
|
|
$CHANGELOG_VERSION = $changelogMatch.Matches[0].Groups[1].Value
|
|
Write-Host "INFO: CHANGELOG.md version: $CHANGELOG_VERSION" -ForegroundColor Green
|
|
|
|
# Extract versions from the Claude plugin + marketplace manifests. These are
|
|
# kept in step by release-please extra-files entries; checking them here is
|
|
# what catches an entry that silently stopped matching.
|
|
$PLUGIN_VERSION = (Get-Content "plugins/mcp-windbg/.claude-plugin/plugin.json" -Raw | ConvertFrom-Json).version
|
|
$MARKETPLACE_VERSION = (Get-Content ".claude-plugin/marketplace.json" -Raw | ConvertFrom-Json).plugins[0].version
|
|
$mcpArgs = (Get-Content "plugins/mcp-windbg/.mcp.json" -Raw | ConvertFrom-Json).mcpServers.'mcp-windbg'.args
|
|
$PINNED_VERSION = ($mcpArgs[0] -split '@')[-1]
|
|
Write-Host "INFO: plugin.json version: $PLUGIN_VERSION" -ForegroundColor Green
|
|
Write-Host "INFO: marketplace.json plugin version: $MARKETPLACE_VERSION" -ForegroundColor Green
|
|
Write-Host "INFO: plugin .mcp.json pinned server: $PINNED_VERSION" -ForegroundColor Green
|
|
|
|
# Check if all versions match
|
|
$errors = @()
|
|
|
|
if ($PYPROJECT_VERSION -ne $PLUGIN_VERSION) {
|
|
$errors += "Version mismatch: pyproject.toml ($PYPROJECT_VERSION) != plugin.json ($PLUGIN_VERSION)"
|
|
}
|
|
|
|
if ($PYPROJECT_VERSION -ne $MARKETPLACE_VERSION) {
|
|
$errors += "Version mismatch: pyproject.toml ($PYPROJECT_VERSION) != marketplace.json ($MARKETPLACE_VERSION)"
|
|
}
|
|
|
|
if ($PYPROJECT_VERSION -ne $PINNED_VERSION) {
|
|
$errors += "Version mismatch: pyproject.toml ($PYPROJECT_VERSION) != plugin .mcp.json pin ($PINNED_VERSION)"
|
|
}
|
|
|
|
if ($PYPROJECT_VERSION -ne $SERVER_VERSION) {
|
|
$errors += "Version mismatch: pyproject.toml ($PYPROJECT_VERSION) != server.json ($SERVER_VERSION)"
|
|
}
|
|
|
|
if ($PYPROJECT_VERSION -ne $PACKAGE_VERSION) {
|
|
$errors += "Version mismatch: pyproject.toml ($PYPROJECT_VERSION) != server.json package ($PACKAGE_VERSION)"
|
|
}
|
|
|
|
if ($PYPROJECT_VERSION -ne $CHANGELOG_VERSION) {
|
|
$errors += "Version mismatch: pyproject.toml ($PYPROJECT_VERSION) != CHANGELOG.md ($CHANGELOG_VERSION)"
|
|
}
|
|
|
|
if ($errors.Count -gt 0) {
|
|
# Not $error: that is a read-only PowerShell automatic variable, and
|
|
# assigning it threw before any mismatch could be reported.
|
|
foreach ($mismatch in $errors) {
|
|
Write-Host "ERROR: $mismatch" -ForegroundColor Red
|
|
}
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "`nAll versions are consistent: $PYPROJECT_VERSION" -ForegroundColor Green
|
|
exit 0
|
|
}
|
|
catch {
|
|
Write-Host "ERROR: $($_.Exception.Message)" -ForegroundColor Red
|
|
exit 1
|
|
}
|