cd /news/ai-agents/shelf-watch-3-what-good-looks-like-o… · home › topics › ai-agents › article
[ARTICLE · art-169092] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=· neutral

Shelf Watch #3: What good looks like on a hosted enterprise MCP server

A developer behind the open-source mcp-ship-check scanner completed a static code audit of 300 MCP servers, finding 10 with verified code-level vulnerabilities, one of which (atlas-mcp-server, GHSA-w3gh-28jp-2hmv) has been fixed and published while the rest are being disclosed privately to maintainers. A separate sweep of 116 registry-listed servers documenting mcp-remote found 284 invocations across 87 repos, 94% of which don't pin a version. The project also published an Enterprise MCP Reference Blueprint of four controls for multi-tenant MCP servers, and the device-code HTTPS fix for CVE-2026-108716 shipped in mcp-remote v0.16.6.

read7 min views6 publishedNov 13, 2026

Originally published in the mcp-ship-check repo, home of the free, offline scanner this post describes.

Update (2026-11-11). Since this post first ran, we've finished a static code audit of 300 MCP servers: 10 had a verified code-level vulnerability. One is fixed and published (atlas-mcp-server, GHSA-w3gh-28jp-2hmv); the rest are being disclosed privately to their maintainers, and we name a project only after its fix ships. Separately, a bridge sweep of 116 registry-listed servers that document mcp-remote found 284 invocations across 87 repos, and 94% of them don't pin a version. And the device-code HTTPS fix for CVE-2026-108716 has shipped in mcp-remote v0.16.6 (e4dc371): if you use --device-code, pin 0.16.6 or later.

In Shelf Watch #2 we read the code of 194 MCP servers and found the usual failure modes: a tool argument reaching a shell, a database "read-only" guard that only checked the first keyword, an HTTP transport listening on every network interface. That post was about what goes wrong.

This one is about the other end of the shelf: what a well-guarded hosted MCP server should look like, especially one that runs for many tenants at once, like the servers MSPs and security vendors are starting to ship. Below is our Enterprise MCP Reference Blueprint: four controls we'd want on any multi-tenant MCP server. None of them needs an enterprise budget. A two-person team can build every one.

The blueprint draws on what we've seen in production hosted MCP architectures and on the MCP specification's 2025-11-25 Authorization and Elicitation sections. The specific limits and exclusions below are our design targets, not a description of any one product.

Then we'll look at the part of the connection a server can't protect on its own: the client-side bridge that many setups use to reach a remote MCP server.

The blueprint. Clients sign in with OAuth 2.1 and PKCE, as the MCP authorization spec describes, not with a static API key. The token's scopes map onto the product's existing role-based access, with separate read and write grants per area of the product. An agent gets exactly the access its operator's role already has, and reading never implies writing.

Why it matters. The MCP layer inherits the permission model your product has already battle-tested, instead of inventing a second, weaker one beside it. A leaked or over-shared token can only do what that user could already do.

How a small team can copy it:

yourapp:invoices:read, yourapp:invoices:write), and grant them from the roles you already have. The blueprint. Cap requests per token. We use 100 per minute as a starting point for an interactive agent. Let each call touch one resource, and scope every write tool to a single subject: one device, process, file, user or incident. There's no "apply to all" write path.

Why it matters. Even with valid credentials, a confused or prompt-injected agent can't turn one bad call into a fleet-wide action. The worst case is bounded by design rather than by hoping the model behaves.

all: true flag.maxItems), and paginate reads. The blueprint. Every write tool waits for synchronous, in-chat human approval before it runs. If the connecting client can't show that approval step, the call fails closed instead of running unapproved.

Why it matters. This is the property the vulnerable servers in Shelf Watch #2 lacked. They ran dangerous operations with no human in the loop at all. "Refuse when we can't ask" is the safe default; "run anyway" is how a missing UI feature becomes an incident.

readOnlyHint for reads, destructiveHint for anything that changes or deletes. In Shelf Watch #2, a third of the tools we could inspect declared neither.elicitation capability when they connect, so your server knows up front whether it can ask. The blueprint. Tools that move money or entitlements (license allocation, trial changes, billing) aren't exposed to external MCP clients at all. If a vendor's own first-party assistant needs them, it gets them on a separate internal surface. The external surface is deliberately a subset of the internal one.

Why it matters. Not every tool belongs on a connector that any compatible client can reach. Anything that moves money, entitlements or billing state is a much bigger prize than a read-only query.

