MCP Server Integration Problems: Protocol, Transport, Permissions, and Schema

A layer-by-layer MCP server diagnostic guide covering protocol era, transport, runtime, authorization, tool schema, and a safe Inspector CLI test.

When an MCP server will not connect or a tool call fails, do not change the client configuration, server code, and permissions at the same time. Identify the failing layer first. Use this order: protocol version → transport → startup and environment → permissions/auth → inputSchema → one read-only call.

As of August 23, 2026, the current verified MCP specification version is 2026-07-28. It no longer requires the old initialize handshake: a client can use server/discover, while the protocol version, client information, and capabilities travel with requests in _meta. Legacy implementations using 2025-11-25 or earlier follow a different sequence: initialize, the server response, and then notifications/initialized. Never combine these two protocol eras in one exchange.

Separate MCP from the model API at the outset. MCP connects a client to tools and context; the model request can use a different route and different credentials. Isolate the model layer with the BetterToken guide: your own API Key and a selected OpenAI-compatible or Anthropic-compatible interface provide a separate, testable route, so you do not have to search for a model error inside MCP. That Key is not an MCP server credential, and BetterToken is not an MCP host or MCP transport.

Classify the failure quickly

Before opening Inspector, record one symptom and the last confirmed checkpoint:

  • the process does not start at all;
  • the process runs, but the client receives no JSON-RPC;
  • the transport responds, but the version or capabilities do not agree;
  • the server returns 401 or 403;
  • tools/list works, but the expected tool is missing;
  • the tool is visible, but tools/call rejects its arguments;
  • the call completes, but its result cannot be verified.

Do not put an API Key, bearer token, cookie, complete prompt, or private file contents in your notes. For correlation, the timestamp, server name, method, JSON-RPC id, error code, and a sanitized message are enough.

1. Establish the protocol era

Find out which version the client, server, and SDK support. A Connected message confirms the transport and part of discovery, but it does not prove agreement on 2026-07-28.

For a modern implementation, check three signals:

  1. The SDK or its release notes explicitly state support for 2026-07-28.
  2. The trace contains server/discover or another discovery path defined by the SDK.
  3. Requests contain valid _meta with the version, client information, and capabilities.

Do not manually copy the shape of _meta from another SDK. A compatible client or official SDK must generate the exact wire format. If the server waits for initialize while the client sends self-contained 2026-07-28 requests, the problem is a protocol-era mismatch, not a tool schema error.

Legacy initialize: only for 2025-11-25 and earlier

This is a minimal legacy request. Do not add it to a modern 2026-07-28 flow “just in case.”

