Learned while setting up the MCP Inspector to debug a Go-based MCP server using the 2026-07-28 Streamable HTTP transport (stateless mode).
- MCP server: Go service using
github.com/modelcontextprotocol/go-sdk/mcp, mounted atPOST /mcp - Transport: Streamable HTTP (stateless,
JSONResponse: true) - Protocol era: Modern (2026-07-28) — requires
_meta.io.modelcontextprotocol/protocolVersionin every request body andMcp-MethodHTTP headers - Inspector version:
@modelcontextprotocol/inspector@2.0.0 - Platform: Linux (Ubuntu, no gnome-keyring running)
The Inspector reads a config file in the standard mcpServers format, with Inspector-specific extensions.
{
"mcpServers": {
"summarize": {
"type": "http",
"url": "http://127.0.0.1:8420/mcp",
"protocolEra": "modern"
}
}
}Key fields:
| Field | Value | Why |
|---|---|---|
type |
"http" |
Streamable HTTP transport (not stdio or sse) |
url |
http://127.0.0.1:8420/mcp |
The MCP endpoint URL |
protocolEra |
"modern" |
Negotiates the 2026-07-28 protocol version. Without this, the Inspector defaults to "legacy" and your modern server rejects the connection with unsupported MCP-Protocol-Version |
For a stdio server, the config looks different:
{
"mcpServers": {
"my-server": {
"type": "stdio",
"command": "node",
"args": ["build/index.js"],
"env": { "API_KEY": "..." },
"cwd": "/path/to/server"
}
}
}Other Inspector-specific per-server fields: connectionTimeout, requestTimeout, roots, metadata, autoRefreshOnListChanged, paginatedLists.
npm install -g @modelcontextprotocol/inspectormcp-inspector --config .inspector.jsonOpens a web UI at http://localhost:6274. The config file's servers appear in a dropdown — select one and click Connect.
Note:
--server <name>has no effect on the web UI; it lists every server in the file. It only selects under--cli.
mcp-inspector --cli --config .inspector.json --server summarize --method tools/listmcp-inspector --tui --config .inspector.json --server summarize# HTTP server
mcp-inspector --transport http --server-url http://127.0.0.1:8420/mcp
# stdio server
mcp-inspector node build/index.js--config <path> |
--catalog <path> |
|
|---|---|---|
| Writable by Inspector | No (read-only) | Yes (Inspector's own server list) |
| Default path | none — must pass | ~/.mcp-inspector/mcp.json (or MCP_CATALOG_PATH env) |
| Editable in web UI | No | Yes |
Use --config when pointing at a file you own (e.g. checked into a repo). Use --catalog (or the default) when you want the Inspector to manage the file.
The Inspector uses @napi-rs/keyring to store OAuth client secrets and env-var values in the OS keyring. On Linux, this requires libsecret + a running keyring daemon (e.g. gnome-keyring-daemon).
If the keyring is unavailable, @napi-rs/keyring's AsyncEntry constructor throws KeyRevoked before the Inspector's KeyringSecretStore.get() can catch it. This blocks all config/catalog loading — both web UI and CLI — with:
Couldn't access platform storage: KeyRevoked
# Check if gnome-keyring is running
which gnome-keyring-daemon
# Test the keyring directly
node -e "
const { AsyncEntry } = require('@napi-rs/keyring');
new AsyncEntry('test', 'test:field');
" 2>&1
# If it throws KeyRevoked, the keyring is brokenSince the Inspector only uses the keyring for optional OAuth secrets (not needed for MCP_AUTH_MODE=none), replacing it with an in-memory no-op is safe for local debugging.
Find the keyring module inside the Inspector's dependencies:
KEYRING=$(find "$(npm root -g)/@modelcontextprotocol/inspector" \
-path "*/@napi-rs/keyring/index.js" | head -1)
echo "$KEYRING"Create an ESM stub + CJS fallback + updated package.json:
KEYRING_DIR=$(dirname "$KEYRING")
# ESM stub
cat > "$KEYRING_DIR/index.mjs" << 'EOF'
class NoopEntry {
#val = null;
constructor(service, account) { this.service = service; this.account = account; }
getPassword() { return Promise.resolve(this.#val); }
setPassword(v) { this.#val = v; return Promise.resolve(); }
deleteCredential() { this.#val = null; return Promise.resolve(); }
getPasswordCB(cb) { cb(null, this.#val); }
setPasswordCB(v, cb) { this.#val = v; cb(null); }
deleteCredentialCB(cb) { this.#val = null; cb(null); }
}
export const AsyncEntry = NoopEntry;
export const Entry = NoopEntry;
export class PasswordTask {}
export class EntryTask {}
export class SecretTask {}
export class FindCredentials {}
export function findCredentials(svc, cb) { if (cb) cb(null, []); return []; }
export async function findCredentialsAsync() { return []; }
export default { AsyncEntry, Entry, PasswordTask, EntryTask, SecretTask, FindCredentials, findCredentials, findCredentialsAsync };
EOF
# CJS fallback
cat > "$KEYRING_DIR/index.cjs" << 'EOF'
class NoopEntry {
constructor(service, account) { this._val = null; this.service = service; this.account = account; }
getPassword() { return Promise.resolve(this._val); }
setPassword(v) { this._val = v; return Promise.resolve(); }
deleteCredential() { this._val = null; return Promise.resolve(); }
}
module.exports = {
AsyncEntry: NoopEntry, Entry: NoopEntry,
PasswordTask: class {}, EntryTask: class {}, SecretTask: class {}, FindCredentials: class {},
findCredentials: (svc, cb) => { if (cb) cb(null, []); return []; },
findCredentialsAsync: async () => [],
};
EOF
# Updated package.json
cat > "$KEYRING_DIR/package.json" << 'EOF'
{
"name": "@napi-rs/keyring",
"version": "1.3.0",
"type": "module",
"main": "./index.mjs",
"module": "./index.mjs",
"exports": {
".": {
"import": "./index.mjs",
"require": "./index.cjs",
"default": "./index.mjs"
}
}
}
EOF
# Back up the original
cp "$KEYRING" "$KEYRING.bak"Caveat: This patch is lost if you reinstall the Inspector. An alternative is to start
gnome-keyring-daemonso the real keyring works.
When using protocolEra: "modern" (2026-07-28):
-
_meta.io.modelcontextprotocol/protocolVersionmust be present in every request body — not just the HTTP headerMCP-Protocol-Version. The Inspector handles this automatically whenprotocolEra: "modern"is set. -
Mcp-Methodheader — your server may require this on every request (our Go server'svalidateMCPHeadersmiddleware does). The Inspector's modern era sends it. -
logging/setLevelunsupported — the CLI mode tries to calllogging/setLevelduring initialization, which modern-era servers may not support. The web UI handles this gracefully; the CLI errors out. This is an Inspector issue, not a server bug.
| Tab | What it does |
|---|---|
| Tools | List registered tools, inspect schemas, call tools interactively |
| Resources | Browse and read exposed resources |
| Prompts | View available prompt templates |
| Network | Full JSON-RPC request/response log — every message sent and received |
| Server Settings | Per-server config: protocol era, timeouts, advertised extensions, roots |
# Install
npm install -g @modelcontextprotocol/inspector
# Create config
cat > .inspector.json << 'EOF'
{
"mcpServers": {
"summarize": {
"type": "http",
"url": "http://127.0.0.1:8420/mcp",
"protocolEra": "modern"
}
}
}
EOF
# Launch web UI
mcp-inspector --config .inspector.json
# CLI: list tools
mcp-inspector --cli --config .inspector.json --server summarize --method tools/list
# CLI: call a tool
mcp-inspector --cli --config .inspector.json --server summarize \
--method tools/call --tool-name summarize \
--tool-arg url=https://www.youtube.com/watch?v=VIDEO_ID