Turn any SpringDoc-powered Spring Boot API into a production-ready MCP gateway.
Swagger MCP Bridge discovers your OpenAPI operations, publishes them as safe MCP tools, and adds a smart gateway layer for API discovery, validation, response shaping, and multi-step workflow orchestration.
Most MCP API bridges stop at a thin tool wrapper. Swagger MCP Bridge is designed as a runtime gateway: it exposes your existing Spring controllers to LLM clients while preserving contracts, guardrails, and operational visibility.
- Zero-boilerplate discovery of SpringDoc OpenAPI operations from your running Spring app
- Automatic MCP tool registration for discovered API operations
- Smart-context gateway tools:
meta_get_api_capabilities,meta_validate_api_call,meta_discover_api_tools,meta_describe_api_tool,meta_list_api_groups,meta_plan_api_workflow,meta_invoke_api_workflow,meta_invoke_api_by_intent - API catalog and workflow layer for capability inspection, preflight validation, grouped exploration, dry-run planning, and sequential execution
- Rich MCP input schemas generated from OpenAPI constraints: required fields, enums, numeric/string/object limits, examples, and deprecation hints
- Response shaping with JSONPath projection and summarization controls
- Execution guardrails: required argument validation, unresolved path-template protection, and safe
_headersfiltering - Structured MCP error responses with stable codes such as
INVALID_ARGUMENT,SECURITY_DENIED,WORKFLOW_ERROR, andHTTP_DISPATCH_FAILED - Java 17 bytecode with CI coverage on Java 17, 21, and 25
- Optional virtual-thread HTTP dispatch on Java 21+ runtimes, with automatic platform-thread fallback on Java 17
- Production guardrails for dangerous operations:
_confirm, blocked paths, role checks, audit logs, and structured client errors
graph TD
User([User / LLM Client]) <--> MCP[MCP Client / Claude Desktop]
MCP <--> Bridge[Swagger MCP Bridge /starter/]
Bridge --> Catalog[Operation Catalog /groups + contracts/]
Bridge --> Workflow[Workflow Orchestrator /plan + dry-run + execute/]
Bridge <--> Docs[SpringDoc OpenAPI /v3/api-docs]
Bridge <--> API[Your Spring Controller /hello]
Use:
- Java 17+
- Spring Boot 3.5.x
- Spring Web
Gradle (build.gradle.kts):
plugins {
id("org.springframework.boot") version "3.5.14"
id("io.spring.dependency-management") version "1.1.7"
java
}
java {
sourceCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.17")
implementation("io.github.neo1228:spring-boot-starter-swagger-mcp:<version>")
}Maven (pom.xml):
<properties>
<swagger-mcp.version>0.1.0-SNAPSHOT</swagger-mcp.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
<version>2.8.17</version>
</dependency>
<dependency>
<groupId>io.github.neo1228</groupId>
<artifactId>spring-boot-starter-swagger-mcp</artifactId>
<version>${swagger-mcp.version}</version>
</dependency>
</dependencies>Use a release version (for example 0.1.0) when consuming from a remote artifact repository.
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class HelloController {
@Operation(operationId = "getHello", summary = "Get greeting message")
@GetMapping("/hello")
public Map<String, Object> hello(@RequestParam(defaultValue = "world") String name) {
return Map.of("message", "Hello " + name);
}
}spring:
ai:
mcp:
server:
protocol: STREAMABLE_HTTP
streamable-http:
mcp-endpoint: /mcp
swagger:
mcp:
enabled: true
api-docs-path: /v3/api-docs
tool-name-prefix: api_- Start app:
./gradlew bootRunor./mvnw spring-boot:run - Verify OpenAPI:
http://localhost:8080/v3/api-docs - Verify MCP endpoint:
http://localhost:8080/mcp - Connect from an MCP client
Generated tool names follow <tool-name-prefix><operation-id> (example: api_gethello).
This starter exposes direct API tools and a meta-tool layer so general MCP clients can work with large APIs without guessing tool names upfront:
meta_get_api_capabilitiesreturns API catalog stats, available gateway tools, orchestration features, safety policy, and response controls.meta_list_api_groupssummarizes the exposed API catalog by OpenAPI tag/group.meta_discover_api_toolsfinds relevant operations for a natural-language request.meta_describe_api_toolreturns the selected tool's method/path, parameters, required arguments, request body schema, risk flags, and full MCP input schema.meta_validate_api_callvalidates one generated API tool call without dispatching HTTP, including required arguments, risky-operation confirmation, and dispatch preview.meta_plan_api_workflowturns a workflow goal into a deterministic candidate step plan with contracts and risk flags.meta_invoke_api_workflowdry-runs or executes multiple generated API tools sequentially.meta_invoke_api_by_intentcan select and invoke the best matching operation when the client already has enough arguments.
The configured tool-name-prefix is still applied, so the default generated names are api_meta_get_api_capabilities, api_meta_validate_api_call, api_meta_list_api_groups, api_meta_discover_api_tools, api_meta_describe_api_tool, api_meta_plan_api_workflow, api_meta_invoke_api_workflow, and api_meta_invoke_api_by_intent.
When a tool call is rejected, the text content remains human-readable and structuredContent.error gives clients a stable machine contract:
{
"error": {
"code": "INVALID_ARGUMENT",
"message": "Missing required argument(s): path parameter: orderId",
"status": 400,
"retryable": false,
"details": { "toolName": "api_getorder" }
}
}Recommended client loop:
- Call
api_meta_get_api_capabilitiesonce to learn the gateway features and safety policy. - Use
api_meta_discover_api_toolsorapi_meta_list_api_groupsto find candidate operations. - Use
api_meta_describe_api_toolfor exact argument schema. - Use
api_meta_validate_api_callbefore risky or generated calls. - For multi-step work, call
api_meta_plan_api_workflow, thenapi_meta_invoke_api_workflowwithdryRun=true, then execute withdryRun=falseonly after validation is clean.
Workflow execution is intentionally safe by default:
meta_validate_api_callandmeta_invoke_api_workflowdry-runs validate tool names, arguments, required fields, dispatch paths, and risk flags before dispatching HTTP.- A workflow step has
{ "id": "...", "toolName": "...", "arguments": { ... } }. - Later steps can read previous structured results with JSONPath interpolation:
${create:$.order.id}. - If the whole argument value is a template, the resolved raw value is passed through. If a template is embedded in a longer string, the value is stringified.
- Recursive meta-tool orchestration is blocked; workflow steps can invoke generated API operation tools only.
- Risky HTTP methods still require the configured
_confirmtoken even inside a workflow.
Example validation payload:
{
"toolName": "api_getorder",
"arguments": {
"orderId": "order-1"
}
}Example workflow payload:
{
"dryRun": false,
"steps": [
{
"id": "create",
"toolName": "api_createorder",
"arguments": {
"body": { "id": "order-1", "item": "shoe" },
"_confirm": "CONFIRM"
}
},
{
"id": "read",
"toolName": "api_getorder",
"arguments": {
"orderId": "${create:$.order.id}"
}
}
]
}For larger APIs, set swagger.mcp.smart-context.gateway-only=true to expose only this gateway/meta layer instead of registering every operation as a top-level MCP tool.
If the artifact is not published to a remote registry yet:
- Build and publish to local Maven cache:
./gradlew publishToMavenLocal
- In your consumer app:
- add
mavenLocal()repository - use version
0.1.0-SNAPSHOT(or your chosen local version)
- add
swagger.mcp.enabled: enable/disable bridge (defaulttrue)swagger.mcp.api-docs-path: OpenAPI docs path (default/v3/api-docs)swagger.mcp.tool-name-prefix: tool name prefix (defaultapi_)swagger.mcp.smart-context.gateway-only: expose only meta toolsswagger.mcp.execution.virtual-threads-enabled: run outbound API dispatch through virtual threads when the current runtime supports them (defaulttrue; safely falls back on Java 17)swagger.mcp.execution.allowed-argument-headers: optional allowlist for dynamic_headerspassed by MCP clientsswagger.mcp.execution.blocked-argument-headers: denylist for dynamic_headers; defaults block hop-by-hop/transport-sensitive headers likeHost,Content-Length,Connection, andTransfer-Encodingswagger.mcp.security.require-confirmation-for-risky-operations: require_confirmtoken for risky methods
For risky HTTP methods (POST, PUT, PATCH, DELETE), default policy requires _confirm=CONFIRM. The adapter also validates missing required path/query/header/body arguments before dispatching HTTP, so MCP clients get a clear tool error instead of a malformed API call.
| Starter | Java | Spring Boot | springdoc-openapi | Spring AI BOM |
|---|---|---|---|---|
| 0.1.x | 17, 21, 25 tested; Java 17 bytecode | 3.5.x | 2.8.17 | 1.1.5 |
Spring Boot 4.x is intentionally not supported in the 0.1.x line. Stay on Spring Boot 3.5.x with springdoc-openapi 2.8.x unless this repository cuts a new major/minor compatibility line. The build uses --release 17, so the artifact remains consumable on Java 17 while CI verifies newer runtimes including Java 25.
See examples/minimal-webmvc-gradle for a minimal Spring Boot app using Swagger MCP Bridge.
- Release process:
RELEASING.md - Versioning policy:
VERSIONING.md - Changelog:
CHANGELOG.md
- Run tests:
./gradlew test - Contribution guide:
CONTRIBUTING.md - Security reporting:
SECURITY.md
Apache License 2.0 (LICENSE)