A Spring Boot framework for building Model Context Protocol (MCP) servers in Java.
Define tools, prompts, and resources as annotated Spring beans. Pull in optional modules for OAuth2, per-handler authorization, Jakarta Bean Validation, structured audit logs, Micrometer observations, MDC correlation, an /actuator/mcp inventory endpoint, multi-node session storage, and GraalVM native-image support — all wired through one customizer SPI.
Status: pre-1.0, actively developed. APIs may still change between 0.x minors. We'd love early feedback and real-world usage reports.
Building an MCP server from scratch means solving the same problems every team solves: JSON-RPC dispatch, SSE streaming, session lifecycle, event replay, schema generation, transport negotiation, OAuth2, tracing, metrics, audit. Mocapi ships those pieces as Spring Boot autoconfiguration you wire by adding a transport starter, and extend through a single customizer SPI.
- MCP 2025-11-25 surface. Tools, prompts, resources, resource templates, completions, elicitation, sampling, progress notifications, logging, and the OAuth2 authorization flow. Exercised by the official conformance suite in CI. Optional subscription features (
resources/subscribe/resources/list_changed/prompts/list_changed) are not served; the capability bits are advertised asfalse, so compliant clients fall back cleanly. - Transport-agnostic handler code. Write a
@McpToolonce; run it over Streamable HTTP (for web clients) or stdio (for Claude Desktop / Cursor / IDE integrations) with no code change. - Observability modules. Metrics and tracing via Micrometer Observation, SLF4J MDC correlation, structured audit logs — each activates by dropping in a module.
- Authorization. OAuth2 resource server (MCP 2025-11-25 spec), per-handler
GuardSPI, and@RequiresScope/@RequiresRoleannotations backed by Spring Security. - Pluggable session storage. Swap the session backend: Redis, PostgreSQL, NATS JetStream, DynamoDB, or Hazelcast. Workers are stateless. Add
substrate-cryptoto AES-GCM-encrypt session, mailbox, and event-journal values before they leave the JVM. - Typed extension SPI. One customizer interface per handler kind. Attach interceptors, guards, or parameter resolvers with full access to the handler's descriptor, method, and bean — no blind bean-list autowiring.
- Virtual-thread-friendly. Context propagates across the per-call virtual-thread spawn so tracing spans parent correctly and
SecurityContextHolderworks on the handler thread. A standing soak test sustained ~565 req/s with full observability on a laptop (see Performance Benchmarking). - GraalVM native-image hints included.
Mocapi's goal is to be the framework you reach for when MCP is a real surface of your product rather than a prototype.
Add the starter dependency:
<dependency>
<groupId>com.callibrity.mocapi</groupId>
<artifactId>mocapi-streamable-http-spring-boot-starter</artifactId>
<version>0.14.0</version>
</dependency>If you depend on multiple mocapi artifacts (e.g., a starter plus one of the mocapi-prompts-* modules), import the BOM to keep versions aligned:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.callibrity.mocapi</groupId>
<artifactId>mocapi-bom</artifactId>
<version>0.14.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>Then declare individual mocapi artifacts without a <version> — the BOM pins them.
Define a tool:
import com.callibrity.mocapi.api.tools.McpTool;
import org.springframework.stereotype.Component;
@Component
public class GreetingTool {
@McpTool(name = "greet", description = "Returns a greeting message")
public GreetingResponse greet(String name) {
return new GreetingResponse("Hello, " + name + "!");
}
public record GreetingResponse(String message) {}
}Define a prompt:
import com.callibrity.mocapi.api.prompts.McpPrompt;
import com.callibrity.mocapi.model.GetPromptResult;
import com.callibrity.mocapi.model.PromptMessage;
import com.callibrity.mocapi.model.Role;
import com.callibrity.mocapi.model.TextContent;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class SummarizationPrompts {
@McpPrompt(name = "summarize", description = "Summarize the provided text")
public GetPromptResult summarize(String text) {
return new GetPromptResult(
"Summarization prompt",
List.of(new PromptMessage(
Role.USER,
new TextContent("Summarize the following:\n\n" + text, null))));
}
}Define a resource (fixed URI) and a resource template (pattern-matched URI):
import com.callibrity.mocapi.api.resources.McpResource;
import com.callibrity.mocapi.api.resources.McpResourceTemplate;
import com.callibrity.mocapi.model.ReadResourceResult;
import com.callibrity.mocapi.model.TextResourceContents;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class DocResources {
@McpResource(uri = "docs://readme", mimeType = "text/markdown")
public ReadResourceResult readme() {
return new ReadResourceResult(
List.of(new TextResourceContents("docs://readme", "text/markdown", "# Hello")));
}
@McpResourceTemplate(uriTemplate = "docs://pages/{slug}", mimeType = "text/markdown")
public ReadResourceResult page(String slug) {
return new ReadResourceResult(
List.of(new TextResourceContents(
"docs://pages/" + slug, "text/markdown", "Content for " + slug)));
}
}Run your Spring Boot application. With mocapi-streamable-http-spring-boot-starter, Mocapi exposes a Streamable HTTP endpoint at /mcp. For clients that launch the server as a subprocess (Claude Desktop, Cursor, and other IDE integrations), depend on mocapi-stdio-spring-boot-starter instead and set mocapi.stdio.enabled=true — same tools, same code, different pipe.
- Writing Tools -- defining tools, parameters, return values, and error handling
- Writing Prompts -- defining prompts, argument binding, and return messages
- Writing Resources -- fixed resources, templated resources, and path-variable binding
- Externalizing Annotation Metadata --
${...}property placeholders for tool/prompt/resource descriptions, URIs, and names - Authorization -- OAuth2 resource-server setup for the Streamable HTTP transport (MCP 2025-11-25)
- Guards -- per-handler visibility + call-time authorization via the
GuardSPI;@RequiresScope/@RequiresRoleviamocapi-spring-security-guards - Validation -- Jakarta Bean Validation on user
@McpTool/@McpPrompt/@McpResourceTemplateparameters via the optionalmocapi-jakarta-validation - Interactive Features -- progress notifications, logging, elicitation, and sampling
- Observability -- metrics + tracing (Micrometer Observation), MDC correlation, and structured audit logging
- OpenTelemetry -- drop-in OTel tracing via
mocapi-otel: bundlesmocapi-o11y+ Spring Boot 4's OTel SDK + tracing bridge; emits a two-layerjsonrpc.server/mcp.handler.executiontrace with OTel MCP / JSON-RPC / GenAI semconv attrs - Logging -- MDC correlation keys via
mocapi-logging - Audit -- structured audit logging via
mocapi-auditfor compliance queries and SIEM ingestion - Actuator Endpoint --
/actuator/mcphandler-inventory endpoint shape and operational checks - Customizers -- the
*HandlerCustomizerSPI for extending mocapi: attach interceptors, guards, and parameter resolvers per handler - Custom Parameter Resolvers -- writing
@CurrentTenant-style parameter resolvers via the customizer SPI - Configuration Reference -- all
mocapi.*properties - Architecture -- server/transport separation, session lifecycle, module structure
- Backend Integration -- using Redis, PostgreSQL, Hazelcast, or other session stores
- Performance Benchmarking -- periodic soak-test + JFR-profiling runbook to track regressions
mocapi-api— user-facing API:@McpTool,@McpPrompt,@McpResource/@McpResourceTemplate,PromptTemplate/PromptTemplateFactory,McpToolContext, provider interfacesmocapi-model— MCP protocol types (Tool, CallToolResult, ElicitResult, etc.) — mechanical mapping from the MCP specmocapi-server— stateful MCP server: session management, JSON-RPC dispatch, tool/prompt/resource invocation
mocapi-streamable-http-transport— HTTP + SSE, encrypted event IDsmocapi-stdio-transport— newline-delimited JSON-RPC on stdin/stdout, for subprocess-launched MCP clients
Only two starters. Every mocapi application adds exactly one.
mocapi-streamable-http-spring-boot-starter— bundlesmocapi-server+ streamable-HTTP transport +spring-boot-starter-web. Expose an/mcpendpoint accessible over the network.mocapi-stdio-spring-boot-starter— bundlesmocapi-server+ stdio transport. For subprocess-launched MCP clients (Claude Desktop, Cursor, IDE integrations); no web stack.
Each module is plain Java + an optional Spring Boot autoconfig (hosted in mocapi-autoconfigure). Add the module to your pom; the corresponding feature activates automatically.
mocapi-oauth2— OAuth2 resource-server protection on the MCP endpoint (MCP 2025-11-25 authorization); wraps Spring Boot's OAuth2 resource-server starter and adds the RFC 9728 protected-resource metadata document. Ships twoSecurityFilterChainbeans (public metadata + authenticated MCP) each with its own customizer SPI (McpMetadataFilterChainCustomizer,McpFilterChainCustomizer), a swappableMcpTokenStrategyfor JWT vs. opaque tokens, and a facet-basedMcpMetadataCustomizerSPI for shaping the metadata document. See Authorization.mocapi-spring-security-guards— annotation-drivenGuardimplementations backed by Spring Security. Reads@RequiresScope/@RequiresRoleoff user handler methods at startup and attaches matching guards via the customizer SPI; denied handlers disappear fromtools/listetc. and call-time returns JSON-RPC-32003 Forbidden. See Guards.mocapi-jakarta-validation— Jakarta Bean Validation on user@McpTool/@McpPrompt/@McpResourceTemplateparameters. Annotations like@NotBlank/@Size/@Patternsurface asCallToolResult.isError=truefor tools (MCP-spec-idiomatic for LLM self-correction) and JSON-RPC-32602 Invalid paramswith per-violation detail for prompts and resources. See Validation.mocapi-logging— SLF4J MDC correlation for MCP handler invocations. Stampsmcp.session,mcp.handler.kind, andmcp.handler.nameonto the MDC for the duration of every handler call so every log line from user code carries correlation context automatically. See Logging.mocapi-o11y— metrics + distributed tracing via Micrometer'sObservationAPI. Two layers: a filter enriches ripcurl-o11y's outerjsonrpc.serverobservation withmcp.method.name/mcp.session.id/mcp.protocol.versiontags; a per-handler interceptor emits an innermcp.handler.executionobservation carrying GenAI / MCP-resource attrs (gen_ai.tool.name,gen_ai.prompt.name,mcp.resource.uri). Self-sufficient — transitively pullsspring-boot-micrometer-observationso anObservationRegistryis always present. See Observability.mocapi-otel— drop-in OpenTelemetry tracing. Source-less dependency bundle that pullsmocapi-o11yplusspring-boot-starter-opentelemetry(OTel SDK + Micrometer Observation → OTel tracing bridge + autoconfig). Add this module plus the exporter for your backend — OTLP for Jaeger/Tempo, Azure Monitor starter for App Insights, Datadog, etc. — andjsonrpc.server/mcp.handler.executionspans flow end-to-end. See OTel guide.mocapi-audit— structured audit logging for every MCP handler invocation. Emits one INFO event on themocapi.auditSLF4J logger per call with caller identity, session id, handler kind/name, outcome (success/forbidden/invalid_params/error), duration, and (opt-in) a SHA-256 hash of the arguments — everything compliance / SIEM queries need, nothing PII-shaped. See Audit.mocapi-actuator— Spring Boot Actuator endpoint (/actuator/mcp) exposing a read-only inventory of the tools, prompts, resources, and resource templates registered on this node. Publishes handler names + schema digests. See Actuator Endpoint.mocapi-autoconfigure— one module hosting every mocapi autoconfig (pulled in by either transport starter; you normally don't depend on it directly).
mocapi-prompts-spring—PromptTemplateFactoryusing Spring's${name}placeholder syntax; no extra dependenciesmocapi-prompts-mustache—PromptTemplateFactorybacked by JMustache for richer{{name}}templates with sections
mocapi-bom— imports into your<dependencyManagement>to align versions across multiple mocapi artifacts without hard-coding each one
Working examples are in the examples/ directory:
| Example | Backend | Description |
|---|---|---|
| In-Memory | In-memory, HTTP | Simplest setup, no external dependencies |
| Stdio | In-memory, stdio | Minimal echo server launchable by Claude Desktop or MCP Inspector over stdio |
| Redis | Redis, HTTP | Clustered sessions via Redis |
| PostgreSQL | PostgreSQL, HTTP | Durable sessions via PostgreSQL |
| NATS | NATS JetStream, HTTP | Clustered sessions via NATS JetStream |
To run the in-memory HTTP example:
cd examples/in-memory
mvn spring-boot:runThen connect with the MCP Inspector:
npx @modelcontextprotocol/inspectorEnter http://localhost:8080/mcp and select "Streamable HTTP" transport.
To run the stdio example (no HTTP server — MCP client launches it as a subprocess):
mvn -pl examples/stdio -am package
npx @modelcontextprotocol/inspector \
java -jar examples/stdio/target/mocapi-example-stdio-*.jarSee examples/stdio/README.md for Claude Desktop configuration.
Mocapi targets the MCP 2025-11-25 specification. Conformance is validated against the official test suite:
# Start the conformance server
cd mocapi-conformance
mvn spring-boot:run
# In another terminal
npx @modelcontextprotocol/conformance server --url http://localhost:8081/mcpmvn clean installRequires Java 25+ and Maven 3.9+.
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
Apache License 2.0 -- see LICENSE.
Mocapi was built primarily with Claude Code, using a spec-driven, iterative workflow we call "the ralph loop" — one spec, one autonomous iteration, in strict numeric order.
The cycle: write a numbered Markdown spec under specs/ describing a single focused change (breaking change, new module, bug fix, refactor — one thing at a time), let an autonomous Claude Code agent pick up the lowest-numbered spec, implement it end-to-end (code + tests + docs + commit), then move on to the next one. The human role is spec author, reviewer, and course-corrector — not typist. Iteration 180 doesn't know iteration 179 existed; it reads the spec and the current codebase from scratch.
specs/done/ holds the 210+ specs that produced the 0.1.0 → 0.12.0 journey. They're the closest thing to a design diary this project has — each file captures the why of one change before it landed.
Things that made the loop work (and things that didn't) are captured under specs/backlog/ and in project-level Claude instructions (CLAUDE.md, ~/CLAUDE-ralph.md). If you're trying this approach on your own project, those are probably more useful reading than the individual specs.
Mocapi is a made-up word that includes the letters MCP (Model Context Protocol). It's pronounced moh-cap-ee (/ˈmoʊˌkæpi/), like a friendly little robot who speaks protocol.