Skip to content
jfarcand edited this page Feb 18, 2026 · 6 revisions

MCP Server — Expose Tools, Resources, and Prompts to AI Agents

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.

Quick Start

Add the dependency:

<dependency>
    <groupId>org.atmosphere</groupId>
    <artifactId>atmosphere-mcp</artifactId>
    <version>4.0.0-SNAPSHOT</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.

Annotations

@McpServer

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

@McpTool

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.

@McpResource

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

@McpPrompt

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.

@McpParam

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 @McpParam for required parameters — the framework reads parameter names from bytecode.

Example: Full MCP Server

@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)
        );
    }
}

Spring Boot Integration

Add the starter and MCP module:

<dependency>
    <groupId>org.atmosphere</groupId>
    <artifactId>atmosphere-spring-boot-starter</artifactId>
    <version>4.0.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.atmosphere</groupId>
    <artifactId>atmosphere-mcp</artifactId>
    <version>4.0.0-SNAPSHOT</version>
</dependency>

Configure the scanning package:

atmosphere.packages=com.example.mcp

The @McpServer class is discovered and registered automatically. Connect at ws://localhost:8080/atmosphere/mcp.

Connecting AI Agents

Claude Desktop

Add to your Claude Desktop config.json:

{
  "mcpServers": {
    "atmosphere": {
      "transport": "websocket",
      "url": "ws://localhost:8080/atmosphere/mcp"
    }
  }
}

Any MCP Client

The server speaks standard JSON-RPC 2.0 over WebSocket. The handshake follows the MCP specification:

  1. Client sends initialize → server responds with capabilities
  2. Client sends initialized notification
  3. Client calls tools/list, resources/list, prompts/list to discover capabilities
  4. Client invokes tools/call, resources/read, prompts/get as needed

Architecture

The MCP module builds on Atmosphere's transport layer:

  • McpRegistry — scans @McpServer classes at startup, indexes all tools, resources, and prompts
  • McpProtocolHandler — parses JSON-RPC messages, routes to the correct method, binds arguments, returns responses
  • McpWebSocketHandler — handles WebSocket frames
  • McpHandler — handles SSE/long-polling fallback (GET suspends, POST processes)
  • 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 WebSocket MCP servers don't have.

See Also

Clone this wiki locally