Skip to content

Instantly share code, notes, and snippets.

@wayanjimmy
Created July 30, 2026 13:56
Show Gist options
  • Select an option

  • Save wayanjimmy/f2ff1bf65ef0b6e4046f7d76eded2e48 to your computer and use it in GitHub Desktop.

Select an option

Save wayanjimmy/f2ff1bf65ef0b6e4046f7d76eded2e48 to your computer and use it in GitHub Desktop.
Debugging an MCP Server with MCP Inspector — setup guide for HTTP transport, modern protocol era, and Linux keyring workaround

Debugging an MCP Server with MCP Inspector

Learned while setting up the MCP Inspector to debug a Go-based MCP server using the 2026-07-28 Streamable HTTP transport (stateless mode).

Context

  • MCP server: Go service using github.com/modelcontextprotocol/go-sdk/mcp, mounted at POST /mcp
  • Transport: Streamable HTTP (stateless, JSONResponse: true)
  • Protocol era: Modern (2026-07-28) — requires _meta.io.modelcontextprotocol/protocolVersion in every request body and Mcp-Method HTTP headers
  • Inspector version: @modelcontextprotocol/inspector@2.0.0
  • Platform: Linux (Ubuntu, no gnome-keyring running)

1. Create an Inspector config file

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.

2. Launch the Inspector

Install globally

npm install -g @modelcontextprotocol/inspector

Web UI (interactive debugging)

mcp-inspector --config .inspector.json

Opens 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.

CLI mode (automation / quick checks)

mcp-inspector --cli --config .inspector.json --server summarize --method tools/list

TUI mode (interactive terminal)

mcp-inspector --tui --config .inspector.json --server summarize

Ad-hoc (no config file)

# HTTP server
mcp-inspector --transport http --server-url http://127.0.0.1:8420/mcp

# stdio server
mcp-inspector node build/index.js

3. Config file selection: --config vs --catalog

--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.

4. Platform keyring issue (Linux without gnome-keyring)

The problem

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

Diagnosis

# 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 broken

Fix: Replace the keyring with a no-op stub

Since 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-daemon so the real keyring works.

5. Modern protocol era quirks

When using protocolEra: "modern" (2026-07-28):

  1. _meta.io.modelcontextprotocol/protocolVersion must be present in every request body — not just the HTTP header MCP-Protocol-Version. The Inspector handles this automatically when protocolEra: "modern" is set.

  2. Mcp-Method header — your server may require this on every request (our Go server's validateMCPHeaders middleware does). The Inspector's modern era sends it.

  3. logging/setLevel unsupported — the CLI mode tries to call logging/setLevel during 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.

6. Useful Inspector web UI features

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

Quick reference

# 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment