Build Responsive MCP Servers
Imagine you're building a website. When a user logs in, you show them different navigation options. When new data arrives, you update the UI in real-time. When their permissions change, you immediately reflect those changes in the interface.
Your MCP server should work the same way.
Most developers think of MCP servers as static APIs that expose a fixed set of tools and resources. But the Model Context Protocol is designed for dynamic, responsive applications that adapt to changing conditions—just like modern websites.
The Problem with Static MCP Servers
When you build a traditional API, you define your endpoints once and they stay the same until you deploy a new version. But AI applications often need to respond to:
- User authentication state (logged in/out, role changes)
- Data availability (new files uploaded, database changes)
- System state (available resources, current workload)
- User preferences (settings changes, feature toggles)
If your MCP server doesn't adapt to these changes, users get a poor experience. They might see tools that don't work, miss new functionality, or get confused by outdated information.
Enter MCP Change Notifications
The Model Context Protocol provides a robust system for notifying clients about changes in real-time. When your server's capabilities or data change, you can immediately notify connected clients, who can then update their interfaces accordingly.
Three Types of Changes
MCP supports three main types of change notifications:
- Tools List Changes - When tools are added, removed, or updated
- Resources List Changes - When available resources change
- Resource Content Updates - When specific resource content changes
Let's look at each one:
1. Tools List Changes
When the set of available tools changes, send a
notifications/tools/list_changed
notification:
// Enable a tool when conditions are metif (userHasPermission('admin')) {adminTool.enable()// The SDK automatically sends the notification}// Disable a tool when it's no longer availableif (!databaseConnection.healthy) {databaseTool.disable()// The SDK automatically sends the notification}
Real-world example: A project management MCP server might enable a "Create Sprint" tool only when the current sprint is complete, or disable a "Deploy to Production" tool when the deployment pipeline is busy.
2. Resources List Changes
Resources can be static (like a file) or dynamic (like a database table). When
the set of available resources changes, send a
notifications/resources/list_changed
notification:
const projectResource = server.registerResource('project',new ResourceTemplate('project://{id}', {list: async () => {// This list can change as projects are created/deletedreturn await getProjectsForUser(userId)},}),{title: 'Project',description: 'A project by ID',},async (uri, { id }) => {// Handle individual project resourcereturn {contents: [{uri: uri.href,text: `Project ${id} data`,},],}},)// When a new project is createdawait createProject(newProject)// The next time list() is called, it will include the new project// Send notification to update clients
Real-world example: A file management MCP server might update its resource list when files are uploaded or deleted.
3. Resource Content Updates
When the content of a specific resource changes, notify subscribed clients:
// Client subscribes to a resourceawait client.subscribeResource({uri: 'project://123',})// Later, when the project content changesawait updateProject(123, { status: 'completed' })// Then the server can notify subscribed clientsawait server.server.notification({method: 'notifications/resources/updated',params: {uri: 'project://123',title: 'Project Alpha',},})
Real-world example: A monitoring MCP server might notify clients when a system's status changes from "healthy" to "degraded", allowing the client to immediately update its dashboard.
Thinking Like a Website
The key insight is to think of your MCP server as a dynamic website, not a static API. Here's how the patterns map:
Website Pattern | MCP Equivalent |
---|---|
Navigation menu updates | Tools list changes |
File browser updates | Resources list changes |
Real-time data updates | Resource content updates |
User role-based UI | Conditional tool enabling |
Live notifications | Resource subscriptions |
The Future of Dynamic MCP
While not all MCP clients currently support change notifications well, this will change. As the ecosystem matures, clients will:
- Automatically refresh tools in LLM context when tools are added/removed
- Update resource browsers when new resources become available
- Show real-time updates for subscribed resources
- Provide visual feedback when capabilities change
This will create a much more responsive and intuitive user experience, similar to how modern web applications feel dynamic and alive.
Tips
- Be proactive about changes - Don't wait for clients to poll for updates
- Batch notifications when possible - Avoid spamming clients with too many updates
- Handle client capabilities gracefully - Some clients might not support all notification types
- Think in terms of user experience - What would a website do in this situation?
- Document your dynamic behavior - Help users understand how your server adapts
Conclusion
MCP servers should be dynamic, responsive applications that adapt to changing conditions—just like modern websites. Change notifications are the key to building AI applications that feel alive and responsive to user needs.
While client support for these features is still evolving, building with change notifications in mind will future-proof your MCP servers and provide a significantly better user experience when clients catch up.
The difference between a static MCP server and a dynamic one is like the difference between a static HTML page and a modern React application. One feels like a document, the other feels like an application.
Start thinking of your MCP server as a website replacement, and you'll naturally build more responsive, user-friendly AI applications.
If you want to dive into this more, join me for a hands-on MCP Workshop.
Resources: