What are Sessions?
Sessions in FastMCP represent a connection between a client and your MCP server. Each session has its own lifecycle, authentication context, and event stream. Understanding sessions is crucial for building stateful MCP servers.
Session Lifecycle
A session goes through several stages:
- Connection - Client connects to the server
- Ready - Session is initialized and ready to handle requests
- Active - Session is processing requests
- Disconnect - Client disconnects or connection is lost
Session Events
Listen to session events to track connections and manage state:
Connect Event
import { FastMCP } from "fastmcp";
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
});
server.on("connect", (event) => {
const { session } = event;
console.log("Client connected");
console.log("Session ID:", session.sessionId);
console.log("Client version:", session.clientVersion);
});
Disconnect Event
server.on("disconnect", (event) => {
const { session } = event;
console.log("Client disconnected");
console.log("Session ID:", session.sessionId);
});
Ready Event
server.on("connect", (event) => {
const { session } = event;
session.on("ready", () => {
console.log("Session is ready to handle requests");
});
});
Error Event
server.on("connect", (event) => {
const { session } = event;
session.on("error", (event) => {
console.error("Session error:", event.error);
});
});
Session Context
The session context is available in tool execution, resource loading, and prompt generation:
import { z } from "zod";
server.addTool({
name: "whoami",
description: "Get current user information",
execute: async (args, context) => {
if (!context.session) {
return "No session context available";
}
const { session } = context;
return `User ID: ${session.userId}\nUsername: ${session.username}\nRole: ${session.role}`;
},
});
In Resources
server.addResource({
uri: "session://current-user",
name: "Current User Information",
mimeType: "application/json",
load: async (auth) => {
if (!auth) {
return {
text: JSON.stringify({ authenticated: false }),
};
}
return {
text: JSON.stringify({
authenticated: true,
user: {
id: auth.userId,
username: auth.username,
role: auth.role,
},
}),
};
},
});
In Prompts
server.addPrompt({
name: "personalized-greeting",
description: "Generate a personalized greeting",
load: async (args, auth) => {
if (!auth) {
return "Hello! Please authenticate to get a personalized greeting.";
}
return `Hello ${auth.username}! You're logged in as a ${auth.role}.`;
},
});
Session IDs
Session IDs help track individual client connections:
HTTP-based Transports
For HTTP Stream and SSE transports, the session ID comes from the Mcp-Session-Id header:
server.addTool({
name: "trackSession",
description: "Track session activity",
execute: async (args, context) => {
const { sessionId } = context;
if (sessionId) {
// Track activity for this specific session
await logActivity(sessionId, "tool_called");
}
return "Activity tracked";
},
});
Request IDs
Track individual requests within a session:
server.addTool({
name: "trackedOperation",
description: "An operation with request tracking",
execute: async (args, context) => {
const { requestId, sessionId } = context;
console.log(`Processing request ${requestId} in session ${sessionId}`);
return "Operation complete";
},
});
Authentication Context
Define custom session authentication types:
interface UserSession {
userId: string;
username: string;
role: "admin" | "user" | "guest";
permissions: string[];
}
const server = new FastMCP<UserSession>({
name: "My Server",
version: "1.0.0",
authenticate: async (request) => {
// For stdio transport (no request object)
if (!request) {
return {
userId: process.env.USER_ID || "default",
username: process.env.USERNAME || "Anonymous",
role: "guest",
permissions: ["read"],
};
}
// For HTTP transports
const authHeader = request.headers["authorization"];
if (!authHeader?.startsWith("Bearer ")) {
throw new Response("Unauthorized", { status: 401 });
}
const token = authHeader.substring(7);
const user = await validateToken(token);
return {
userId: user.id,
username: user.name,
role: user.role,
permissions: user.permissions,
};
},
});
Roots Management
Roots allow clients to provide filesystem-like root locations:
Accessing Roots
server.on("connect", (event) => {
const { session } = event;
// Access initial roots
console.log("Initial roots:", session.roots);
// Listen for changes
session.on("rootsChanged", (event) => {
console.log("Roots updated:", event.roots);
});
});
server.addTool({
name: "listRoots",
description: "List available root directories",
execute: async (args, context) => {
// Roots are available through the session
// This would need to be passed through context
// or stored in session state
return "Roots listed";
},
});
Disabling Roots
You can disable roots support:
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
roots: {
enabled: false,
},
});
Disabling roots is useful for better compatibility with clients that don’t support the roots capability.
Session State Management
Track state across multiple requests in a session:
const sessionState = new Map<string, any>();
server.on("connect", (event) => {
const { session } = event;
const sessionId = session.sessionId || "default";
// Initialize session state
sessionState.set(sessionId, {
requestCount: 0,
startTime: Date.now(),
});
});
server.on("disconnect", (event) => {
const { session } = event;
const sessionId = session.sessionId || "default";
// Clean up session state
sessionState.delete(sessionId);
});
server.addTool({
name: "getSessionStats",
description: "Get statistics for the current session",
execute: async (args, context) => {
const sessionId = context.sessionId || "default";
const state = sessionState.get(sessionId);
if (!state) {
return "No session state found";
}
state.requestCount++;
return `Requests: ${state.requestCount}\nUptime: ${Date.now() - state.startTime}ms`;
},
});
Complete Example
Here’s a complete example showing session management:
import { FastMCP } from "fastmcp";
import { z } from "zod";
interface UserSession {
userId: string;
username: string;
role: "admin" | "user";
permissions: string[];
authenticatedAt: string;
}
const server = new FastMCP<UserSession>({
name: "Session Demo",
version: "1.0.0",
authenticate: async (request) => {
if (!request) {
// stdio transport
return {
userId: "local-user",
username: "Local User",
role: "user",
permissions: ["read", "write"],
authenticatedAt: new Date().toISOString(),
};
}
// HTTP transport - validate token
const token = request.headers["authorization"]?.substring(7);
const user = await validateToken(token);
return {
userId: user.id,
username: user.name,
role: user.role,
permissions: user.permissions,
authenticatedAt: new Date().toISOString(),
};
},
});
// Track active sessions
const activeSessions = new Map();
server.on("connect", (event) => {
const { session } = event;
const sessionId = session.sessionId || "stdio";
console.log(`Session connected: ${sessionId}`);
activeSessions.set(sessionId, {
connectedAt: Date.now(),
requestCount: 0,
});
session.on("ready", () => {
console.log(`Session ready: ${sessionId}`);
});
session.on("error", (event) => {
console.error(`Session error: ${sessionId}`, event.error);
});
session.on("rootsChanged", (event) => {
console.log(`Roots changed: ${sessionId}`, event.roots);
});
});
server.on("disconnect", (event) => {
const { session } = event;
const sessionId = session.sessionId || "stdio";
console.log(`Session disconnected: ${sessionId}`);
activeSessions.delete(sessionId);
});
server.addTool({
name: "whoami",
description: "Get current user information",
execute: async (args, context) => {
if (!context.session) {
return "No session context available";
}
return `User: ${context.session.username}\nRole: ${context.session.role}\nPermissions: ${context.session.permissions.join(", ")}`;
},
});
server.addTool({
name: "sessionStats",
description: "Get session statistics",
execute: async (args, context) => {
const sessionId = context.sessionId || "stdio";
const stats = activeSessions.get(sessionId);
if (!stats) {
return "Session not found";
}
stats.requestCount++;
const uptime = Date.now() - stats.connectedAt;
return `Session: ${sessionId}\nRequests: ${stats.requestCount}\nUptime: ${uptime}ms`;
},
});
API Reference
FastMCPSession Type
interface FastMCPSession<T> {
sessionId?: string;
clientVersion?: string;
roots?: Root[];
on(event: "ready", listener: () => void): void;
on(event: "error", listener: (event: { error: Error }) => void): void;
on(event: "rootsChanged", listener: (event: { roots: Root[] }) => void): void;
}
Context Type
type Context<T> = {
session: T | undefined;
sessionId?: string;
requestId?: string;
log: Logger;
reportProgress: (progress: Progress) => Promise<void>;
streamContent: (content: Content | Content[]) => Promise<void>;
client: {
version: string;
};
};