---
title: "Event Handling"
description: "Handle events and manage state in your MCP server"
---
MCP servers process various events including requests, notifications, and connection lifecycle events. This guide explains how to implement event handlers effectively in your server.
## Request handlers
Request handlers process incoming requests from clients and must return a response. Each handler is associated with a specific method name.
```typescript server.ts
import { Server } from "@modelcontextprotocol/sdk/server";
import { ListToolsRequestSchema, ListToolsResultSchema } from "@modelcontextprotocol/sdk/types";
const server = new Server({
name: "example-server",
version: "1.0.0",
});
server.setRequestHandler(ListToolsRequestSchema, async (request) => {
return {
tools: [
{
name: "example-tool",
description: "An example tool",
inputSchema: {
type: "object",
properties: {
message: { type: "string" },
},
},
},
],
};
});
```
```python server.py
from mcp import Server
from mcp.types import ListToolsRequest, ListToolsResult, Tool
server = Server(name="example-server", version="1.0.0")
@server.request_handler("tools/list")
async def handle_list_tools(request: ListToolsRequest) -> ListToolsResult:
return ListToolsResult(
tools=[
Tool(
name="example-tool",
description="An example tool",
input_schema={
"type": "object",
"properties": {
"message": {"type": "string"}
}
}
)
]
)
```
## Notification handlers
Notification handlers process one-way messages that don't require responses. They're useful for handling events like resource updates or logging messages.
```typescript notifications.ts
import { ResourceUpdatedNotificationSchema } from "@modelcontextprotocol/sdk/types";
server.setNotificationHandler(ResourceUpdatedNotificationSchema, async (notification) => {
const { uri } = notification.params;
console.log(`Resource updated: ${uri}`);
});
```
```python notifications.py
from mcp.types import ResourceUpdatedNotification
@server.notification_handler("notifications/resources/updated")
async def handle_resource_updated(notification: ResourceUpdatedNotification):
uri = notification.params.uri
print(f"Resource updated: {uri}")
```
## Connection lifecycle
Your server can handle connection lifecycle events through callbacks. This is useful for setup and cleanup operations.
```typescript lifecycle.ts
const server = new Server({
name: "example-server",
version: "1.0.0",
});
server.oninitialized = () => {
console.log("Client has completed initialization");
};
server.onclose = () => {
console.log("Connection closed");
// Perform cleanup
};
```
```python lifecycle.py
server = Server(name="example-server", version="1.0.0")
@server.on_initialized
async def handle_initialized():
print("Client has completed initialization")
@server.on_close
async def handle_close():
print("Connection closed")
# Perform cleanup
```
## Error handling
Properly handle errors in your event handlers to provide meaningful feedback to clients.
```typescript errors.ts
import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types";
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
// Attempt to call tool
const result = await callTool(request.params);
return { toolResult: result };
} catch (error) {
throw new McpError(
ErrorCode.InvalidParams,
"Failed to execute tool",
{ details: error.message }
);
}
});
```
```python errors.py
from mcp.types import CallToolRequest, McpError, ErrorCode
@server.request_handler("tools/call")
async def handle_call_tool(request: CallToolRequest):
try:
# Attempt to call tool
result = await call_tool(request.params)
return {"toolResult": result}
except Exception as e:
raise McpError(
code=ErrorCode.INVALID_PARAMS,
message="Failed to execute tool",
data={"details": str(e)}
)
```
## Progress reporting
For long-running operations, you can report progress back to the client.
```typescript progress.ts
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const progressToken = request.params._meta?.progressToken;
if (progressToken) {
await server.notification({
method: "notifications/progress",
params: {
progressToken,
progress: 50,
total: 100
}
});
}
// Complete the operation
return { toolResult: "Operation completed" };
});
```
```python progress.py
@server.request_handler("tools/call")
async def handle_call_tool(request: CallToolRequest):
progress_token = request.params._meta.progress_token if request.params._meta else None
if progress_token:
await server.notify(
"notifications/progress",
{
"progressToken": progress_token,
"progress": 50,
"total": 100
}
)
# Complete the operation
return {"toolResult": "Operation completed"}
```
## State management
While MCP servers are generally stateless, you might need to maintain some state between requests. Here's how to manage state safely:
```typescript state.ts
class ExampleServer {
private subscriptions: Set = new Set();
private server: Server;
constructor() {
this.server = new Server({
name: "example-server",
version: "1.0.0",
});
this.server.setRequestHandler(SubscribeRequestSchema, async (request) => {
const { uri } = request.params;
this.subscriptions.add(uri);
return {};
});
this.server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
const { uri } = request.params;
this.subscriptions.delete(uri);
return {};
});
this.server.onclose = () => {
this.subscriptions.clear();
};
}
}
```
```python state.py
class ExampleServer:
def __init__(self):
self.subscriptions = set()
self.server = Server(name="example-server", version="1.0.0")
@self.server.request_handler("resources/subscribe")
async def handle_subscribe(request: SubscribeRequest):
self.subscriptions.add(request.params.uri)
return {}
@self.server.request_handler("resources/unsubscribe")
async def handle_unsubscribe(request: UnsubscribeRequest):
self.subscriptions.remove(request.params.uri)
return {}
@self.server.on_close
async def handle_close():
self.subscriptions.clear()
```
## Best practices
When implementing event handlers in your MCP server:
Always validate request parameters before processing
Use appropriate error codes and provide helpful error messages
Handle long-running operations asynchronously and report progress
Clean up resources and state when connections are closed
## Debugging tips
To help debug your event handlers:
1. Enable logging in your server:
```typescript logging.ts
server.setRequestHandler(SetLevelRequestSchema, async (request) => {
const { level } = request.params;
server.notification({
method: "notifications/message",
params: {
level: "debug",
data: `Setting log level to: ${level}`
}
});
return {};
});
```
```python logging.py
@server.request_handler("logging/setLevel")
async def handle_set_level(request: SetLevelRequest):
level = request.params.level
await server.notify(
"notifications/message",
{
"level": "debug",
"data": f"Setting log level to: {level}"
}
)
return {}
```
2. Use the progress reporting mechanism to debug long-running operations
3. Implement comprehensive error handling with detailed error messages
4. Add request/response logging for debugging complex interactions
Remember that logging and debugging information should be appropriate for the execution environment and should never expose sensitive information.