Troubleshooting & FAQ
Connection issues
Server not showing in Claude Desktop
Register the server:
bashnpx @airmcp-dev/cli connect claude-desktopRestart Claude Desktop. Config is read at startup only.
Check config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%/Claude/claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
Verify the entry exists and uses absolute paths:
json{ "mcpServers": { "my-server": { "command": "npx", "args": ["tsx", "/absolute/path/to/my-server/src/index.ts"] } } }- macOS:
For SSE/HTTP, verify server is running:
curl http://localhost:3510/Use absolute paths. Claude Desktop doesn't run in a shell environment — relative paths won't work.
Cursor/VS Code connection fails
- Register:
npx @airmcp-dev/cli connect cursororconnect vscode - Restart the IDE. Required after config changes.
- VS Code: Ensure MCP extension is installed.
- Cursor config path:
- macOS:
~/Library/Application Support/Cursor/User/globalStorage/cursor.mcp/config.json - Windows:
%APPDATA%/Cursor/User/globalStorage/cursor.mcp/config.json
- macOS:
No logs in stdio mode
Expected. In stdio transport, stdout is reserved for MCP protocol. All logs go to stderr.
node dist/index.js 2>server.log
node dist/index.js 2>&1 | tee server.logWARNING
Using console.log() in stdio mode will break the MCP protocol. Use air's built-in logger or console.error().
SSE connection keeps dropping
Reverse proxies may close long-lived connections:
proxy_read_timeout 86400;
proxy_send_timeout 86400;
proxy_buffering off;
proxy_cache off;
proxy_set_header Connection "";Cloudflare may drop SSE after ~100 seconds. Switch to http transport in that case.
TIP
Use http transport behind reverse proxies. HTTP request-response is more proxy-friendly.
"Server not responding" error
- Check server status:
npx @airmcp-dev/cli status - Check port conflicts:
lsof -i :3510(macOS/Linux) ornetstat -ano | findstr :3510(Windows) - Check firewall. Local firewalls may block the port.
Development issues
Entry file not found with dev command
Search order: src/index.ts → src/index.js → index.ts → index.js. Run from the project root:
ls src/index.ts # Must exist
npx @airmcp-dev/cli devHot reload not working
dev watches src/ for .ts, .js, .json files only.
Checklist:
- Edited file is inside
src/ .envchanges are not detected (restart needed)- 300ms debounce — wait briefly after save
- WSL:
fs.watchmay not work across filesystem boundaries
TypeScript compile errors
air requires ESM. In tsconfig.json:
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022",
"outDir": "dist"
}
}In package.json: "type": "module"
Cannot find module error
ESM requires .js extension in imports:
// ❌ Error
import { myHelper } from './utils/helper';
// ✅ Works
import { myHelper } from './utils/helper.js';Use .js even for TypeScript files. This is a Node.js ESM rule.
top-level await error
createStorage needs await. Top-level await requires "module": "NodeNext" in tsconfig. Or wrap in async IIFE:
(async () => {
const store = await createStorage({ type: 'file' });
const server = defineServer({ ... });
server.start();
})();Plugin issues
Why does plugin order matter?
use array order = middleware execution order. Recommended:
use: [
authPlugin(), // 1. Auth first (reject early)
sanitizerPlugin(), // 2. Sanitize input
timeoutPlugin(), // 3. Timeout
retryPlugin(), // 4. Retry (within timeout)
cachePlugin(), // 5. Cache (check before retry)
queuePlugin(), // 6. Concurrency limit
]cachePlugin not working
- Cache key: Same tool + same params = hit. Any param difference = miss. Param key sort order matters too.
excludelist: Check if the tool is excluded.- TTL: Default 60s. Expired entries return miss.
maxEntries: Default 1000. Excess entries evicted FIFO.
authPlugin _auth parameter not reaching server
MCP protocol strips params not in schema. Define _auth in tool params:
params: { query: 'string', _auth: { type: 'string', optional: true } }INFO
This is MCP SDK behavior, not an air limitation. dryrunPlugin's perCall mode has the same issue.
retryPlugin not retrying
retryOnfilter: Default retries all errors. Custom filters may exclude the error.maxRetries: Default 3. May have already exhausted retries.- Error must be thrown:
return { error: '...' }is a normal response, not retried. Usethrowfor retry.
circuitBreakerPlugin blocking all tools
Check perTool option:
circuitBreakerPlugin({ perTool: true }) // Default: independent per tool
circuitBreakerPlugin({ perTool: false }) // Global: one failure affects allStorage issues
FileStore data not persisting
FileStore flushes every 5s. Force-killing (kill -9) loses last ~5s. Always use:
onShutdown(async () => { await store.close(); });TTL unit confusion
cachePlugin({ ttlMs: 60000 })— millisecondsstore.set('ns', 'key', value, 3600)— seconds
Rule: option names with Ms suffix = milliseconds. Without = seconds.
MemoryStore data disappears on restart
Expected. MemoryStore is ephemeral. Use FileStore for persistence:
const store = await createStorage({ type: 'file', path: '.air/data' });FileStore concurrent access
FileStore is single-process only. Multiple processes sharing the same directory causes data corruption. For multi-process, use an external DB (PostgreSQL, Redis, etc.).
Deploy issues
FileStore fails on Cloudflare Workers
Workers have no persistent filesystem. Use MemoryStore or Cloudflare KV/D1.
Lambda FileStore initialization error
Lambda's /tmp is writable but ephemeral:
const store = await createStorage({ type: 'file', path: '/tmp/air-data' });Docker .air/data permission error
RUN mkdir -p /app/.air/data && chown -R node:node /app/.air
USER nodePM2 keeps restarting
- Node.js 18+ required.
- ESM:
"type": "module"in package.json. - Ecosystem file:javascript
// ecosystem.config.cjs module.exports = { apps: [{ name: 'mcp', script: 'dist/index.js', env: { NODE_ENV: 'production', MCP_TRANSPORT: 'http', PORT: '3510' } }], };
Performance
Meter Ring Buffer full?
Max 10,000 records. Oldest auto-evicted. Constant memory. For more history, use webhookPlugin or jsonLoggerPlugin to stream to external systems.
Many tools slow?
No impact up to 100+ tools. Tool lookup is Map O(1). Chain time scales with middleware count, not tool count.
Memory keeps growing
- cachePlugin
maxEntries: Default 1000. Large responses consume memory fast. - dedupPlugin inflight: Large
windowMsgrows the map. Default 1000ms is fine. - FileStore cache: Many namespaces = many in-memory caches.
Gateway issues
Duplicate tools in Gateway
Expected when multiple servers provide the same tool. Gateway's Tool Index handles routing. For unique tools, use distinct names per server.
Server stuck in "error" status
Health Checker runs every healthCheckInterval (default 15s). Server auto-recovers on next successful check. If not recovering, check server logs.
Other issues
If your problem isn't listed here, please report it on GitHub Issues:
github.com/airmcp-dev/air/issues
When filing an issue, please include:
- air version (
npx @airmcp-dev/cli license) - Node.js version (
node -v) - OS and environment (macOS/Windows/Linux, Docker)
- Transport type (stdio/SSE/HTTP)
- MCP client (Claude Desktop, Cursor, VS Code, etc.)
- Full error message
- Minimal reproduction code