msp-seat-reconcile All four controls above live on the server. But many clients reach a remote MCP server through a local bridge: most often the open-source mcp-remote package, launched from the client's config with something like npx -y mcp-remote@<version> https://…. That bridge runs on the user's own machine, handles their login, and stores their tokens. A well-guarded server can't protect a user whose bridge is out of date.

Two public advisories show why this matters:

mcp-remote before 0.1.16. A malicious server could put a crafted URL in its OAuth metadata and get it run as a command on the user's machine 0.8.0 through 0.14.3. In the device-code login flow, the client secret and the issued tokens can be sent over plain HTTP if the authorization server's endpoints aren't HTTPS --device-code, so a normal browser login doesn't hit it. e4dc371) now requires HTTPS for the device-authorization and token endpoints (HTTP only on loopback), and --allow-http doesn't relax that. Releases before it, 0.14.4– 0.16.5 included, lack the check, so if you use 0.16.6 or later. Pinning a version is a tradeoff. An exact pin is reproducible and immune to a bad new release, but it stays frozen as advisories are published against it. An unpinned npx mcp-remote picks up fixes, but it also picks up whatever ships next. Our suggestion: pin an exact version, then actually re-check it on a schedule. That goes double if your product's docs tell customers which bridge to run: your setup snippet is part of your security surface.

To make the re-check cheap, we added a bridge check to our free, offline mcp-ship-check. It reads MCP client configs (claude_desktop_config.json, .mcp.json, mcp.json, including .cursor/ and .vscode/) and the code blocks in your docs, and checks every mcp-remote invocation against known advisories:

npx github:bot2botlabs/mcp-ship-check bridge --strict

Here's what it reports on a sample config with three servers (re-run with v0.3.3 after the 0.16.6 fix):

✖ bridge: 1 config/Markdown file(s) scanned; 3 mcp-remote invocation(s); 3 error(s), 0 warning(s) — --strict: warnings are errors
✖ bridge-cve-2025-6514: claude_desktop_config.json:3 (acme-legacy): mcp-remote@0.1.15 is in CVE-2025-6514's affected range (< 0.1.16): OS command injection via a malicious server's OAuth authorization_endpoint — upgrade to 0.1.16 or later
✖ bridge-cve-2026-108716: claude_desktop_config.json:4 (acme): mcp-remote@0.12.0 is in CVE-2026-108716's affected range (>= 0.8.0, < 0.16.6; the advisory record lists 0.8.0–0.14.3; fixed upstream in 0.16.6 (e4dc371)): client secret and tokens can be sent over cleartext HTTP in the device-code grant; this invocation doesn't pass --device-code, so the vulnerable path isn't used as written — upgrade to 0.16.6 or later, which enforces HTTPS on the device-code endpoints
✖ bridge-cve-2026-108716: claude_desktop_config.json:5 (acme-ci): mcp-remote@0.16.5 is in CVE-2026-108716's affected range (>= 0.8.0, < 0.16.6; the advisory record lists 0.8.0–0.14.3; fixed upstream in 0.16.6 (e4dc371)): client secret and tokens can be sent over cleartext HTTP in the device-code grant; this invocation passes --device-code, so the vulnerable path is in use — upgrade to 0.16.6 or later, which enforces HTTPS on the device-code endpoints
FAIL (4 checks)

Two details matter here. First, the check never calls a version "patched" just because it sits above an advisory's listed range: it records a fixed release only once the fix is confirmed in the upstream source, and otherwise says the fix is unconfirmed. Second, the affected range follows the fix, not just the advisory record: 0.16.5 with --device-code is still flagged, because the HTTPS check first shipped in 0.16.6, and an unpinned bridge run with --device-code gets a reminder to pin 0.16.6 or later. It also flags --allow-http pointed at a non-loopback http:// URL, and an unpinned mcp-remote as information (since it can't be checked offline). Without --strict it only warns, so you can add it to CI without breaking anything on day one.

Server side:

npx github:bot2botlabs/mcp-ship-check sinks --strict on your source: no shell strings, unguarded paths, interpolated SQL or exposed HTTP transport. Client side:

npx github:bot2botlabs/mcp-ship-check bridge --strict in CI, over your configs and docs. Shipping an MCP server and want a second set of eyes on its trust boundaries before launch? We do quick, hands-on reviews for small teams and solo builders — open an issue on bot2botlabs/mcp-ship-check or reach out, and we'll take a look.

Sources

Bot2Bot Labs keeps a daily, public-data archive of the MCP shelf. This is the third in a series on what's actually out there.

── more in #ai-agents 4 stories · sorted by recency
── more on @mcp-ship-check 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/shelf-watch-3-what-g…] indexed:0 read:7min 2026-11-13 · —