Skip to content

Instantly share code, notes, and snippets.

@senocak
Last active June 25, 2026 11:19
Show Gist options
  • Select an option

  • Save senocak/5284714adebc1db4294c61c9778b28e9 to your computer and use it in GitHub Desktop.

Select an option

Save senocak/5284714adebc1db4294c61c9778b28e9 to your computer and use it in GitHub Desktop.
OpenApi3Configuration for Spring AI McpServer Swagger UI
@Configuration
public class OpenApi3Configuration {
@Bean
public OpenApiCustomizer mcpToolCustomizer(final ListableBeanFactory beanFactory) {
return openApi -> {
final var sessionHeader = new io.swagger.v3.oas.models.parameters.Parameter()
.in(SecurityScheme.In.HEADER.toString())
.name("mcp-session-id")
.required(true)
.allowEmptyValue(false)
.description("MCP Session ID used for request tracking")
.schema(new StringSchema()._default("00000000-0000-0000-0000-000000000000"));
final var acceptHeader = new io.swagger.v3.oas.models.parameters.Parameter()
.in("header")
.name("Accept")
.required(true)
.explode(false)
.allowEmptyValue(false)
.description("Accepted response types: application/json, text/event-stream")
.schema(new StringSchema()._default("application/json, text/event-stream"));
final Tag mcpTag = new Tag()
.name("MCP")
.description("Model Context Protocol (JSON-RPC 2.0)");
final List<Example> examples = new ArrayList<>();
// initialize method
final Map<String, Object> initialize = Map.of(
"jsonrpc", "2.0",
"id", 0,
"method", "initialize",
"params", Map.of(
"protocolVersion", "2024-11-05",
"clientInfo", Map.of(
"name", "http-client",
"version", "1.0.0"
),
"capabilities", Map.of()
)
);
examples.add(new Example()
.summary("initialize")
.value(initialize));
// tools/list method
final Map<String, Object> toolsList = Map.of(
"jsonrpc", "2.0",
"id", 3,
"method", "tools/list",
"params", Map.of("_meta", Map.of("progressToken", 3))
);
examples.add(new Example()
.summary("tools/list")
.value(toolsList));
// tools/call methods
final String[] beanNames = beanFactory.getBeanDefinitionNames();
int idCounter = 10;
for (String beanName : beanNames) {
Object bean;
try {
bean = beanFactory.getBean(beanName);
} catch (Exception _) {
continue;
}
final Class<?> targetClass = AopUtils.getTargetClass(bean);
int finalIdCounter = idCounter;
int finalIdCounter1 = idCounter;
ReflectionUtils.doWithMethods(targetClass, method -> {
final McpTool annotation = AnnotationUtils.findAnnotation(method, McpTool.class);
if (annotation == null)
return;
final Map<String, Object> arguments = new LinkedHashMap<>();
for (Parameter parameter : method.getParameters()) {
arguments.put(parameter.getName(), parameter.getType().getSimpleName());
}
final Map<String, Object> toolCall = Map.of(
"jsonrpc", "2.0",
"id", finalIdCounter,
"method", "tools/call",
"params", Map.of(
"name", annotation.name(),
"arguments", arguments,
"_meta", Map.of("progressToken", finalIdCounter1)
)
);
examples.add(new Example()
.summary(annotation.name())
.value(toolCall));
});
idCounter++;
}
// BUILD SWAGGER CONTENT (ONE ENDPOINT ONLY)
final Map<String, Example> exampleMap = examples.stream()
.collect(Collectors.toMap(
Example::getSummary,
e -> e,
(a, _) -> a,
LinkedHashMap::new
));
final MediaType mediaType = new MediaType().examples(exampleMap);
final RequestBody requestBody = new RequestBody()
.required(true)
.content(new Content().addMediaType("application/json", mediaType));
final ApiResponses responses = new ApiResponses();
final ApiResponse response = new ApiResponse()
.description("JSON-RPC response or SSE stream")
.content(new Content().addMediaType("application/json, text/event-stream", mediaType));
responses.addApiResponse("200", response);
final Operation operation = new Operation()
.summary("MCP JSON-RPC Endpoint")
.description("""
Single MCP endpoint supporting JSON-RPC 2.0:
- initialize
- tools/list
- tools/call
Each request must include:
- jsonrpc: "2.0"
- id: number
- method: string
- params: object
""")
.tags(List.of(mcpTag.getName()))
.requestBody(requestBody)
.addParametersItem(sessionHeader)
.addParametersItem(acceptHeader)
.responses(responses);
openApi.path("/mcp", new PathItem().post(operation));
openApi.addTagsItem(mcpTag);
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment