Building a Simple WebMVC MCP Server with Spring and Kotlin

Apply WebMVC to an MCP Server.

Getting Started

The previous example built a STDIO MCP Server with Spring. This page creates a server using WebMVC transport.

MCP Protocols and Execution Modes

Spring AI MCP Server provides:

  • STDIO: Communicates through process standard input and output.
  • SSE: Uses Server-Sent Events for real-time server-client communication.
  • Streamable HTTP: Extends HTTP communication to stream responses and handle multiple clients with POST and GET requests.
  • Stateless Streamable HTTP: Sends requests and streams responses through one HTTP endpoint without retaining session memory between requests, which suits microservice environments.

This example uses Stateless Streamable HTTP.

Changing the MCP Server to WebMVC

Reuse the existing STDIO MCP Server code.

Copying the Existing Code

Copy the previous project or use the GitHub example.

% mv spring-ai-mcp-server spring-ai-mcp-server-webmvc

Build Script

Change the root project name in settings.gradle.kts.

rootProject.name = "spring-ai-mcp-server-webmvc"

Add the WebMVC MCP Server dependency.

dependencies {
  ...
  implementation("org.springframework.ai:spring-ai-starter-mcp-server")
  implementation("org.springframework.ai:spring-ai-starter-mcp-server-webmvc")
  ...
}

Application Configuration

Change application.yml.

spring:
  main:
#    web-application-type: none
    banner-mode: off
  ai:
    mcp:
      server:
        name: my-weather-server
        version: 0.0.1
        protocol: STATELESS

logging:
#  pattern:
#    console:
  file:
    name: ./log/spring-ai-starter-mcp-server-webmvc.log

The server configuration adds protocol: STATELESS. Removing the STDIO-only settings starts a daemon web process and allows console logging.

Implementing an MCP Client for Testing

Create HttpClient.kt.

package com.devkuma.ai.mcp.client

import io.modelcontextprotocol.client.McpClient
import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest

fun main() {
    val transport = HttpClientStreamableHttpTransport.builder("http://localhost:8080")
        .build()

    val client = McpClient.sync(transport)
        .build()

    client.initialize()
    client.ping()

    val toolsList = client.listTools()
    println("Available Tools = $toolsList")

    val alertResult = client.callTool(CallToolRequest("get_weather", mapOf("city" to "seoul")))
    println("get_weather Response = $alertResult")

    client.closeGracefully()
}

HttpClientStreamableHttpTransport receives the HTTP address. Start the MCP Server process before running the client. The client prints the available tools and calls get_weather, just as the STDIO version does.

References