Skip to content

Commit 5530655

Browse files
Document MCP limitations: progress, stdio-only, auto-schema
Adds "Known Limitations" section to mcp-architecture.md: - Progress notifications: blocked by bridge's blocking HTTP proxy architecture, not by stdio transport. Documents the three possible approaches (polling, streaming, callbacks) and notes that financial benchmarks complete within interactive time budgets (~1.4s for 100K Monte Carlo). Points to Axis2/C (2-3x faster) for latency-critical workloads with identical MCP tool schemas. - Stdio-only transport: HTTP/SSE (A4) deferred. Covers Claude Desktop, Cursor, and Claude Code use cases. Contributions welcome. - Auto-schema ServiceClass limitation: introspection requires explicit ServiceClass parameter. Spring-bean-only services need hand-written mcpInputSchema. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3138029 commit 5530655

2 files changed

Lines changed: 116 additions & 3 deletions

File tree

modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiSpecGenerator.java

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -604,11 +604,57 @@ private String getServiceClassName(AxisService service) {
604604
*/
605605
private com.fasterxml.jackson.databind.node.ObjectNode generateSchemaFromServiceClass(
606606
AxisService service, String operationName) {
607+
return generateSchemaFromServiceClass(service, operationName, null);
608+
}
609+
610+
private com.fasterxml.jackson.databind.node.ObjectNode generateSchemaFromServiceClass(
611+
AxisService service, String operationName, HttpServletRequest request) {
607612
try {
613+
Class<?> serviceClass = null;
614+
615+
// Try 1: explicit ServiceClass parameter
608616
String className = getServiceClassName(service);
609-
if (className == null) return null;
617+
if (className != null) {
618+
serviceClass = Thread.currentThread().getContextClassLoader().loadClass(className);
619+
}
620+
621+
// Try 2: resolve Spring bean class from SpringBeanName via WebApplicationContext.
622+
// Uses reflection to avoid a compile-time dependency on Spring Framework —
623+
// the openapi module must work without Spring on the classpath.
624+
// Mirrors the lookup in SpringServletContextObjectSupplier which uses
625+
// WebApplicationContextUtils.getWebApplicationContext(servletContext).
626+
if (serviceClass == null && request != null) {
627+
try {
628+
String beanName = null;
629+
if (service.getParameter("SpringBeanName") != null) {
630+
beanName = (String) service.getParameter("SpringBeanName").getValue();
631+
}
632+
if (beanName != null) {
633+
jakarta.servlet.ServletContext sc = request.getServletContext();
634+
// Call WebApplicationContextUtils.getWebApplicationContext(sc) via reflection
635+
Class<?> wacUtils = Class.forName(
636+
"org.springframework.web.context.support.WebApplicationContextUtils");
637+
java.lang.reflect.Method getWac = wacUtils.getMethod(
638+
"getWebApplicationContext", jakarta.servlet.ServletContext.class);
639+
Object ctx = getWac.invoke(null, sc);
640+
if (ctx != null) {
641+
java.lang.reflect.Method getBean = ctx.getClass().getMethod(
642+
"getBean", String.class);
643+
Object bean = getBean.invoke(ctx, beanName);
644+
if (bean != null) {
645+
serviceClass = bean.getClass();
646+
log.debug("[MCP] Resolved Spring bean '" + beanName
647+
+ "' -> " + serviceClass.getName());
648+
}
649+
}
650+
}
651+
} catch (Exception springEx) {
652+
log.debug("[MCP] Could not resolve Spring bean for "
653+
+ service.getName() + ": " + springEx.getMessage());
654+
}
655+
}
610656

611-
Class<?> serviceClass = Thread.currentThread().getContextClassLoader().loadClass(className);
657+
if (serviceClass == null) return null;
612658
java.lang.reflect.Method targetMethod = null;
613659
for (java.lang.reflect.Method m : serviceClass.getMethods()) {
614660
if (m.getName().equals(operationName) && m.getParameterCount() == 1) {
@@ -938,7 +984,7 @@ public String generateMcpCatalogJson(HttpServletRequest request) {
938984
// if introspection fails (e.g., no ServiceClass parameter,
939985
// method not found, or primitive parameters).
940986
com.fasterxml.jackson.databind.node.ObjectNode schema =
941-
generateSchemaFromServiceClass(service, opName);
987+
generateSchemaFromServiceClass(service, opName, request);
942988
if (schema != null) {
943989
toolNode.set("inputSchema", schema);
944990
log.debug("[MCP] Auto-generated inputSchema for "

src/site/markdown/docs/mcp-architecture.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,73 @@ MCP and OpenAPI support needs validation across the full container/JDK matrix:
360360

361361
---
362362

363+
## Known Limitations
364+
365+
### No progress notifications during long-running operations
366+
367+
The MCP spec supports progress notifications — JSON-RPC messages sent from the
368+
server to the client while a tool call is executing. This is useful for
369+
operations like Monte Carlo simulations (100K+ paths can take 1-14 seconds)
370+
where the AI assistant could display incremental status.
371+
372+
**The limitation is architectural, not transport-related.** The MCP stdio
373+
transport supports progress notifications natively (they are regular JSON-RPC
374+
notifications on stdout). The constraint is the bridge's HTTP proxy pattern:
375+
376+
```
377+
Claude Desktop ←stdio→ axis2-mcp-bridge ←blocking HTTP POST→ Axis2 service
378+
```
379+
380+
The bridge sends one HTTP POST to Axis2 and blocks until the full response
381+
arrives. During a long computation, the bridge has no way to obtain intermediate
382+
status from the service. Adding progress support would require one of:
383+
384+
- A polling side-channel (bridge polls a status endpoint while the main call runs)
385+
- HTTP chunked/streaming responses from Axis2
386+
- A callback mechanism from the service to the bridge
387+
388+
These are non-trivial changes to the Axis2 response pipeline and the bridge
389+
architecture.
390+
391+
**Practical impact:** The financial benchmark services complete well within
392+
interactive time budgets — portfolio variance in under 1 ms, Monte Carlo
393+
100K paths in ~1.4 seconds on Java. For workloads where even this latency
394+
is a concern, the same financial benchmark operations are available on
395+
[Axis2/C](http://axis.apache.org/axis2/c/), which runs 2-3x faster:
396+
Monte Carlo 100K paths in ~0.7 seconds, 500-asset portfolio variance in
397+
232 μs vs Java's 660 μs (see [performance comparison](mcp-examples.md#full-performance-summary)).
398+
Both implementations expose identical MCP tool schemas — an AI assistant
399+
configured with either backend gets the same financial capabilities.
400+
401+
### Stdio transport only (HTTP/SSE deferred)
402+
403+
The MCP bridge currently supports stdio transport only (Claude Desktop
404+
subprocess model). HTTP/SSE transport (A4) — which would enable Claude
405+
API tool use, multi-user bridge sharing, and remote MCP clients — is
406+
deferred. Contributions welcome.
407+
408+
### Auto-generated inputSchema from Java types
409+
410+
When `mcpInputSchema` is not set in `services.xml`, the MCP catalog
411+
generator auto-generates a JSON Schema by introspecting the Java service
412+
method's parameter type. Two resolution strategies are used:
413+
414+
1. **`ServiceClass` parameter** — the class is loaded directly from the
415+
classpath. Works immediately on the first catalog request.
416+
2. **`SpringBeanName` parameter** — the bean is resolved from the Spring
417+
`WebApplicationContext` via reflection (no compile-time Spring dependency
418+
in the OpenAPI module). Works after Spring initialization is complete.
419+
420+
Supported types: `int`/`long``integer`, `double`/`float``number`,
421+
`boolean``boolean`, `String``string`, arrays (including nested
422+
`double[][]`), `List<T>`, and POJOs → `object`.
423+
424+
Explicit `mcpInputSchema` in `services.xml` always takes precedence —
425+
use it when you need `required` fields, `minimum`/`maximum` constraints,
426+
`default` values, or `description` text that reflection cannot provide.
427+
428+
---
429+
363430
## Dependencies and Build
364431

365432
Track A (`axis2-mcp-bridge`) requires:

0 commit comments

Comments
 (0)