@sentry/core
Version:
Base implementation for all Sentry JavaScript SDKs
187 lines (162 loc) • 6.12 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const currentScopes = require('../../currentScopes.js');
const object = require('../../utils/object.js');
const trace = require('../../tracing/trace.js');
const correlation = require('./correlation.js');
const errorCapture = require('./errorCapture.js');
const sessionExtraction = require('./sessionExtraction.js');
const sessionManagement = require('./sessionManagement.js');
const spans = require('./spans.js');
const validation = require('./validation.js');
/**
* Transport layer instrumentation for MCP server
*
* Handles message interception and response correlation.
* @see https://modelcontextprotocol.io/specification/2025-06-18/basic/transports
*/
/**
* Wraps transport.onmessage to create spans for incoming messages.
* For "initialize" requests, extracts and stores client info and protocol version
* in the session data for the transport.
* @param transport - MCP transport instance to wrap
*/
function wrapTransportOnMessage(transport) {
if (transport.onmessage) {
object.fill(transport, 'onmessage', originalOnMessage => {
return function ( message, extra) {
if (validation.isJsonRpcRequest(message)) {
if (message.method === 'initialize') {
try {
const sessionData = sessionExtraction.extractSessionDataFromInitializeRequest(message);
sessionManagement.storeSessionDataForTransport(this, sessionData);
} catch {
// noop
}
}
const isolationScope = currentScopes.getIsolationScope().clone();
return currentScopes.withIsolationScope(isolationScope, () => {
const spanConfig = spans.buildMcpServerSpanConfig(message, this, extra );
const span = trace.startInactiveSpan(spanConfig);
correlation.storeSpanForRequest(this, message.id, span, message.method);
return trace.withActiveSpan(span, () => {
return (originalOnMessage ).call(this, message, extra);
});
});
}
if (validation.isJsonRpcNotification(message)) {
return spans.createMcpNotificationSpan(message, this, extra , () => {
return (originalOnMessage ).call(this, message, extra);
});
}
return (originalOnMessage ).call(this, message, extra);
};
});
}
}
/**
* Wraps transport.send to handle outgoing messages and response correlation.
* For "initialize" responses, extracts and stores protocol version and server info
* in the session data for the transport.
* @param transport - MCP transport instance to wrap
*/
function wrapTransportSend(transport) {
if (transport.send) {
object.fill(transport, 'send', originalSend => {
return async function ( ...args) {
const [message] = args;
if (validation.isJsonRpcNotification(message)) {
return spans.createMcpOutgoingNotificationSpan(message, this, () => {
return (originalSend ).call(this, ...args);
});
}
if (validation.isJsonRpcResponse(message)) {
if (message.id !== null && message.id !== undefined) {
if (message.error) {
captureJsonRpcErrorResponse(message.error);
}
if (validation.isValidContentItem(message.result)) {
if (message.result.protocolVersion || message.result.serverInfo) {
try {
const serverData = sessionExtraction.extractSessionDataFromInitializeResponse(message.result);
sessionManagement.updateSessionDataForTransport(this, serverData);
} catch {
// noop
}
}
}
correlation.completeSpanWithResults(this, message.id, message.result);
}
}
return (originalSend ).call(this, ...args);
};
});
}
}
/**
* Wraps transport.onclose to clean up pending spans for this transport only
* @param transport - MCP transport instance to wrap
*/
function wrapTransportOnClose(transport) {
if (transport.onclose) {
object.fill(transport, 'onclose', originalOnClose => {
return function ( ...args) {
correlation.cleanupPendingSpansForTransport(this);
sessionManagement.cleanupSessionDataForTransport(this);
return (originalOnClose ).call(this, ...args);
};
});
}
}
/**
* Wraps transport error handlers to capture connection errors
* @param transport - MCP transport instance to wrap
*/
function wrapTransportError(transport) {
if (transport.onerror) {
object.fill(transport, 'onerror', (originalOnError) => {
return function ( error) {
captureTransportError(error);
return originalOnError.call(this, error);
};
});
}
}
/**
* Captures JSON-RPC error responses for server-side errors.
* @see https://www.jsonrpc.org/specification#error_object
* @internal
* @param errorResponse - JSON-RPC error response
*/
function captureJsonRpcErrorResponse(errorResponse) {
try {
if (errorResponse && typeof errorResponse === 'object' && 'code' in errorResponse && 'message' in errorResponse) {
const jsonRpcError = errorResponse ;
const isServerError =
jsonRpcError.code === -32603 || (jsonRpcError.code >= -32099 && jsonRpcError.code <= -32000);
if (isServerError) {
const error = new Error(jsonRpcError.message);
error.name = `JsonRpcError_${jsonRpcError.code}`;
errorCapture.captureError(error, 'protocol');
}
}
} catch {
// noop
}
}
/**
* Captures transport connection errors
* @internal
* @param error - Transport error
*/
function captureTransportError(error) {
try {
errorCapture.captureError(error, 'transport');
} catch {
// noop
}
}
exports.wrapTransportError = wrapTransportError;
exports.wrapTransportOnClose = wrapTransportOnClose;
exports.wrapTransportOnMessage = wrapTransportOnMessage;
exports.wrapTransportSend = wrapTransportSend;
//# sourceMappingURL=transport.js.map