{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-11-25", "capabilities": {}, "clientInfo": { "name": "mcp-diagnostic-client", "version": "1.0.0" } } }

After a successful response, the legacy client sends notifications/initialized. If that exchange breaks, compare versions and the capabilities list first. It is too early to proceed to tools/list.

2. Test the transport separately from MCP semantics

MCP defines methods and data, while the transport handles startup, framing, delivery, and request cancellation. Switching from stdio to HTTP will not repair an invalid inputSchema.

stdio

With stdio, the client launches the server as a child process. Messages pass through stdin and stdout as newline-delimited UTF-8 JSON-RPC documents.

Check that:

  1. command exists and runs as the same user.
  2. Arguments are passed as separate items without relying on shell aliases.
  3. The working directory contains the required files, or paths are absolute.
  4. Required variables are actually available to the child process.
  5. stdout contains no banner, debug lines, or stack trace; logs go to stderr.

One stray console.log() on stdout can break framing before the client sees a JSON-RPC response.

Streamable HTTP

With Streamable HTTP, the client sends POST messages to one MCP endpoint. A response can be plain JSON or request-scoped SSE. Verify the exact URL, HTTP method, Content-Type, TLS, redirects, proxy, and authentication method.

Run the transport test on loopback or in an isolated test environment. Do not scan a public production endpoint without permission. If POST returns an HTML login page, a 301/302 to another host, or a reverse-proxy response, you have not reached MCP yet.

3. Reproduce startup in the same environment

For stdio, run the server command directly from the same directory and as the same user as the MCP client. An IDE launch is not an equivalent test because PATH, cwd, runtime, and permissions may differ.

Check:

node --version pwd node ./dist/server.js

pwd does not reveal a secret by itself, but do not publish the path if it contains a user name or a private project name. The server command should either wait for JSON-RPC on stdin or exit with a clear error on stderr. An immediate exit without a message usually indicates a wrong entry point, a missing dependency, or a startup error that was handled without logging.

As of August 23, 2026, the official Inspector CLI documentation requires Node.js 22.19.0 or newer. If the version is lower, stop and switch runtimes before continuing the diagnosis.

4. Separate permissions from authentication

The response code determines the next check:

  • 401 Unauthorized — the credential is missing, expired, or rejected;
  • 403 Forbidden — the identity is recognized but lacks the required permission or scope;
  • 404 — this often means the endpoint or route is wrong, not that permissions are missing;
  • timeout — the server, proxy, or tool did not finish in time; it is not proof of an authentication error.

Do not disable permissions for a smoke test. Create a separate test identity with the minimum scope and select a read-only tool without external effects. The client should leave a human able to reject the call; tool annotations are untrusted data and do not replace policy.

Keep the authentication decision (allowed/denied), scope name, and correlation ID in logs. Remove or mask the credential itself, the Authorization header, and cookies.

5. Validate the capability and inputSchema

The server must declare the tools capability before serving tools/list. Every tool needs a unique name and a valid JSON Schema object in inputSchema. Arguments sent to tools/call must match that schema.

A minimal read-only tool declaration:

{ "name": "echo", "description": "Returns the supplied text unchanged", "inputSchema": { "type": "object", "properties": { "text": { "type": "string" } }, "required": ["text"], "additionalProperties": false } }

Common mistakes are straightforward: no root type: object, a required field missing from properties, the client sends a number instead of a string, an argument name uses the wrong case, or the server advertises two tools with the same name.

After the version and transport agree, test the method with this JSON-RPC payload:

{ "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {} }

Then call exactly one tool:

{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "echo", "arguments": { "text": "MCP_OK_2026" } } }

These snippets show method payloads, not a complete connection bootstrap. In a 2026-07-28 flow, a compatible client adds the required request metadata in _meta; in a legacy flow, initialize happens first. Do not send these JSON snippets manually to a production endpoint without permission.

6. Run a safe test with MCP Inspector CLI

First install Inspector as a pinned project dependency from a trusted lockfile. The --no-install command below will not download an arbitrary current version during diagnosis.

List tools on a local stdio server:

npx --no-install @modelcontextprotocol/inspector --cli node ./dist/server.js --method tools/list

Call echo once:

npx --no-install @modelcontextprotocol/inspector --cli node ./dist/server.js \ --method tools/call \ --tool-name echo \ --tool-arg text=MCP_OK_2026

For a test Streamable HTTP endpoint on loopback:

npx --no-install @modelcontextprotocol/inspector --cli \ http://127.0.0.1:3000/mcp \ --transport http \ --method tools/list

Do not put a token in shell history, a URL, or an article. If the endpoint requires authentication, configure the credential through Inspector’s normal mechanism in the local environment, or stop the test and request a test identity from the server owner. The commands above intentionally contain no real Key.

Symptom → check → fix

SymptomCheck firstMinimal fix
spawn ENOENT or process not foundAbsolute command path, runtime, and the client process PATHPoint to an existing executable or correct the launch environment
Process exits immediatelycwd, entry point, dependencies, and the error on stderrLaunch from the correct directory and return a clear non-zero exit
Client reports a JSON parse errorExtra output on stdout, UTF-8, and newline framingKeep only JSON-RPC on stdout; send logs to stderr
HTTP returns HTML or a redirectMCP URL, proxy, TLS, and the POST routeUse one correct MCP endpoint and fix the proxy rule
Version error before tools/listProtocol era and SDK supportUpgrade the incompatible side or retain an explicit legacy path; do not mix handshakes
401 UnauthorizedCredential presence and expiryObtain a separate test credential through the approved process
403 ForbiddenScope, resource policy, and identityGrant only the required scope to the test identity
tools/list → method/capability errorWhether the tools capability is declaredFix the capability declaration before registering tools
Tool missing from the listUnique name and actual registrationRegister one tool and restart the server
tools/call rejects argumentsinputSchema, types, required fields, and name casingMake arguments conform to the schema; do not loosen it to an arbitrary object
Call hangsTimeout, cancellation, and the tool’s external dependencyReplace the test with a local read-only echo, then test the dependency separately

Acceptance criteria

The integration passes the minimum acceptance test only when all five conditions hold:

  1. Logs or telemetry show the expected negotiated protocol version.
  2. The transport preserves framing: stdio has no extra stdout, and HTTP responds from the MCP endpoint.
  3. tools/list returns one expected tool with a valid inputSchema.
  4. tools/call is actually invoked with text=MCP_OK_2026 and returns MCP_OK_2026 unchanged.
  5. The test did not disable permissions, expose credentials, or trigger an external side effect.

Seeing MCP_OK_2026 in a model response is not enough. You need a recorded tool call with the matching JSON-RPC id, or an Inspector record plus a sanitized server log.

Stop conditions

Stop the diagnosis and do not move to the next layer if:

  • the protocol era supported by either side is unknown;
  • Inspector wants to install an unpinned package version without review;
  • the test requires a production credential, disabled authentication, or broader scope;
  • the only available tool writes to a database, sends a message, changes a file, or runs a command;
  • the HTTP endpoint belongs to a third party and permission to test it is unconfirmed;
  • a Key, token, cookie, personal data, or private resource content appears in logs;
  • tools/list is unstable or returns different schemas between repeated runs;
  • the server crashes before producing a valid JSON-RPC response.

In these cases, preserve the sanitized symptom, client/server/SDK versions, transport, correlation ID, and a minimal error fragment. That is enough to hand the problem to the owner of the correct layer without distributing unnecessary permissions or secrets.

Sources

The specification version, transport details, and minimum Node.js version were verified on August 23, 2026. Check the current official documentation again before repeating the diagnosis after updating the client, server, SDK, or Inspector.

Ready to optimize your LLM workflow?

Join thousands of developers building faster, smarter, and more cost-effective AI applications with BetterToken.