Skip to content

Harden server stability and fix MCP protocol hygiene#7

Merged
jack-arturo merged 1 commit into
mainfrom
fix/server-stability
Jun 11, 2026
Merged

Harden server stability and fix MCP protocol hygiene#7
jack-arturo merged 1 commit into
mainfrom
fix/server-stability

Conversation

@jack-arturo

Copy link
Copy Markdown
Member

Problem

The WordPress server intermittently shows as disconnected in Claude Desktop. Audit of server.ts + Desktop logs found real protocol bugs and a fragile failure mode:

  1. ~65KB initialize responses: capabilities.tools was stuffed with full tool objects, serializing zod schema internals ("vendor":"zod", ZodOptional, …) into every connection handshake.
  2. No tool descriptions, ever: server.tool() was called without the description argument, so tools/list served all 43 tools with names and schemas only. Clients have been selecting tools blind.
  3. unhandledRejectionprocess.exit(1): any stray background rejection killed the server — exactly what a user sees as "server disconnected, tools gone until restart".
  4. No exit-reason logging, so disconnects left no server-side trace to correlate.

Fix

  • Spec-correct capabilities: { tools: {} }; advertisement via registration. Initialize: 65KB → 164 bytes.
  • Descriptions passed to server.tool(): 43/43 tools now describe themselves in tools/list.
  • Log-and-continue on unhandledRejection (with stack); uncaughtException still exits but logs the stack first; [SHUTDOWN] Process exiting with code N trace on every exit.
  • cli.ts startup banner moved to stderr (stdout belongs to the inherited JSON-RPC channel).
  • New CI workflow with job test (npm ci, tsc --noEmit, build) — the status check the org MCP template expects.

Verified

Stdio handshake probe against the built server: initialize 164 bytes with {"tools":{"listChanged":true}}, zero zod internals, 43/43 tool descriptions present, SIGTERM produces [SHUTDOWN] traces.

🤖 Generated with Claude Code

Addresses the WordPress server intermittently showing as disconnected in
Claude Desktop, and two protocol-level bugs found while auditing it:

- capabilities.tools was a map of full tool objects, which serialized every
  tool's zod schema internals into the initialize response (~65KB per
  connection). It's now the spec-correct capability flags object; tool
  advertisement happens through server.tool() registration. initialize
  drops to ~164 bytes.
- server.tool() was never passed the tool description, so tools/list
  served all 43 tools with no descriptions at all — clients picked tools
  on names alone. Descriptions are now registered.
- unhandledRejection no longer kills the process: a stray background
  rejection surfaced to clients as an unexplained disconnect. It now logs
  loudly (with stack) and the server continues. uncaughtException still
  exits but logs the full stack first.
- The process logs an exit trace ([SHUTDOWN] code N) so future
  "transport closed unexpectedly" reports can be correlated with a cause
  on the server side.
- cli.ts wrote its startup banner to stdout, which belongs to the
  JSON-RPC channel of the inherited-stdio child server; moved to stderr.
- Add CI workflow (job "test": npm ci, tsc --noEmit, build) matching the
  org template's expected status check.

Verified by stdio handshake probe: initialize 164 bytes, zero zod
internals, 43/43 tools with descriptions, SIGTERM leaves shutdown traces.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 11, 2026 01:56
@jack-arturo jack-arturo merged commit 367c4d6 into main Jun 11, 2026
1 of 2 checks passed
@jack-arturo jack-arturo deleted the fix/server-stability branch June 11, 2026 01:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the WordPress MCP server’s protocol compliance and runtime stability to reduce “disconnected” events in MCP clients and improve tool discoverability.

Changes:

  • Fixes MCP initialize capability advertisement to avoid embedding full tool definitions (and zod internals) in the handshake payload.
  • Registers tools with descriptions and improves process-level error/shutdown logging behavior.
  • Moves the CLI startup banner to stderr and adds a new GitHub Actions CI workflow.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/server.ts Corrects MCP capabilities/tool registration behavior and improves error/shutdown logging.
src/cli.ts Ensures startup banner goes to stderr to keep stdout clean for JSON-RPC.
.github/workflows/ci.yml Adds CI workflow to run TypeScript checks and build.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/ci.yml
with:
node-version: 22
cache: npm
- run: npm ci
Comment thread src/server.ts
Comment on lines +104 to +105
process.on('exit', (code) => {
process.stderr.write(`[SHUTDOWN] Process exiting with code ${code}\n`);
Comment thread src/server.ts
Comment on lines +46 to +47
const zodSchema = z.object(tool.inputSchema.properties as z.ZodRawShape);
server.tool(tool.name, tool.description ?? '', zodSchema.shape, wrappedHandler)
Comment thread src/server.ts
Comment on lines 22 to 24
capabilities: {
tools: allTools.reduce((acc, tool) => {
acc[tool.name] = tool;
return acc;
}, {} as Record<string, any>)
tools: {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants