MCP defines a structured logging protocol via notifications/message with severity levels (debug, info, warning, error, etc.) that clients can display, filter, and route to observability systems. A server that only uses console.error keeps all operational visibility on stderr — invisible to the connected AI client and to any MCP-aware monitoring tool. When a server-side failure occurs during an agentic run, the client has no programmatic signal to surface the failure; the AI sees only the missing tool result with no explanation.
Low because the absence of MCP-native logging reduces observability without causing data loss or security failures — clients still function, but operational diagnosis is harder.
Use server.sendLoggingMessage() for events the client should see. Keep console.error for local debugging only.
// src/utils/logger.ts — MCP-native logging
import { server } from '../index.js' // your McpServer instance
export function log(level: 'info' | 'warning' | 'error', message: string) {
server.sendLoggingMessage({ level, logger: 'my-server', data: message })
}
// In tool handlers:
log('info', 'Connected to database successfully')
log('error', `Failed to connect: ${error.message}`) // client sees this
Declare logging: {} in your server capabilities so clients know to expect log messages.
ID: mcp-server.security-capabilities.mcp-logging
Severity: low
What to look for: Count all logging statements. Enumerate which use MCP logging notifications (notifications/message) vs. console.log or other non-MCP logging. The MCP spec provides a structured logging mechanism via notifications/message with severity levels (debug, info, notice, warning, error, critical, alert, emergency). Check whether the server uses this mechanism for logging instead of (or in addition to) console.error. Look for server.sendLoggingMessage() or equivalent SDK method. This is primarily important for servers that need to communicate status to the client without corrupting tool output.
Pass criteria: The server uses MCP's notifications/message for logging important events. If console.error is also used (for local debugging), it does not replace structured MCP logging for client-facing messages. At least 50% of logging should use MCP logging notifications for client visibility.
Fail criteria: The server only uses console.error for logging with no MCP-native logging, or important status/error messages are only visible via stderr and never sent to the client.
Skip (N/A) when: The server has no logging needs (simple, stateless tools). All checks skip when no MCP server is detected.
Cross-reference: For credential leak prevention in logs, see no-credential-leaks.
Detail on fail: "Server logs errors only via console.error — MCP clients have no visibility into server-side issues. Use server.sendLoggingMessage() to surface important messages to the client" or "Server declares logging capability but never calls sendLoggingMessage"
Remediation: Use MCP's native logging for client-visible messages:
// src/utils/logger.ts — MCP logging notification
server.sendLoggingMessage({ level: "info", logger: "tool-handler", data: "Processing search query" })
// Use MCP logging for messages the client should see
server.sendLoggingMessage({
level: 'info',
logger: 'my-server',
data: 'Connected to database successfully',
})
// console.error is fine for local debugging, but use MCP logging
// for messages that clients need to see
server.sendLoggingMessage({
level: 'error',
logger: 'my-server',
data: `Failed to connect to database: ${error.message}`,
})