|
Looking for an older release? Documentation for springdoc-openapi v2.10.0 (Spring Boot 3.x) and springdoc-openapi v1 (Spring Boot 2.x/1.x) is still available. |
springdoc-openapi is on Open Collective. If you ❤️ this project consider becoming a sponsor.
This project is sponsored by
1. Introduction
springdoc-openapi java library helps to automate the generation of API documentation using spring boot projects.
springdoc-openapi works by examining an application at runtime to infer API semantics based on spring configurations, class structure and various annotations.
Automatically generates documentation in JSON/YAML and HTML format APIs. This documentation can be completed by comments using swagger-api annotations.
This library supports:
-
OpenAPI 3
-
Spring-boot v4 (Java 17 & Jakarta EE 9)
-
JSR-303, specifically for @NotNull, @Min, @Max, and @Size.
-
Swagger-ui
-
Scalar
-
OAuth 2
-
GraalVM native images
The following video introduces the Library:
This is a community-based project, not maintained by the Spring Framework Contributors (Pivotal).
2. Getting Started
For the integration between spring-boot and swagger-ui, add the library to the list of your project dependencies (No additional configuration is needed)
2.1. Maven
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>3.2.0</version>
</dependency>
2.2. Gradle
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:3.2.0'
For example, using version 3.2.0:
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:3.2.0'
This will automatically deploy swagger-ui to a spring-boot application:
-
Documentation will be available in HTML format, using the official swagger-ui jars
-
The Swagger UI page will then be available at
http://server:port/context-path/swagger-ui.htmland the OpenAPI description will be available at the following url for json format:http://server:port/context-path/v3/api-docs-
server: The server name or IP
-
port: The server port
-
context-path: The context path of the application
-
-
Documentation will be available in yaml format as well, on the following path : /v3/api-docs.yaml
| For custom path of the swagger documentation in HTML format, add a custom springdoc property, in your spring-boot configuration file: . |
# swagger-ui custom path
springdoc.swagger-ui.path=/swagger-ui.html
3. Springdoc-openapi Modules
3.2. Spring WebMvc support
-
Documentation will be available at the following url for json format:
http://server:port/context-path/v3/api-docs-
server: The server name or IP
-
port: The server port
-
context-path: The context path of the application
-
-
Documentation will be available in yaml format as well, on the following path : /v3/api-docs.yaml
-
Add the library to the list of your project dependencies. (No additional configuration is needed)
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
<version>3.2.0</version>
</dependency>
| This dependency is relevant if you want to generate the OpenAPI description without using the swagger-ui. |
| For custom path of the OpenAPI documentation in Json format, add a custom springdoc property, in your spring-boot configuration file: |
# /api-docs endpoint custom path
springdoc.api-docs.path=/api-docs
3.3. Spring WebFlux support
-
Documentation will be available in yaml format as well, on the following path : /v3/api-docs.yaml
-
Add the library to the list of your project dependencies (No additional configuration is needed)
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-api</artifactId>
<version>3.2.0</version>
</dependency>
3.4. Swagger-UI support
Swagger UI allows anyone — be it your development team or your end consumers — to visualize and interact with the API’s resources without having any of the implementation logic in place. It’s automatically generated from your OpenAPI (formerly known as Swagger) Specification, with the visual documentation making it easy for back end implementation and client side consumption.
-
If your project is using
spring-boot-starter-weband you need the access to the swagger-ui, add the following dependency:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>3.2.0</version>
</dependency>
-
If your project is using
spring-boot-starter-webfluxand you need the access to the swagger-ui, add the following dependency:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
<version>3.2.0</version>
</dependency>
3.5. Scalar support
Scalar is an open-source API platform, designed for creating beautiful, interactive API documentation and providing a built-in REST API client, with first-class support for OpenAPI (formerly Swagger) specifications. It’s particularly aimed at improving the developer experience when exploring APIs.
springdoc-openapi extends Scalar configuration to make it compatible with Spring Boot applications.
You can rely on native Scalar configuration properties, using the prefix scalar, as explained in the Scalar documentation.
-
The Swagger UI page will then be available at
http://server:port/context-path/scalarand the OpenAPI description will be available at the following url for json format:http://server:port/context-path/v3/api-docs-
server: The server name or IP
-
port: The server port
-
context-path: The context path of the application
-
-
If your project is using
spring-boot-starter-weband you need the access to the swagger-ui, add the following dependency:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-scalar</artifactId>
<version>3.2.0</version>
</dependency>
-
If your project is using
spring-boot-starter-webfluxand you need the access to the swagger-ui, add the following dependency:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-scalar</artifactId>
<version>3.2.0</version>
</dependency>
Since v3.1.1, the Scalar starters no longer register a ForwardedHeaderFilter (Web MVC) or a ForwardedHeaderTransformer (WebFlux). Adding the starter therefore no longer makes the whole application trust the Forwarded and X-Forwarded-* headers of every caller. If your application runs behind a trusted reverse proxy and the Scalar page must be rendered with the external host, enable Spring Boot’s own support instead:
|
server.forward-headers-strategy=framework
Only the springdoc Scalar starters replace Scalar’s own Spring Boot integration. Since v3.2.0, an application using another springdoc starter (Swagger UI or API only) together with Scalar’s scalar-webmvc or scalar-webflux gets Scalar’s standalone integration, enabled by default: set scalar.enabled=false to disable it, or scalar.url to point it at your OpenAPI description, as it otherwise renders a demo specification.
|
3.6. MCP support
springdoc-openapi provides MCP (Model Context Protocol) integration modules that automatically expose your REST API endpoints as AI tools. For detailed usage and configuration, see the MCP Support section.
-
If your project is using
spring-boot-starter-weband you need MCP support, add the following dependency:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-mcp</artifactId>
<version>3.2.0</version>
</dependency>
-
If your project is using
spring-boot-starter-webfluxand you need MCP support, add the following dependency:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-mcp</artifactId>
<version>3.2.0</version>
</dependency>
3.7. Spring Hateoas support
The support for Spring Hateoas is available using the dependency springdoc-openapi-hateoas.
The projects that use spring-boot-starter-hateoas should use:
-
springdoc-openapi-starter-webmvc-apiif they need only the access to the OpenAPI endpoints -
OR
springdoc-openapi-starter-webmvc-ui, if they need also the access to the swagger-ui
3.8. Spring Data Rest support
springdoc-openapi project supports spring-boot-starter-data-rest types like: @RepositoryRestResource and QuerydslPredicate annotations.
The projects that use spring-boot-starter-data-rest should use:
-
springdoc-openapi-starter-webmvc-apiif they need only the access to the OpenAPI endpoints -
OR
springdoc-openapi-starter-webmvc-ui, if they need also the access to the swagger-ui
3.9. Spring Security support
springdoc-openapi helps ignoring @AuthenticationPrincipal type in case it is used on REST Controllers.
springdoc-openapi supports also exposing Oauth2 endpoints of spring-security-oauth2-authorization-server.
The projects that use spring-boot-starter-security or spring-security-oauth2-authorization-server should use:
-
springdoc-openapi-starter-webmvc-apiif they depend onspring-boot-starter-weband they only need the access to the OpenAPI endpoints. -
OR
springdoc-openapi-starter-webflux-apiif they depend onspring-boot-starter-webfluxand they only the access to the OpenAPI endpoints.
3.10. Actuator support
-
In order to display
spring-boot-actuatorendpoints, simply add the following property:
springdoc.show-actuator=true
Starting from the release 1.5.1, it will be possible to expose the swagger-ui and the openapi endpoints on actuator port.
| The actuator management port has to be different from the application port. |
To expose the swagger-ui, on the management port, you should set
springdoc.use-management-port=true
# This property enables the openapi and swagger-ui endpoints to be exposed beneath the actuator base path.
management.endpoints.web.exposure.include=openapi, swagger-ui
Once enabled, you should also be able to see the springdoc-openapi endpoints under: (host and port depends on your settings)
- http://serverName:managementPort/actuator
For example, if you have the following settings:
Two endpoints will be available:
-
REST API that holdes the OpenAPI definition:
-
http://serverName:managementPort/actuator/openapi
-
-
An Endpoint, that routes to the swagger-ui:
-
http://serverName:managementPort/actuator/swagger-ui
-
management.server.port=9090
For the example, you should also be able to see the springdoc-openapi endpoints:
-
http://serverName:9090/actuator -
http://serverName:9090/actuator/swagger-ui -
http://serverName:9090/actuator/openapi
All the path springdoc-openapi properties are not applicable when springdoc.use-management-port=true.
If you want to reach the application endpoints, from the swagger-ui deployed beneath the actuator base path, using a different port from your application, CORS for your endpoints on your application level should be enabled.
|
Additionally, it is also possible to combine this property, with the existing property to display the actuator endpoints in the swagger-ui.
springdoc.show-actuator=true
Once enabled: - A dedicated group for the actuator endpoints will be by default added. - If no group is defined for the application, a default one will be added.
The swagger-ui will be then accessible through the actuator port:
-
http://serverName:managementPort/actuator/swagger-ui
If the management port is different from the application port and springdoc.use-management-port is not defined but springdoc.show-actuator is set to true:
-
The swagger-ui will be then accessible through the application port. For example:
http://serverName:applicationPort/swagger-ui.html -
A dedicated group for the actuator endpoints will be by default added.
-
If no group is defined for the application, a default one will be added.
If you want to reach the actuator endpoints for this case (different port from your application), CORS for your actuator endpoints should be enabled.
|
Note: The naming of these new endpoints beneath the actuator base path cannot be customized for now.
3.11. Spring Cloud Function Web support
spring-cloud-function-web exposes Java Function as REST endpoint automatically.
* Since version v1.6.3, the support of functional endpoints has been added.
-
These starters will display the OpenAPI description of the
spring-cloud-function-webendpoints.-
If you are using
spring-web, simply add thespringdoc-openapi-starter-webmvc-apidependency. -
If you are using
spring-webflux, simply add thespringdoc-openapi-starter-webflux-apidependency.
-
The customisation of the output can be achieved programmatically through OpenApiCustomizer or with the annotations: @RouterOperations and @RouterOperation.
For annotation usage, you have:
* @RouterOperation: It can be used alone, if the customisation is related to a single REST API.
When using @RouterOperation, it’s not mandatory to fill the path
-
@RouterOperation, contains the@Operationannotation. The@Operationannotation can also be placed on the bean method level if the property beanMethod is declared.
| Don’t forget to set operationId which is mandatory. |
@Bean
@RouterOperation(operation = @Operation(description = "Say hello", operationId = "hello", tags = "persons",
responses = @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = PersonDTO.class)))))
public Supplier<PersonDTO> helloSupplier() {
return () -> new PersonDTO();
}
-
@RouterOperations: This annotation should be used to describe the multiple REST APIs exposed byspring-cloud-function-web. When usingRouterOperations, it’s mandatory to fill the method property. -
A
@RouterOperations, contains many@RouterOperation.
@Bean
@RouterOperations({
@RouterOperation(method = RequestMethod.GET, operation = @Operation(description = "Say hello GET", operationId = "lowercaseGET", tags = "persons")),
@RouterOperation(method = RequestMethod.POST, operation = @Operation(description = "Say hello POST", operationId = "lowercasePOST", tags = "positions"))
})
public Function<Flux<String>, Flux<String>> lowercase() {
return flux -> flux.map(value -> value.toLowerCase());
}
Some code samples are available on GITHUB of demos:
3.12. Kotlin support
springdoc-openapi supports Kotlin types.
The projects that use Kotlin should use:
-
springdoc-openapi-starter-webmvc-apiif they depend onspring-boot-starter-weband they only need the access to the OpenAPI endpoints. -
OR
springdoc-openapi-starter-webflux-apiif they depend onspring-boot-starter-webfluxand they only the access to the OpenAPI endpoints.
In addition, your project should add jackson-module-kotlin as well to have the full support of Kotlin types:
|
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
3.13. Groovy support
The projects that use Groovy should use:
-
springdoc-openapi-starter-webmvc-apiif they depend onspring-boot-starter-weband they only need the access to the OpenAPI endpoints. -
OR
springdoc-openapi-starter-webflux-apiif they depend onspring-boot-starter-webfluxand they only the access to the OpenAPI endpoints.
3.14. Javadoc support
springdoc-openapi can introspect Javadoc annotations and comments:
-
The javadoc comment of a method: is resolved as the
@Operationdescription -
@return: is resolved as the@Operationresponse description -
The javadoc comment of an attribute: is resolved as '@Schema' description for this field.
The projects that needs Javadoc support should use:
-
springdoc-openapi-starter-webmvc-apiif they depend onspring-boot-starter-weband they only need the access to the OpenAPI endpoints. -
OR
springdoc-openapi-starter-webflux-apiif they depend onspring-boot-starter-webfluxand they only the access to the OpenAPI endpoints.
In addition, your project should add therapi-runtime-javadoc to read Javadoc comments at runtime.
Ensure that you add it as well as its annotation processor to your project’s dependencies. Otherwise, the Javadoc support will fail silently.
|
<!--Annotation processor -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc-scribe</artifactId>
<version>0.15.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
<!-- Runtime library -->
<dependency>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc</artifactId>
<version>0.15.0</version>
</dependency>
| If both a swagger-annotation description and a javadoc comment are present. The value of the swagger-annotation description will be used. |
3.15. Springdoc-openapi BOM
Starting from version v2.8.7, springdoc-openapi provides a BOM (Bill of Materials) to manage the dependencies of the project.
You can declare it in your project as follows:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-bom</artifactId>
<version>3.2.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
4. Springdoc-openapi Features
4.1. Adding API Information and Security documentation
The library uses spring-boot application auto-configured packages to scan for the following annotations in spring beans: OpenAPIDefinition and Info. These annotations declare, API Information: Title, version, licence, security, servers, tags, security and externalDocs. For better performance of documentation generation, declare @OpenAPIDefinition and @SecurityScheme annotations within a spring managed bean.
4.2. Error Handling for REST using @ControllerAdvice
To generate documentation automatically, make sure all the methods declare the HTTP Code responses using the annotation: @ResponseStatus
4.3. Disabling the springdoc-openapi endpoints
In order to disable the springdoc-openapi endpoint (/v3/api-docs by default) use the following property:
# Disabling the /v3/api-docs endpoint
springdoc.api-docs.enabled=false
4.4. Disabling the swagger-ui
In order to disable the swagger-ui, use the following property:
# Disabling the swagger-ui
springdoc.swagger-ui.enabled=false
4.5. Swagger-ui configuration
The library supports the swagger-ui official properties:
You need to declare swagger-ui properties as spring-boot properties. All these properties should be declared with the following prefix: springdoc.swagger-ui
4.6. Selecting the Rest Controllers to include in the documentation
Additionally, to @Hidden annotation from swagger-annotations, its possible to restrict the generated OpenAPI description using package or path configuration.
For the list of packages to include, use the following property:
# Packages to include
springdoc.packagesToScan=com.package1, com.package2
For the list of paths to include, use the following property:
# Paths to include
springdoc.pathsToMatch=/v1, /api/balance/**
4.7. Spring-webflux/WebMvc.fn with Functional Endpoints
Since version v1.5.0, a functional DSL has been introduced, thanks to this enhancement in the spring-framework: #25938
It’s an alternative functional API to the @RouterOperations annotations.
This is a sample DSL, to generate OpenAPI description to the webflux/WebMvc.fn REST endpoints:
@Bean
RouterFunction<?> routes() {
return route().GET("/foo", HANDLER_FUNCTION, ops -> ops
.operationId("hello")
.parameter(parameterBuilder().name("key1").description("My key1 description"))
.parameter(parameterBuilder().name("key2").description("My key2 description"))
.response(responseBuilder().responseCode("200").description("This is normal response description"))
.response(responseBuilder().responseCode("404").description("This is another response description"))
).build();
}
Here is the link for some sample codes:
And the Demo code, using the functional endpoints DSL:
Since version v1.3.8, the support of functional endpoints has been added.
Two main annotations have been added for this purpose: @RouterOperations and @RouterOperation.
Only REST APIs with the @RouterOperations and @RouterOperation can be displayed on the swagger-ui.
-
@RouterOperation: It can be used alone, if the Router bean contains one single route related to the REST API.. When using @RouterOperation, its not mandatory to fill the path -
@RouterOperation, can reference directly a spring Bean (beanClass property) and the underlying method (beanMethod property): Springdoc-openapi, will then inspect this method and the swagger annotations on this method level.
@Bean
@RouterOperation(beanClass = EmployeeService.class, beanMethod = "findAllEmployees")
RouterFunction<ServerResponse> getAllEmployeesRoute() {
return route(GET("/employees").and(accept(MediaType.APPLICATION_JSON)),
req -> ok().body(
employeeService().findAllEmployees(), Employee.class));
}
-
@RouterOperation, contains the@Operationannotation. The@Operationannotation can also be placed on the bean method level if the property beanMethod is declared.
| Don’t forget to set operationId which is mandatory. |
@Bean
@RouterOperation(operation = @Operation(operationId = "findEmployeeById", summary = "Find purchase order by ID", tags = { "MyEmployee" },
parameters = { @Parameter(in = ParameterIn.PATH, name = "id", description = "Employee Id") },
responses = { @ApiResponse(responseCode = "200", description = "successful operation", content = @Content(schema = @Schema(implementation = Employee.class))),
@ApiResponse(responseCode = "400", description = "Invalid Employee ID supplied"),
@ApiResponse(responseCode = "404", description = "Employee not found") }))
RouterFunction<ServerResponse> getEmployeeByIdRoute() {
return route(GET("/employees/{id}"),
req -> ok().body(
employeeRepository().findEmployeeById(req.pathVariable("id")), Employee.class));
}
-
@RouterOperations: This annotation should be used if the Router bean contains multiple routes. When using RouterOperations, its mandatory to fill the path property. -
A
@RouterOperations, contains many@RouterOperation.
@RouterOperations({ @RouterOperation(path = "/getAllPersons", beanClass = PersonService.class, beanMethod = "getAll"),
@RouterOperation(path = "/getPerson/{id}", beanClass = PersonService.class, beanMethod = "getById"),
@RouterOperation(path = "/createPerson", beanClass = PersonService.class, beanMethod = "save"),
@RouterOperation(path = "/deletePerson/{id}", beanClass = PersonService.class, beanMethod = "delete") })
@Bean
public RouterFunction<ServerResponse> personRoute(PersonHandler handler) {
return RouterFunctions
.route(GET("/getAllPersons").and(accept(MediaType.APPLICATION_JSON)), handler::findAll)
.andRoute(GET("/getPerson/{id}").and(accept(MediaType.APPLICATION_STREAM_JSON)), handler::findById)
.andRoute(POST("/createPerson").and(accept(MediaType.APPLICATION_JSON)), handler::save)
.andRoute(DELETE("/deletePerson/{id}").and(accept(MediaType.APPLICATION_JSON)), handler::delete);
}
All the documentations filled using @RouterOperation, might be completed by the router function data.
For that, @RouterOperation fields must help identify uniquely the concerned route.
springdoc-openpi scans for a unique route related to a @RouterOperation annotation, using on the following criteria:
-
by path
-
by path and RequestMethod
-
by path and produces
-
by path and consumes
-
by path and RequestMethod and produces
-
by path and RequestMethod and consumes
-
by path and produces and consumes
-
by path and RequestMethod and produces and consumes
Some code samples are available on GITHUB of demos:
And some project tests: (from app69 to app75)
4.8. MCP Integration (AI Agent Support)
springdoc-openapi provides built-in support for the Model Context Protocol (MCP), enabling your REST API endpoints to be automatically discovered and used as tools by AI agents.
Key features:
-
Auto-Discovery: Automatically turns your documented
@RestControllerendpoints into AI Tools. -
Semantic Mapping: Maps OpenAPI descriptions to AI tool metadata so LLMs understand context.
-
MCP Dashboard: Built-in web UI for discovering and testing MCP tools, with tools grouped by OpenAPI tags.
-
Agent Ready: Compatible with Claude Desktop, Cursor, and other MCP-enabled clients.
-
Customizable: Extension points to rename, redescribe, filter, or exclude tools.
-
AI-Optimized Descriptions:
@McpToolDescriptionannotation for writing descriptions specifically for AI agents. -
Tool Grouping: Tools are automatically organized by their
@Tagannotation, matching how OpenAPI operations are grouped in Swagger UI. -
Return Type Descriptions: Tool descriptions are enriched with human-readable return type information for better AI comprehension.
-
MCP Guardrails: Safe/mutating tool classification and Human-in-the-Loop (HITL) support — mutating operations require human approval before execution by default.
Add the dependency matching your web stack:
For Spring WebMvc:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-mcp</artifactId>
<version>3.2.0</version>
</dependency>
For Spring WebFlux:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-mcp</artifactId>
<version>3.2.0</version>
</dependency>
Then enable the integration:
springdoc.ai.mcp.enabled=true
Your @RestController endpoints are now available as MCP tools. For detailed configuration, extension points, guardrails, and dashboard usage, see the MCP Support section.
5. MCP Support
springdoc-openapi provides integration with the Model Context Protocol (MCP), bridging your REST API with AI Agents using Spring AI.
This integration automatically turns your documented @RestController endpoints into AI Tools, making them discoverable and callable by MCP-enabled clients such as Claude Desktop, Cursor, and other AI agents.
5.1. Modules
| Module | Description |
|---|---|
|
Core engine: properties, tool callbacks, schema converter, customizers, annotations, MCP dashboard controller, and frontend assets. |
|
WebMvc integration: static resource configurer, servlet API version strategy, |
|
WebFlux integration: reactive resource configurer, reactive API version strategy, |
5.2. Getting Started
Add the dependency matching your web stack:
For WebMvc (Spring MVC / Servlet):
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-mcp</artifactId>
<version>3.2.0</version>
</dependency>
For WebFlux (Reactive):
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-mcp</artifactId>
<version>3.2.0</version>
</dependency>
The integration is opt-in: adding the dependency alone registers nothing. Enable it explicitly in your application.properties:
springdoc.ai.mcp.enabled=true
That’s it. Your @RestController endpoints are now available as MCP tools. AI agents connecting to your MCP server will automatically discover them.
When MCP is enabled, springdoc.pre-loading-enabled is automatically forced to true by an environment post-processor, ensuring the OpenAPI specification is available at startup for tool registration.
|
Since v3.1.1, springdoc.ai.mcp.enabled defaults to false and no MCP bean, endpoint or dashboard is registered until you set it to true. Previously, adding the starter was enough to expose /mcp.
|
5.2.1. Securing the MCP endpoints
The MCP endpoints - /mcp, the dashboard at /mcp-ui and its REST API at /api/mcp-admin/* - are integration surfaces, exactly like /swagger-ui. *They provide no authentication or authorization of their own: they let an MCP client discover and invoke your API endpoints, so securing them is your application’s responsibility, for example with Spring Security.
@Bean
SecurityFilterChain mcpSecurity(HttpSecurity http) throws Exception {
return http.securityMatcher("/mcp", "/mcp-ui/**", "/api/mcp-admin/**")
.authorizeHttpRequests(requests -> requests.anyRequest().hasRole("MCP_ADMIN"))
.httpBasic(Customizer.withDefaults())
.build();
}
5.3. How It Works
-
At startup, springdoc scans your
@RestControllerendpoints and builds the OpenAPI specification (pre-loading is forced automatically). -
Each OpenAPI operation is converted into a
ToolCallbackwith:-
A name derived from the
operationId(converted tosnake_casefor better LLM tokenization, e.g.,getUserByIdbecomesget_user_by_id). -
A description from
summary/description, enriched with return type information (e.g., "Returns User (id, name, email)"). -
A JSON Schema input schema from parameters and request body.
-
A group from the first
@Tagannotation on the controller.
-
-
The customizer chain runs on each tool definition, applying
@McpToolDescriptionannotations first, then any user-registeredMcpToolCustomizerbeans. -
AI agents connect to the MCP server, discover the available tools, and can invoke your API endpoints directly.
-
When an MCP server is present, tools are registered dynamically after all singletons are instantiated via a deferred registration mechanism to avoid startup deadlocks: a
McpSyncServer, or, sincev3.2.0, theMcpStatelessSyncServerof the Spring AISTATELESSprotocol (spring.ai.mcp.server.protocol=STATELESS).
5.4. MCP Guardrails
Guardrails protect your API from unintended mutations triggered by AI agents. Two complementary safety features are built in:
5.4.1. Safe vs. Mutating Classification
Every MCP tool is automatically classified based on its HTTP method:
| Classification | HTTP Methods |
|---|---|
Safe (read-only) |
|
Mutating |
|
Individual endpoints can override this classification by calling context.setSafeEndpoint(Boolean) inside a McpToolCustomizer bean, which takes precedence over the global safe-methods list.
5.4.2. Human-in-the-Loop (HITL)
HITL is a confirmation step, not an authorization control. It gives a human operator the chance to review a mutating tool call before it runs. It does not authenticate or authorize the caller, and it is not designed to withstand a caller that deliberately confirms its own call: whoever can reach the MCP endpoint can also complete the confirmation - by repeating the call through /mcp, or by sending approved: true to /api/mcp-admin/tools/execute. Do not use it as an access-control boundary; secure /mcp and /api/mcp-admin/** in your application instead.
|
When require-approval-for-mutating-tools is true (the default), calling a mutating tool through the MCP protocol does not execute the underlying HTTP request. Instead, the agent receives a structured JSON response:
{
"requires_human_approval": true,
"tool_name": "create_book",
"http_method": "POST",
"path": "/books",
"arguments": { "title": "...", "author": "..." },
"message": "This mutating operation (POST /books) requires human approval before execution."
}
The AI agent is expected to relay this to the user, who can then confirm or reject the action.
To allow AI agents to call mutating tools freely (e.g., in a trusted internal environment):
springdoc.ai.mcp.guardrails.require-approval-for-mutating-tools=false
5.5. Extension Points
5.5.1. McpToolCustomizer
Register a McpToolCustomizer bean to modify the tool name, description, or input schema for any operation. Return null to exclude a tool entirely. Multiple customizers are applied in order.
@Bean
McpToolCustomizer rewriteDescriptions() {
return (context, path, method, operation) -> {
// Rename a tool
if ("listUsers".equals(context.getName())) {
context.setName("fetchAllUsers");
}
// Rewrite a description
if ("getUserById".equals(context.getName())) {
context.setDescription("Look up a single user by their unique ID");
}
// Exclude a tool by returning null
if (path.startsWith("/internal")) {
return null;
}
return context;
};
}
The McpToolDefinitionContext passed to each customizer exposes:
| Getter / Setter | Description |
|---|---|
|
The tool name (defaults to the |
|
The tool description shown to AI agents. |
|
The JSON Schema string for tool input. |
|
Set to |
|
Override the safe/mutating classification for this endpoint. |
Excluding endpoints via context.setExclude(true):
@Bean
McpToolCustomizer excludeDeleteAndInternal() {
return (context, path, method, operation) -> {
if (method == PathItem.HttpMethod.DELETE || path.startsWith("/internal")) {
context.setExclude(true);
}
return context;
};
}
Per-endpoint safety override via context.setSafeEndpoint(Boolean):
@Bean
McpToolCustomizer overrideSafety() {
return (context, path, method, operation) -> {
// Treat POST /reports/generate as safe (read-only despite POST method)
if (method == PathItem.HttpMethod.POST && "/reports/generate".equals(path)) {
context.setSafeEndpoint(true);
}
// Treat GET /cache/invalidate as mutating despite GET method
else if (method == PathItem.HttpMethod.GET && "/cache/invalidate".equals(path)) {
context.setSafeEndpoint(false);
}
return context;
};
}
5.5.2. @McpToolDescription Annotation
Place @McpToolDescription on a controller method to provide an AI-optimized description that overrides the OpenAPI summary/description in the MCP tool definition. Optionally override the tool name.
@GetMapping("/orders/{id}")
@Operation(summary = "Get an order by ID", operationId = "getOrderById")
@McpToolDescription(value = "Look up a single order by its unique identifier",
name = "findOrder")
public Order getOrderById(@PathVariable String id) { ... }
| Attribute | Required | Description |
|---|---|---|
|
yes |
AI-optimized description for the tool. |
|
no |
Tool name override. When empty, the |
The built-in McpToolDescriptionCustomizer reads these annotations and runs at Ordered.HIGHEST_PRECEDENCE, so user-registered McpToolCustomizer beans can further modify the annotation-provided values.
5.6. Tool Grouping
Tools in the MCP dashboard are automatically organized into groups:
-
OpenAPI-generated tools are grouped by their first
@Tagannotation. For example, a controller annotated with@Tag(name = "Users")will have all its operations grouped under "Users". -
Native MCP tools (from
@McpTool/@Toolannotated methods) are grouped under"mcp-tools". -
Tools without a tag appear under "Other" in the dashboard.
@RestController
@Tag(name = "Users")
public class UserController {
@GetMapping("/users")
@Operation(operationId = "listUsers")
public List<User> listUsers() { ... } // group: "Users"
@GetMapping("/users/{id}")
@Operation(operationId = "getUserById")
public User getUserById(@PathVariable String id) { ... } // group: "Users"
}
@RestController
@Tag(name = "Products")
public class ProductController {
@GetMapping("/products")
@Operation(operationId = "listProducts")
public List<Product> listProducts() { ... } // group: "Products"
}
5.7. MCP Dashboard
The built-in MCP Developer Dashboard is a web UI served at the configured dashboard-path (default: /mcp-ui). Enable it with:
springdoc.ai.mcp.dashboard-enabled=true
It provides:
-
Tool Discovery: Lists all registered MCP tools grouped by their OpenAPI tags, with names, descriptions, HTTP methods, paths, and input schemas.
-
Tool Execution: Execute any tool directly from the browser with JSON input, view response body, status code, and execution duration.
-
Guardrail Indicators: Mutating tools that require human approval are marked with an amber warning badge in the tool list. Executing a blocked tool shows an
Approval Requiredresponse. -
Header Forwarding: Authentication headers (e.g.,
Authorization, API keys) are automatically forwarded from the dashboard to the underlying API calls. -
Security Settings: Configure authentication credentials (Bearer tokens, Basic auth, API keys) in the dashboard UI.
-
Multi-Source Support: Discovers tools from both
ToolCallbackProviderbeans (OpenAPI-backed tools) andMcpSyncServertool specifications (@McpToolannotated methods). -
cURL Export: Copy tool invocations as cURL commands for use outside the dashboard.
-
Latency Attribution: The audit tab displays a stacked latency bar per tool call with a tooltip showing the full breakdown.
-
Payload Size & Token Impact: Trace viewer tabs show byte sizes and estimated token counts for MCP arguments, request bodies, and response bodies. Responses larger than 2 KB trigger an orange warning suggesting lightweight DTOs to reduce context window usage.
5.7.1. Dashboard Endpoints
The dashboard REST API is served at /api/mcp-admin:
| Method | Path | Description |
|---|---|---|
|
|
Lists all available MCP tools (includes |
|
|
Executes a tool by name with JSON arguments. |
5.7.2. Audit Logging
Every MCP tool execution produces a structured JSON audit event logged at INFO level to the org.springdoc.ai.mcp.audit logger. When the dashboard is active, events are also stored in-memory for the audit tab.
Since v3.1.1, values carried by secret-shaped keys (authorization, password, token, api-key, secret, cookie, …) are masked in the audit event - in the tool arguments, the request URL query string, and the request and response bodies - before it is logged or stored. Turn the masking off only if your audit sink is itself a trusted, access-controlled store:
springdoc.ai.mcp.audit.redact=false
5.8. Schema Conversion
The OpenApiSchemaConverter translates OpenAPI operations into JSON Schema suitable for AI tool input. It handles:
-
Parameters: Path, query, and header parameters with types, descriptions, and required markers.
-
Undeclared Path Variables: Detects
{var}in path templates not declared as parameters and adds them as required string properties. -
Request Body: Mapped as a
bodyproperty; supports$refresolution and multiple media types (prefersapplication/json). -
Composed Schemas:
allOf(merged),anyOf,oneOfcomposition keywords. -
Nested Objects: Recursive property processing with circular reference detection.
-
Validation Constraints:
pattern,minimum/maximum,exclusiveMinimum/exclusiveMaximum,minLength/maxLength,minItems/maxItems,multipleOf,enum,default. -
Response Descriptions: Appends a human-readable return type description (e.g., "Returns an array of User (id, name, email)") to the tool description.
6. Springdoc-openapi Properties
springdoc-openapi relies on standard spring configuration properties (yml or properties) using the standard files locations.
6.1. springdoc-openapi core properties
| Parameter name | Default Value | Description |
|---|---|---|
springdoc.api-docs.path |
|
|
springdoc.api-docs.enabled |
|
|
springdoc.packages-to-scan |
|
|
springdoc.paths-to-match |
|
|
springdoc.produces-to-match |
|
|
springdoc.headers-to-match |
|
|
springdoc.consumes-to-match |
|
|
springdoc.paths-to-exclude |
|
|
springdoc.packages-to-exclude |
|
|
springdoc.default-consumes-media-type |
|
|
springdoc.default-produces-media-type |
|
|
springdoc.cache.disabled |
|
|
springdoc.cache.max-entries |
|
|
springdoc.show-actuator |
|
|
springdoc.auto-tag-classes |
|
|
springdoc.model-and-view-allowed |
|
|
springdoc.override-with-generic-response |
|
|
springdoc.group-configs[0].group |
|
|
springdoc.group-configs[0].display-name |
|
|
springdoc.group-configs[0].packages-to-scan |
|
|
springdoc.group-configs[0].paths-to-match |
|
|
springdoc.group-configs[0].paths-to-exclude |
`` |
|
springdoc.group-configs[0].packages-to-exclude |
|
|
springdoc.group-configs[0].produces-to-match |
|
|
springdoc.group-configs[0].consumes-to-match |
|
|
springdoc.group-configs[0].headers-to-match |
|
|
springdoc.api-docs.resolve-schema-properties |
|
|
springdoc.remove-broken-reference-definitions |
|
|
springdoc.writer-with-default-pretty-printer |
|
|
springdoc.model-converters.deprecating-converter.enabled |
|
|
springdoc.model-converters.polymorphic-converter.enabled |
|
|
springdoc.model-converters.polymorphic-converter.one-of-as-ref |
|
|
springdoc.model-converters.polymorphic-converter.one-of-ref-suffix |
|
|
springdoc.model-converters.kotlin-nullable-property-customizer.enabled |
|
|
springdoc.model-converters.java-nullable-property-customizer.enabled |
|
|
springdoc.model-converters.pageable-converter.enabled |
|
|
springdoc.model-converters.sort-converter.enabled |
|
|
springdoc.use-fqn |
|
|
springdoc.show-login-endpoint |
|
|
springdoc.pre-loading-enabled |
|
|
springdoc.pre-loading-locales |
|
|
springdoc.writer-with-order-by-keys |
|
|
springdoc.use-management-port |
|
|
springdoc.disable-i18n |
|
|
springdoc.show-spring-cloud-functions |
|
|
springdoc.enable-groovy |
|
|
springdoc.enable-spring-security |
|
|
springdoc.enable-kotlin |
|
|
springdoc.enable-hateoas |
|
|
springdoc.enable-data-rest |
|
|
springdoc.api-docs.version |
|
|
springdoc.default-flat-param-object |
|
|
springdoc.default-support-form-data |
|
|
springdoc.nullable-request-parameter-enabled |
|
|
springdoc.show-oauth2-endpoints |
|
|
springdoc.api-docs.resolve-extensions-properties |
|
|
springdoc.enable-default-api-docs |
|
|
springdoc.trim-kotlin-indent |
|
|
springdoc.allowed-locales |
|
|
springdoc.enable-extra-schemas |
|
|
springdoc.explicit-object-schema |
|
|
springdoc.use-arbitrary-schemas |
|
|
6.2. springdoc-openapi MCP properties
All properties are under the springdoc.ai.mcp prefix. These properties require the springdoc-openapi-starter-webmvc-mcp or springdoc-openapi-starter-webflux-mcp dependency.
| Parameter name | Default Value | Description |
|---|---|---|
springdoc.ai.mcp.enabled |
|
|
springdoc.ai.mcp.base-url |
|
|
springdoc.ai.mcp.init-timeout-seconds |
|
|
springdoc.ai.mcp.paths-to-exclude |
|
|
springdoc.ai.mcp.mcp-endpoint |
|
|
springdoc.ai.mcp.dashboard-enabled |
|
|
springdoc.ai.mcp.dashboard-path |
|
|
springdoc.ai.mcp.guardrails.require-approval-for-mutating-tools |
|
|
springdoc.ai.mcp.guardrails.safe-methods |
|
|
springdoc.ai.mcp.audit.redact |
|
|
When MCP is enabled, springdoc.pre-loading-enabled is automatically forced to true by an environment post-processor, ensuring the OpenAPI specification is available at startup for tool registration.
|
The MCP endpoints (/mcp, /mcp-ui and /api/mcp-admin/**) are integration surfaces, exactly like /swagger-ui: they provide no authentication or authorization of their own and must be secured by your application. In particular, springdoc.ai.mcp.guardrails.require-approval-for-mutating-tools is a human-in-the-loop confirmation step, not an access-control boundary.
|
6.3. swagger-ui properties
-
The support of the swagger-ui properties is available on
springdoc-openapi. See Official documentation. -
You can use the same swagger-ui properties in the documentation as Spring Boot properties.
All these properties should be declared with the following prefix: springdoc.swagger-ui
|
| Parameter name | Default Value | Description |
|---|---|---|
springdoc.swagger-ui.path |
|
|
springdoc.swagger-ui.enabled |
|
|
springdoc.swagger-ui.configUrl |
|
|
springdoc.swagger-ui.layout |
|
|
springdoc.swagger-ui.validatorUrl |
By default, Swagger UI does not validate specs. You can use this parameter to set a validator URL, for example for against swagger.io’s online validator. |
|
springdoc.swagger-ui.tryItOutEnabled |
|
|
springdoc.swagger-ui.filter |
|
|
springdoc.swagger-ui.operationsSorter |
|
|
springdoc.swagger-ui.tagsSorter |
|
|
springdoc.swagger-ui.oauth2RedirectUrl |
|
|
springdoc.swagger-ui.displayOperationId |
|
|
springdoc.swagger-ui.displayRequestDuration |
|
|
springdoc.swagger-ui.deepLinking |
|
|
springdoc.swagger-ui.defaultModelsExpandDepth |
|
|
springdoc.swagger-ui.defaultModelExpandDepth |
|
|
springdoc.swagger-ui.defaultModelRendering |
|
|
springdoc.swagger-ui.docExpansion |
|
|
springdoc.swagger-ui.maxDisplayedTags |
|
|
springdoc.swagger-ui.showExtensions |
|
|
springdoc.swagger-ui.url |
|
|
springdoc.swagger-ui.showCommonExtensions |
|
|
springdoc.swagger-ui.supportedSubmitMethods |
|
|
springdoc.swagger-ui.queryConfigEnabled |
|
|
springdoc.swagger-ui.oauth. additionalQueryStringParams |
|
|
springdoc.swagger-ui.disable-swagger-default-url |
|
|
springdoc.swagger-ui.urls[0].url |
|
|
springdoc.swagger-ui.urls[0].name |
|
|
springdoc.swagger-ui.urlsPrimaryName |
|
|
springdoc.swagger-ui.oauth.clientId |
|
|
springdoc.swagger-ui.oauth.clientSecret |
|
|
springdoc.swagger-ui.oauth.realm |
|
|
springdoc.swagger-ui.oauth.appName |
|
|
springdoc.swagger-ui.oauth.scopeSeparator |
|
|
springdoc.swagger-ui.oauth.scopes |
|
|
springdoc.swagger-ui.csrf.enabled |
|
|
springdoc.swagger-ui.csrf.use-local-storage |
|
|
springdoc.swagger-ui.csrf.use-session-storage |
|
|
springdoc.swagger-ui.csrf.cookie-name |
|
|
springdoc.swagger-ui.csrf.header-name |
|
|
springdoc.swagger-ui.syntaxHighlight.activated |
|
|

