Building from Source¶
Both implementations can be built locally from the repository. Pre-built binaries are available on the Releases page if you don't need to build from source.
Go¶
Requirements: Go 1.26+
Run tests:
Run linter (requires golangci-lint):
C# (.NET 10)¶
Requirements: .NET 10 SDK
Build for development (no AOT):
cd csharp
dotnet restore BingWebmasterMcp.slnx
dotnet build BingWebmasterMcp.slnx -c Release --no-restore
Run tests:
Publish as a Native AOT self-contained binary:
Native AOT requirements
Native AOT compilation on Linux requires clang and zlib1g-dev. Install with:
End-to-End Testing¶
Unit tests (go test ./..., dotnet test) mock the HTTP layer and are genuinely thorough about
request-building and response-parsing -- but they call client methods directly and never go
through the MCP protocol itself. That gap matters: real bugs have hidden specifically in MCP's
argument-binding and tool-dispatch layer, invisible to 100+ passing client-layer unit tests, and
only surfaced by driving the actual compiled binary over real MCP stdio. Two layers of E2E testing
close that gap.
Automated suite (tests/e2e/, runs in CI)¶
A pytest suite that drives both compiled binaries over the real MCP stdio
JSON-RPC protocol (initialize -> notifications/initialized -> tools/list -> tools/call)
against a local, stdlib-only mock Bing API server (tests/e2e/mock_bing_server.py). No live
credentials, network access, or real Bing account are required -- this is what runs on every push
and pull request.
Both go/bwt-mcp-go(.exe) and the C# build output at
csharp/src/BingWebmasterMcp/bin/Release/net10.0/bwt-mcp-csharp(.exe) must already be built (see
above); the suite skips with a clear message if a binary is missing rather than building it itself.
For every one of the 43 tools, against both languages, the suite calls the tool twice: once with only its required arguments (omitting every optional parameter) and once with every optional argument explicitly supplied. The first case is exactly the scenario that catches a missing or mismatched default value at the MCP layer -- unit tests structurally cannot reach this, since they bypass MCP argument binding entirely by calling the client directly.
tests/e2e/test_regression.py permanently guards three real bugs found this way, each of which
100+ passing unit tests missed:
- Go's
add_site_roleonce called the wrong Bing endpoint (AddSiteRoleinstead of the realAddSiteRoles) -- unit tests asserted the request body but not the URL path. - C#'s
get_site_roles/add_site_rolewere missing default values on optional bool parameters, which only breaks when the MCP SDK itself tries to bind a missing argument -- invisible to tests that call the client directly. - Go's
add_site_roledefaultedis_read_onlytofalse(bool's zero value) when the argument was omitted, while C# defaulted it totrue-- a silent, security-relevant cross-language divergence for the same tool and the same omitted argument, found by this suite and not by any prior test or manual round.
Each of these was confirmed to actually fail the corresponding regression test when deliberately reintroduced, then re-fixed -- this isn't just passing trivially.
Manual live-account harness (scripts/e2e_mcp_harness.py)¶
The lower-level MCP stdio client the automated suite is itself built on
(scripts/mcp_stdio_client.py) is also usable standalone for occasional manual testing against the
real Bing API and a real account -- useful before a release, or when validating a change the
mock server can't represent. Requires Python 3 and a real or dummy API key.
python scripts/e2e_mcp_harness.py \
--label "Go E2E" \
--command go/bwt-mcp-go.exe \
--api-key YOUR_REAL_OR_DUMMY_KEY
With a real API key this exercises live Bing responses end-to-end. With a dummy key it still
validates the full protocol lifecycle, tool schema registration, and that the server correctly
surfaces Bing's real error responses ({"ErrorCode":..., "Message":...} at HTTP 400) as clean
MCP tool errors -- without needing a real account. Pass --read-only to restrict calls to
non-mutating tools when testing against an account you don't want changed, --site-url to
substitute a real site for the https://example.test/ placeholder, or --call TOOL_NAME
'{"json":"args"}' to make one explicit, ad-hoc tool call. Run it against both go/bwt-mcp-go.exe
and the published bwt-mcp-csharp binary and diff the tools/list output to confirm Go/C# parity.
Contributing¶
- Open an issue describing the bug or feature before submitting a PR
- Run
golangci-lint run(Go) ordotnet buildwith zero warnings (C#) before submitting - Keep both implementations in sync -- a feature added to Go should also be added to C#