-
-
Notifications
You must be signed in to change notification settings - Fork 761
MCP Server
Atmosphere 4.0 includes a built-in Model Context Protocol (MCP) server that lets AI agents (Claude Desktop, GitHub Copilot, custom clients) invoke tools, read resources, and use prompt templates — all over WebSocket with automatic SSE fallback.
You annotate plain Java methods with @McpTool, @McpResource, or @McpPrompt, and Atmosphere handles the JSON-RPC 2.0 protocol, transport negotiation, and reconnection.
Add the dependency:
<dependency> <groupId>org.atmosphere</groupId> <artifactId>atmosphere-mcp</artifactId> <version>4.0.1</version> </dependency>
Annotate a class:
@McpServer(name = "my-server", path = "/atmosphere/mcp") public class MyMcpServer { @McpTool(name = "get_time", description = "Get the current server time") public String getTime( @McpParam(name = "timezone", description = "IANA timezone", required = false) String tz ) { var zone = tz != null ? ZoneId.of(tz) : ZoneId.systemDefault(); return Instant.now().atZone(zone).format(DateTimeFormatter.RFC_1123_DATE_TIME); } }
Connect from Claude Desktop, Copilot, or any MCP client at ws://localhost:8080/mcp.
Marks a class as an MCP endpoint. One class per server.
| Attribute | Default | Description |
|---|---|---|
name |
(required) | Server name reported during initialize
|
version |
"1.0.0" |
Server version |
path |
"/mcp" |
WebSocket/SSE endpoint path |
Exposes a method as a callable tool. AI agents discover tools via tools/list and invoke them via tools/call.
| Attribute | Description |
|---|---|
name |
Tool name (how agents refer to it) |
description |
Human-readable description (agents use this to decide when to call it) |
The method return value is serialized to JSON and sent back to the agent. Parameters are automatically mapped from the agent's JSON arguments.
Exposes a method as a read-only resource. Agents discover resources via resources/list and read them via resources/read.
| Attribute | Default | Description |
|---|---|---|
uri |
(required) | Resource URI (e.g., atmosphere://server/status) |
name |
(required) | Human-readable name |
description |
What this resource provides | |
mimeType |
"text/plain" |
Content type |
Exposes a method as a prompt template. Returns a List<McpMessage> with system and user messages.
| Attribute | Description |
|---|---|
name |
Prompt name |
description |
What this prompt does |
Use McpMessage.system(...) and McpMessage.user(...) to build the message list.
Annotates method parameters with metadata for the agent.
| Attribute | Default | Description |
|---|---|---|
name |
(required) | Parameter name in the JSON schema |
description |
Helps the agent understand what to pass | |
required |
true |
Whether the agent must provide this parameter |
Tip: If you compile with
-parameters, you can omit@McpParamfor required parameters — the framework reads parameter names from bytecode.
@McpServer(name = "notes-server", version = "1.0.0", path = "/atmosphere/mcp") public class NotesMcpServer { private final Map<String, String> notes = new ConcurrentHashMap<>(); // ── Tools ── @McpTool(name = "save_note", description = "Save a note with a title") public Map<String, Object> saveNote( @McpParam(name = "title", description = "Note title") String title, @McpParam(name = "content", description = "Note content") String content ) { notes.put(title, content); return Map.of("saved", title); } @McpTool(name = "list_notes", description = "List all saved notes") public Map<String, String> listNotes() { return Map.copyOf(notes); } // ── Resources ── @McpResource(uri = "atmosphere://notes/count", name = "Note Count", description = "Number of stored notes", mimeType = "application/json") public String noteCount() { return Map.of("count", notes.size()).toString(); } // ── Prompts ── @McpPrompt(name = "summarize", description = "Summarize all notes") public List<McpMessage> summarize() { var list = notes.isEmpty() ? "No notes." : String.join("\n", notes.values()); return List.of( McpMessage.system("Summarize concisely."), McpMessage.user("Notes:\n" + list) ); } }
Add the starter and MCP module:
<dependency> <groupId>org.atmosphere</groupId> <artifactId>atmosphere-spring-boot-starter</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>org.atmosphere</groupId> <artifactId>atmosphere-mcp</artifactId> <version>4.0.1</version> </dependency>
Configure the scanning package:
atmosphere.packages=com.example.mcpThe @McpServer class is discovered and registered automatically. Connect at ws://localhost:8080/atmosphere/mcp.
The Spring Boot MCP Server sample ships with a React frontend that uses the useAtmosphere hook to provide a live chat UI alongside the MCP endpoint:
import { useAtmosphere } from 'atmosphere.js/react'; const { data, state, push } = useAtmosphere<ChatMessage>({ request: { url: '/atmosphere/chat', transport: 'websocket', fallbackTransport: 'long-polling', trackMessageLength: true, contentType: 'application/json', }, });
This demonstrates a common pattern: MCP tools + real-time UI in the same application. AI agents connect to /atmosphere/mcp to invoke tools (list users, broadcast messages, ban users), while human users interact through the React chat UI at /atmosphere/chat.
See Framework Hooks — React, Vue, Svelte for the full hooks API.
For dynamic tools or when you prefer lambdas over annotations, register tools, resources, and prompts programmatically:
McpRegistry registry = McpRegistry.get(); // Register a tool with a lambda registry.registerTool("greet", "Greet a user by name", List.of(new McpRegistry.ParamEntry("name", "User name", true)), args -> "Hello, " + args.get("name") + "!" ); // Register a resource registry.registerResource("atmosphere://app/version", "App Version", "Current application version", "text/plain", () -> "4.0.0" ); // Register a prompt registry.registerPrompt("welcome", "Welcome message for new users", List.of(), args -> List.of( McpMessage.system("You are a friendly assistant."), McpMessage.user("Welcome the user warmly.") ) );
Programmatic and annotation-based registrations coexist — agents see all of them via tools/list.
The MCP module supports three transports:
| Transport | URL | Use Case |
|---|---|---|
| WebSocket | ws://host/atmosphere/mcp |
Full-duplex, lowest latency |
| Streamable HTTP | POST http://host/atmosphere/mcp |
MCP spec 2025年03月26日, SSE responses |
| stdio (via bridge) | java -jar atmosphere-mcp-stdio-bridge.jar <url> |
Claude Desktop, VS Code |
The Streamable HTTP transport follows the MCP 2025年03月26日 specification:
-
POST — send JSON-RPC requests. Set
Accept: text/event-streamto receive SSE responses - GET — open an SSE stream for server-initiated notifications
- DELETE — terminate the session
- Sessions tracked via the
Mcp-Session-Idheader
For MCP clients that only support stdio (e.g., some Claude Desktop configurations), the stdio bridge translates between stdin/stdout and the Streamable HTTP endpoint:
java -jar atmosphere-mcp-stdio-bridge.jar http://localhost:8083/atmosphere/mcp
Claude Desktop config.json with stdio:
{
"mcpServers": {
"atmosphere": {
"command": "java",
"args": ["-jar", "atmosphere-mcp-stdio-bridge.jar",
"http://localhost:8083/atmosphere/mcp"]
}
}
}Via WebSocket:
{
"mcpServers": {
"atmosphere": {
"transport": "websocket",
"url": "ws://localhost:8080/atmosphere/mcp"
}
}
}Via Streamable HTTP:
{
"mcpServers": {
"atmosphere": {
"transport": "streamable-http",
"url": "http://localhost:8080/atmosphere/mcp"
}
}
}Via stdio bridge:
{
"mcpServers": {
"atmosphere": {
"command": "java",
"args": ["-jar", "atmosphere-mcp-stdio-bridge.jar",
"http://localhost:8080/atmosphere/mcp"]
}
}
}The server speaks standard JSON-RPC 2.0 over WebSocket. The handshake follows the MCP specification:
- Client sends
initialize→ server responds with capabilities - Client sends
initializednotification - Client calls
tools/list,resources/list,prompts/listto discover capabilities - Client invokes
tools/call,resources/read,prompts/getas needed
The MCP module builds on Atmosphere's transport layer:
-
McpRegistry— scans@McpServerclasses at startup, indexes tools/resources/prompts (annotation and programmatic) -
McpProtocolHandler— parses JSON-RPC messages, dispatches to annotation methods or lambda handlers -
McpWebSocketHandler— handles WebSocket frames -
McpHandler— handles Streamable HTTP (POST/GET/DELETE) and SSE fallback -
McpSession— tracks session state andMcp-Session-Id -
McpStdioBridge— stdin/stdout ↔ HTTP adapter for Claude Desktop -
JsonRpc— serializes/deserializes JSON-RPC 2.0 request and response objects
Because MCP runs over Atmosphere's transport, you get automatic reconnection, heartbeats, and transport fallback for free — features that raw MCP servers don't have.
-
Framework Hooks — React, Vue, Svelte —
useAtmospherehook used by the MCP sample frontend - AI / LLM Streaming — stream LLM responses token-by-token
- Understanding @ManagedService — real-time endpoints
- Sample: Spring Boot MCP Server