Skip to content

fix: follow spec #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 28, 2025
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
15 changes: 8 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "mcp-framework",
"version": "0.2.4",

"description": "Framework for building Model Context Protocol (MCP) servers in Typescript",
"type": "module",
"author": "Alex Andru <[email protected]>",
Expand Down Expand Up @@ -58,7 +57,7 @@
"@types/content-type": "^1.1.8",
"@types/jest": "^29.5.12",
"@types/jsonwebtoken": "^9.0.8",
"@types/node": "^20.11.24",
"@types/node": "^20.17.28",
"jest": "^29.7.0",
"ts-jest": "^29.1.2"
}
Expand Down
55 changes: 30 additions & 25 deletions src/core/MCPServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,16 @@ export interface MCPServerConfig {

export type ServerCapabilities = {
tools?: {
enabled: true;
};
schemas?: {
enabled: true;
listChanged?: true; // Optional: Indicates support for list change notifications
};
prompts?: {
enabled: true;
listChanged?: true; // Optional: Indicates support for list change notifications
};
resources?: {
enabled: true;
listChanged?: true; // Optional: Indicates support for list change notifications
subscribe?: true; // Optional: Indicates support for resource subscriptions
};
// Other standard capabilities like 'logging' or 'completion' could be added here if supported
};

export class MCPServer {
Expand All @@ -83,9 +82,7 @@ export class MCPServer {
private serverVersion: string;
private basePath: string;
private transportConfig: TransportConfig;
private capabilities: ServerCapabilities = {
tools: { enabled: true }
};
private capabilities: ServerCapabilities = {}; // Initialize as empty
private isRunning: boolean = false;
private transport?: BaseTransport;
private shutdownPromise?: Promise<void>;
Expand Down Expand Up @@ -188,11 +185,11 @@ export class MCPServer {
});
}
};
// Removed misplaced semicolon from previous line

transport.onerror = (error) => {
logger.error(`Transport (${transport.type}) error: ${error.message}\n${error.stack}`);
};

transport.onerror = (error: Error) => {
logger.error(`Transport (${transport.type}) error: ${error.message}\n${error.stack}`);
};
return transport;
}

Expand Down Expand Up @@ -236,26 +233,28 @@ export class MCPServer {
}

private setupHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async (request) => {
// TODO: Replace 'any' with the specific inferred request type from the SDK schema if available
this.server.setRequestHandler(ListToolsRequestSchema, async (request: any) => {
logger.debug(`Received ListTools request: ${JSON.stringify(request)}`);

const tools = Array.from(this.toolsMap.values()).map(
(tool) => tool.toolDefinition
);

logger.debug(`Found ${tools.length} tools to return`);
logger.debug(`Tool definitions: ${JSON.stringify(tools)}`);

const response = {
tools: tools,
nextCursor: undefined
};

logger.debug(`Sending ListTools response: ${JSON.stringify(response)}`);
return response;
});

this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
// TODO: Replace 'any' with the specific inferred request type from the SDK schema if available
this.server.setRequestHandler(CallToolRequestSchema, async (request: any) => {
logger.debug(`Tool call request received for: ${request.params.name}`);
logger.debug(`Tool call arguments: ${JSON.stringify(request.params.arguments)}`);

Expand Down Expand Up @@ -285,6 +284,7 @@ export class MCPServer {
});

if (this.capabilities.prompts) {
// No request parameter for ListPrompts
this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
return {
prompts: Array.from(this.promptsMap.values()).map(
Expand All @@ -293,7 +293,8 @@ export class MCPServer {
};
});

this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
// TODO: Replace 'any' with the specific inferred request type from the SDK schema if available
this.server.setRequestHandler(GetPromptRequestSchema, async (request: any) => {
const prompt = this.promptsMap.get(request.params.name);
if (!prompt) {
throw new Error(
Expand All @@ -312,6 +313,7 @@ export class MCPServer {
}

if (this.capabilities.resources) {
// No request parameter for ListResources
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: Array.from(this.resourcesMap.values()).map(
Expand All @@ -320,9 +322,10 @@ export class MCPServer {
};
});

// TODO: Replace 'any' with the specific inferred request type from the SDK schema if available
this.server.setRequestHandler(
ReadResourceRequestSchema,
async (request) => {
async (request: any) => {
const resource = this.resourcesMap.get(request.params.uri);
if (!resource) {
throw new Error(
Expand All @@ -340,7 +343,8 @@ export class MCPServer {
}
);

this.server.setRequestHandler(SubscribeRequestSchema, async (request) => {
// TODO: Replace 'any' with the specific inferred request type from the SDK schema if available
this.server.setRequestHandler(SubscribeRequestSchema, async (request: any) => {
const resource = this.resourcesMap.get(request.params.uri);
if (!resource) {
throw new Error(`Unknown resource: ${request.params.uri}`);
Expand All @@ -356,7 +360,8 @@ export class MCPServer {
return {};
});

this.server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
// TODO: Replace 'any' with the specific inferred request type from the SDK schema if available
this.server.setRequestHandler(UnsubscribeRequestSchema, async (request: any) => {
const resource = this.resourcesMap.get(request.params.uri);
if (!resource) {
throw new Error(`Unknown resource: ${request.params.uri}`);
Expand All @@ -376,12 +381,12 @@ export class MCPServer {

private async detectCapabilities(): Promise<ServerCapabilities> {
if (await this.promptLoader.hasPrompts()) {
this.capabilities.prompts = { enabled: true };
this.capabilities.prompts = {}; // Indicate capability exists, but don't claim listChanged
logger.debug("Prompts capability enabled");
}

if (await this.resourceLoader.hasResources()) {
this.capabilities.resources = { enabled: true };
this.capabilities.resources = {}; // Indicate capability exists, but don't claim listChanged/subscribe
logger.debug("Resources capability enabled");
}

Expand Down
10 changes: 10 additions & 0 deletions src/transports/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ import { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
* Base transport interface
*/
export interface BaseTransport extends Transport {
// Properties from SDK Transport (explicitly listed for clarity/safety)
onclose?: (() => void) | undefined;
onerror?: ((error: Error) => void) | undefined;
onmessage?: ((message: JSONRPCMessage) => void) | undefined;

// Methods from SDK Transport (explicitly listed for clarity/safety)
send(message: JSONRPCMessage): Promise<void>;
close(): Promise<void>;
start(): Promise<void>; // Assuming start is also needed, mirroring AbstractTransport

/**
* The type of transport (e.g., "stdio", "sse")
*/
Expand Down