> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mcpmanager.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build with Spring AI (Java)

> How to build a Java MCP server with Spring AI to run behind MCP Manager: serving the STREAMABLE protocol, choosing between the mcp-authorization-server module (the only JVM path to dynamic client registration, built on Spring Authorization Server) and the resource-server or API-key modules, and the critical gotcha that the default InMemoryRegisteredClientRepository is ephemeral and must be swapped for JdbcRegisteredClientRepository in production.

[Spring AI](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-security.html) is the JVM path to an MCP server, and its `mcp-security` modules are the **only Java option that can be a full OAuth authorization server with dynamic client registration** — via the `mcp-authorization-server` module, built on Spring Authorization Server. The official [Java SDK](https://github.com/modelcontextprotocol/java-sdk) deliberately delegates authorization to Spring, so this page is really about the Spring security modules. They're authoritative; this page covers the choices and the one gotcha that matters most for **MCP Manager**.

<Note>
  Start with [Building Your Own MCP
  Server](/build-your-own-mcp-server/overview)
  for the requirements and the auth-mode
  decision tree. This page is the Spring
  AI layer on top.
</Note>

## Serve the STREAMABLE protocol

Use the WebMVC server starter (`spring-ai-starter-mcp-server-webmvc`) and set `spring.ai.mcp.server.protocol=STREAMABLE` (or `STATELESS` for a stateless deployment). That gives you the Streamable HTTP transport MCP Manager requires.

<Warning>
  `mcp-security` supports **WebMVC**,
  not WebFlux — a WebFlux MCP server
  isn't covered by the security modules
  today. And as with every framework
  here, don't ship a server that only
  speaks the legacy HTTP+SSE transport;
  MCP Manager won't connect to it.
</Warning>

## Choose a security module per MCP Manager mode

`mcp-security` has two distinct modules. Pick the one matching your [chosen MCP Manager mode](/build-your-own-mcp-server/overview#choosing-an-authentication-mode).

| You want                            | Use in Spring                                                                            | Notes                                                                                                                     |
| ----------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **Standard OAuth + DCR**            | `mcp-authorization-server` (`McpAuthorizationServerConfigurer.mcpAuthorizationServer()`) | Built on Spring Authorization Server; **DCR is on by default**. Also serves RFC 8414 + RFC 9728 metadata                  |
| **Pre-registration / bearer token** | `mcp-server-security` (`McpServerOAuth2Configurer.mcpServerOAuth2()`)                    | Resource server: validates inbound JWTs against an issuer, serves RFC 9728 at `/.well-known/oauth-protected-resource/mcp` |
| **Token in a header**               | `McpApiKeyConfigurer.mcpServerApiKey()`                                                  | API-key auth, maps to MCP Manager's header-token mode                                                                     |

See [Spring AI — MCP Security](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-security.html) and the [Securing MCP Servers](https://spring.io/blog/2025/09/30/spring-ai-mcp-server-security/) blog post.

## The gotcha: the default client repository is in-memory

If you run `mcp-authorization-server`, registered clients are stored in Spring Authorization Server's `RegisteredClientRepository`, which **defaults to `InMemoryRegisteredClientRepository`** — dev/test only, and ephemeral. On a multi-instance deployment that's the [classic DCR failure](/build-your-own-mcp-server/debugging-self-hosted-oauth): MCP Manager registers against one instance, the authorize hop lands on another, and the client isn't found.

The fix is to wire a **`JdbcRegisteredClientRepository`** backed by a shared database (applying the `oauth2-registered-client-schema.sql` tables). This is an explicit configuration step — it does not happen automatically.

<Warning>
  Switching to
  `JdbcRegisteredClientRepository` is
  required for any production or
  multi-instance deployment. Leaving the
  default in-memory repository in place
  is the single most common reason a
  Spring-based DCR server works in
  testing and fails once it scales.
</Warning>

<Note>
  Switching to the JDBC repository doesn't reconnect a server you already added. MCP Manager reuses the `client_id` and `client_secret` it first registered, so once the persistent repository is live, **delete and re-add the server** to force a fresh registration.
</Note>

## MCP Manager compatibility checklist

<Steps>
  <Step title="STREAMABLE protocol on WebMVC">
    Set
    `spring.ai.mcp.server.protocol=STREAMABLE`
    with the WebMVC starter;
    mcp-security doesn't cover WebFlux.
  </Step>

  <Step title="Pick the right module">
    `mcp-authorization-server` for DCR;
    `mcp-server-security` for
    bearer-token resource-server;
    `McpApiKeyConfigurer` for header
    tokens.
  </Step>

  <Step title="Use JdbcRegisteredClientRepository">
    Replace the default in-memory client
    repository with the JDBC one so
    registrations survive restarts and
    are shared across instances.
  </Step>

  <Step title="Public issuer and resource">
    Ensure the authorization server's
    issuer and the resource server's
    audience are your public URL, so
    discovery and token validation line
    up.
  </Step>
</Steps>

## Spring AI gotchas

<AccordionGroup>
  <Accordion title="In-memory RegisteredClientRepository" icon="database">
    The default loses clients on restart and isn't shared across instances. Wire `JdbcRegisteredClientRepository` for production. See [Debug Self-Hosted OAuth](/build-your-own-mcp-server/debugging-self-hosted-oauth).
  </Accordion>

  <Accordion title="WebFlux is not supported" icon="ban">
    `mcp-security` targets WebMVC. If
    you're on WebFlux, the security
    modules don't apply — plan for WebMVC
    or handle auth yourself.
  </Accordion>

  <Accordion title="Resource-identifier breadth" icon="circle-info">
    A documented limitation of the current modules is that every client supports all `resource` identifiers. If you rely on per-resource audience separation, validate that behavior against your version.
  </Accordion>
</AccordionGroup>

## Further reading

<CardGroup cols={2}>
  <Card title="Spring AI — MCP Security" icon="leaf" href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-security.html">
    The authoritative reference for the resource-server and authorization-server modules.
  </Card>

  <Card title="Securing MCP Servers (blog)" icon="shield" href="https://spring.io/blog/2025/09/30/spring-ai-mcp-server-security/">
    A walkthrough of the Spring AI MCP
    server security model.
  </Card>

  <Card title="Debug Self-Hosted OAuth" icon="bug" href="/build-your-own-mcp-server/debugging-self-hosted-oauth">
    The dynamic-client-registration
    failure and why in-memory storage
    causes it.
  </Card>

  <Card title="Building Your Own MCP Server" icon="hammer" href="/build-your-own-mcp-server/overview">
    The cross-framework requirements, decision tree, and troubleshooting catalog.
  </Card>
</CardGroup>
