Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 30 additions & 67 deletions src/core/MCPServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ import { HttpStreamTransport } from "../transports/http/server.js";
import { HttpStreamTransportConfig, DEFAULT_HTTP_STREAM_CONFIG } from "../transports/http/types.js";
import { DEFAULT_CORS_CONFIG } from "../transports/sse/types.js";
import { AuthConfig } from "../auth/types.js";
import { createRequire } from 'module';

const require = createRequire(import.meta.url);

function isRequest(msg: any): boolean {
return msg && typeof msg.method === 'string' && msg.jsonrpc === "2.0" && 'id' in msg;
Expand Down Expand Up @@ -219,7 +222,6 @@ export class MCPServer {
private getDefaultName(): string {
const packageJson = this.readPackageJson();
if (packageJson?.name) {
logger.info(`Using name from package.json: ${packageJson.name}`);
return packageJson.name;
}
logger.error("Couldn't find project name in package json");
Expand All @@ -229,7 +231,6 @@ export class MCPServer {
private getDefaultVersion(): string {
const packageJson = this.readPackageJson();
if (packageJson?.version) {
logger.info(`Using version from package.json: ${packageJson.version}`);
return packageJson.version;
}
return "0.0.0";
Expand Down Expand Up @@ -391,14 +392,40 @@ export class MCPServer {
return this.capabilities;
}

private getSdkVersion(): string {
try {
const sdkSpecificFile = require.resolve("@modelcontextprotocol/sdk/server/index.js");

const sdkRootDir = resolve(dirname(sdkSpecificFile), '..', '..', '..');

const correctPackageJsonPath = join(sdkRootDir, "package.json");

const packageContent = readFileSync(correctPackageJsonPath, "utf-8");

const packageJson = JSON.parse(packageContent);

if (packageJson?.version) {
logger.debug(`Found SDK version: ${packageJson.version}`);
return packageJson.version;
} else {
logger.warn("Could not determine SDK version from its package.json.");
return "unknown";
}
} catch (error: any) {
logger.warn(`Failed to read SDK package.json: ${error.message}`);
return "unknown";
}
}

async start() {
try {
if (this.isRunning) {
throw new Error("Server is already running");
}
this.isRunning = true;

logger.info("Starting MCP server...");
const sdkVersion = this.getSdkVersion();
logger.info(`Starting MCP server with SDK ${sdkVersion}...`);

const tools = await this.toolLoader.loadTools();
this.toolsMap = new Map(
Expand All @@ -420,13 +447,10 @@ export class MCPServer {

this.setupHandlers();

logger.info("Creating transport...");
this.transport = this.createTransport();

logger.info(`Connecting transport (${this.transport.type}) to SDK Server...`);
// Let the SDK handle starting the transport through the connect method
await this.server.connect(this.transport);
logger.info(`SDK Server connected to ${this.transport.type} transport.`);

logger.info(`Started ${this.serverName}@${this.serverVersion} successfully on transport ${this.transport.type}`);

Expand Down Expand Up @@ -466,69 +490,8 @@ export class MCPServer {
}
}

private async handleSdkMessage(message: JsonRpcMessage): Promise<void> {
let method = 'response/notification';
let id: JsonRpcId = null;

if (isRequest(message) || isNotification(message)) {
method = (message as any).method;
}
if (isRequest(message) || isResponse(message)) {
id = (message as any).id;
}

logger.debug(`[MCPServer <- Transport] Received: ${method} ${id}`);
logger.debug(`[MCPServer <- Transport] Message Detail: ${JSON.stringify(message)}`);

if (!this.server) {
logger.error("Cannot handle message: SDK Server not initialized.");
if (id !== null && id !== undefined) {
await this.trySendErrorResponse(id, -32005, "Server not fully initialized");
}
return;
}

try {
const sdkMessage = message as unknown as JSONRPCMessage;
const response = await (this.server as any).processMessage(sdkMessage);

if (response) {
const responses = Array.isArray(response) ? response : [response];
logger.debug(`[MCPServer -> Transport] Sending ${responses.length} response(s) for ID ${id ?? 'N/A'}`);

for (const resp of responses) {
logger.debug(`[MCPServer -> Transport] Sending Detail: ${JSON.stringify(resp)}`);
await this.transport?.send(resp);
}
} else {
logger.debug(`[MCPServer] SDK processed ${method} ${id} without direct response.`);
}
} catch (error: any) {
logger.error(`[MCPServer] Error processing message via SDK Server: ${error.message}`);
logger.debug(error.stack);

if (id !== null && id !== undefined) {
await this.trySendErrorResponse(id, -32000, `Internal server error: ${error.message}`);
}
}
}

private async trySendErrorResponse(id: JsonRpcId, code: number, message: string): Promise<void> {
if (!this.transport) return;

const errorResponse: JsonRpcErrorResponse = {
jsonrpc: "2.0",
id: id,
error: { code, message }
};

try {
logger.debug(`[MCPServer -> Transport] Sending Error Response: ${JSON.stringify(errorResponse)}`);
await this.transport.send(errorResponse as unknown as JSONRPCMessage);
} catch (sendError: any) {
logger.error(`[MCPServer -> Transport] Failed to send error response for ID ${id}: ${sendError.message}`);
}
}

async stop() {
if (!this.isRunning) {
Expand Down
6 changes: 1 addition & 5 deletions src/transports/http/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,17 +354,14 @@ export class HttpStreamTransport extends AbstractTransport {
let session: SessionData | undefined;

if (this._config.session.enabled && sessionIdHeader) {
// If a session ID is provided, validate it
session = this.validateSession(sessionIdHeader, req, false);
session.lastActivity = Date.now();
logger.debug(`Found valid session: ${session.id}`);
await this.handleAuthentication(req, res, `GET session ${session.id}`, session);
} else if (this._config.session.enabled) {
// Allow initial GET requests without session ID during initialization phase
logger.debug(`GET request without session ID - allowing as potential initialization connection`);
await this.handleAuthentication(req, res, `GET initialization`, undefined);
} else {
// Sessions disabled
await this.handleAuthentication(req, res, `GET (sessions disabled)`, undefined);
}

Expand Down Expand Up @@ -538,9 +535,8 @@ export class HttpStreamTransport extends AbstractTransport {
throw this.httpError(400, `Bad Request: Missing required session header ${headerName}`, -32601, undefined, requestId);
}
else {
// This is a valid case for initialization or when sessionId is optional
logger.debug(`No session ID provided and not mandatory - acceptable for initialization`);
return undefined as any; // Will be caught by typescript at call site
return undefined as any;
}
}

Expand Down