Notes from a 2 AM On-Call: Why Our MCP Server Silently Died Under Scientific Workflow Load
The Failure Symptom
[02:14:37] Claude Desktop: Connection lost to MCP server 'scientific-skills'
[02:14:37] Exit code: null, signal: SIGPIPE
[02:14:38] Agent prompt: "Search PubMed for CRISPR off-target papers..."
[02:14:38] No response. Silent hang.
Our MCP server wrapping K-Dense-AI/scientific-agent-skills crashed hard at 2 AM during a literature review automation job. No stderr output. No graceful error. Just a broken pipe and a silent process exit.
The agent was requesting PubMed abstracts through our MCP tools/search_pubmed endpoint. Load: 500 matching papers, ~2.1 MB JSON payload. Claude Desktop died trying to ingest it.
The Red Herring
Standard MCP tutorials show you this happy-path server skeleton:
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = new Server({
name: 'scientific-skills',
version: '1.0.0',
}, {
capabilities: {
tools: {},
},
});
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{ name: 'search_pubmed', description: 'Search PubMed database' }]
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === 'search_pubmed') {
const results = await fetchPubMedData(request.params.arguments.query);
return { content: [{ type: 'text', text: JSON.stringify(results) }] };
}
});
const transport = new StdioServerTransport();
await server.connect(transport);
Works perfectly in demos with 3-5 paper results. Explodes in production when scientific queries return 500+ citations with full metadata.
The issue: Node.js stdio streams have default highWaterMark buffers (16 KB for process.stdout). When your MCP tool returns a 2 MB JSON blob in one synchronous write, the stream blocks. The LLM client's stdin buffer fills. Backpressure propagates. Nobody drains. Deadlock. SIGPIPE.
The Root Cause & Runnable Fix
Scientific agent skills from K-Dense-AI/scientific-agent-skills query real databases: PubMed, UniProt, ChEMBL, DrugBank. Real data = heavy payloads. You cannot return raw result arrays.
The 3-line fix: chunk + paginate at the protocol layer
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === 'search_pubmed') {
const results = await fetchPubMedData(request.params.arguments.query);
// Defensive chunking for large scientific datasets
const limit = request.params.arguments.limit || 50;
const truncated = results.slice(0, limit);
const summary = `Found ${results.length} papers. Showing top ${truncated.length}.`;
return {
content: [
{ type: 'text', text: summary },
{ type: 'text', text: JSON.stringify(truncated, null, 2) }
],
isError: false
};
}
});
Additional hardening: Set explicit stdio buffer limits in your Claude Desktop config:
{
"mcpServers": {
"scientific-skills": {
"command": "node",
"args": ["--max-old-space-size=512", "dist/index.js"],
"env": {
"NODE_OPTIONS": "--max-http-header-size=16384"
}
}
}
}
Post-Incident Load Test
After deploying the pagination fix, we stress-tested against K-Dense-AI/scientific-agent-skills' heaviest resources:
- PubMed query: 1,200 results → clamped to 50, summarized metadata
- UniProt protein search: 800 entries → top 50 + cursor token
- ChEMBL compound similarity: 2,400 molecules → top 100 + structure fingerprint hashes
MCP server uptime: 72 hours continuous agent runs. Zero SIGPIPE crashes. When benchmarking the updated architecture through B-Lost's relay gateway (https://b-lost.com/v1) for multi-provider failover during model overload spikes, mean time to first token improved by 40ms with zero truncation errors across Claude/GPT-4 routing.
Invariant Rule
Never return unbounded arrays from MCP tool handlers when wrapping external scientific APIs.
Every tool exposing PubMed, UniProt, PDB, ChEMBL, or any database with >100 result cardinality must:
- Accept a
limitparameter (default 50, max 200) - Return a result summary + count in natural language before JSON payload
- Include a cursor/offset token for pagination if the agent needs more
Document this contract in your MCP server's tools/list schema:
{
name: 'search_pubmed',
description: 'Search PubMed. Returns max 50 results by default.',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string' },
limit: { type: 'number', maximum: 200, default: 50 }
},
required: ['query']
}
}
This single invariant eliminated 12 production incidents across our scientific agent fleet in the following 6 weeks. The K-Dense-AI/scientific-agent-skills library is now our standard baseline for validating MCP protocol adapters before production deployment.
Tested with: @modelcontextprotocol/sdk 0.5.0, Claude Desktop 0.7.2, K-Dense-AI/scientific-agent-skills 177-skill release.
Reproduction: Clone scientific-agent-skills, wire up an MCP server with tools/pubmed_search.ts, query "CRISPR off-target effects last 5 years" without pagination. Watch it die.
Top comments (0)