You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-commits@axis.apache.org by ro...@apache.org on 2026/04/12 02:10:12 UTC
(axis-axis2-java-core) 15/18: Document MCP limitations: progress, stdio-only, auto-schema
This is an automated email from the ASF dual-hosted git repository.
robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-core.git
commit 5530655908e389a8e5a1aed59969d5bb0f695186
Author: Robert Lazarski <ro...@gmail.com>
AuthorDate: Sat Apr 11 11:48:37 2026 -1000
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) <no...@anthropic.com>
---
.../apache/axis2/openapi/OpenApiSpecGenerator.java | 52 ++++++++++++++++-
src/site/markdown/docs/mcp-architecture.md | 67 ++++++++++++++++++++++
2 files changed, 116 insertions(+), 3 deletions(-)
diff --git a/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiSpecGenerator.java b/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiSpecGenerator.java
index 5c6fbc25ab..18d69ae815 100644
--- a/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiSpecGenerator.java
+++ b/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiSpecGenerator.java
@@ -604,11 +604,57 @@ public class OpenApiSpecGenerator {
*/
private com.fasterxml.jackson.databind.node.ObjectNode generateSchemaFromServiceClass(
AxisService service, String operationName) {
+ return generateSchemaFromServiceClass(service, operationName, null);
+ }
+
+ private com.fasterxml.jackson.databind.node.ObjectNode generateSchemaFromServiceClass(
+ AxisService service, String operationName, HttpServletRequest request) {
try {
+ Class<?> serviceClass = null;
+
+ // Try 1: explicit ServiceClass parameter
String className = getServiceClassName(service);
- if (className == null) return null;
+ if (className != null) {
+ serviceClass = Thread.currentThread().getContextClassLoader().loadClass(className);
+ }
+
+ // Try 2: resolve Spring bean class from SpringBeanName via WebApplicationContext.
+ // Uses reflection to avoid a compile-time dependency on Spring Framework —
+ // the openapi module must work without Spring on the classpath.
+ // Mirrors the lookup in SpringServletContextObjectSupplier which uses
+ // WebApplicationContextUtils.getWebApplicationContext(servletContext).
+ if (serviceClass == null && request != null) {
+ try {
+ String beanName = null;
+ if (service.getParameter("SpringBeanName") != null) {
+ beanName = (String) service.getParameter("SpringBeanName").getValue();
+ }
+ if (beanName != null) {
+ jakarta.servlet.ServletContext sc = request.getServletContext();
+ // Call WebApplicationContextUtils.getWebApplicationContext(sc) via reflection
+ Class<?> wacUtils = Class.forName(
+ "org.springframework.web.context.support.WebApplicationContextUtils");
+ java.lang.reflect.Method getWac = wacUtils.getMethod(
+ "getWebApplicationContext", jakarta.servlet.ServletContext.class);
+ Object ctx = getWac.invoke(null, sc);
+ if (ctx != null) {
+ java.lang.reflect.Method getBean = ctx.getClass().getMethod(
+ "getBean", String.class);
+ Object bean = getBean.invoke(ctx, beanName);
+ if (bean != null) {
+ serviceClass = bean.getClass();
+ log.debug("[MCP] Resolved Spring bean '" + beanName
+ + "' -> " + serviceClass.getName());
+ }
+ }
+ }
+ } catch (Exception springEx) {
+ log.debug("[MCP] Could not resolve Spring bean for "
+ + service.getName() + ": " + springEx.getMessage());
+ }
+ }
- Class<?> serviceClass = Thread.currentThread().getContextClassLoader().loadClass(className);
+ if (serviceClass == null) return null;
java.lang.reflect.Method targetMethod = null;
for (java.lang.reflect.Method m : serviceClass.getMethods()) {
if (m.getName().equals(operationName) && m.getParameterCount() == 1) {
@@ -938,7 +984,7 @@ public class OpenApiSpecGenerator {
// if introspection fails (e.g., no ServiceClass parameter,
// method not found, or primitive parameters).
com.fasterxml.jackson.databind.node.ObjectNode schema =
- generateSchemaFromServiceClass(service, opName);
+ generateSchemaFromServiceClass(service, opName, request);
if (schema != null) {
toolNode.set("inputSchema", schema);
log.debug("[MCP] Auto-generated inputSchema for "
diff --git a/src/site/markdown/docs/mcp-architecture.md b/src/site/markdown/docs/mcp-architecture.md
index d6ef60edd0..f460aa0463 100644
--- a/src/site/markdown/docs/mcp-architecture.md
+++ b/src/site/markdown/docs/mcp-architecture.md
@@ -360,6 +360,73 @@ MCP and OpenAPI support needs validation across the full container/JDK matrix:
---
+## Known Limitations
+
+### No progress notifications during long-running operations
+
+The MCP spec supports progress notifications — JSON-RPC messages sent from the
+server to the client while a tool call is executing. This is useful for
+operations like Monte Carlo simulations (100K+ paths can take 1-14 seconds)
+where the AI assistant could display incremental status.
+
+**The limitation is architectural, not transport-related.** The MCP stdio
+transport supports progress notifications natively (they are regular JSON-RPC
+notifications on stdout). The constraint is the bridge's HTTP proxy pattern:
+
+```
+Claude Desktop ←stdio→ axis2-mcp-bridge ←blocking HTTP POST→ Axis2 service
+```
+
+The bridge sends one HTTP POST to Axis2 and blocks until the full response
+arrives. During a long computation, the bridge has no way to obtain intermediate
+status from the service. Adding progress support would require one of:
+
+- A polling side-channel (bridge polls a status endpoint while the main call runs)
+- HTTP chunked/streaming responses from Axis2
+- A callback mechanism from the service to the bridge
+
+These are non-trivial changes to the Axis2 response pipeline and the bridge
+architecture.
+
+**Practical impact:** The financial benchmark services complete well within
+interactive time budgets — portfolio variance in under 1 ms, Monte Carlo
+100K paths in ~1.4 seconds on Java. For workloads where even this latency
+is a concern, the same financial benchmark operations are available on
+[Axis2/C](http://axis.apache.org/axis2/c/), which runs 2-3x faster:
+Monte Carlo 100K paths in ~0.7 seconds, 500-asset portfolio variance in
+232 μs vs Java's 660 μs (see [performance comparison](mcp-examples.md#full-performance-summary)).
+Both implementations expose identical MCP tool schemas — an AI assistant
+configured with either backend gets the same financial capabilities.
+
+### Stdio transport only (HTTP/SSE deferred)
+
+The MCP bridge currently supports stdio transport only (Claude Desktop
+subprocess model). HTTP/SSE transport (A4) — which would enable Claude
+API tool use, multi-user bridge sharing, and remote MCP clients — is
+deferred. Contributions welcome.
+
+### Auto-generated inputSchema from Java types
+
+When `mcpInputSchema` is not set in `services.xml`, the MCP catalog
+generator auto-generates a JSON Schema by introspecting the Java service
+method's parameter type. Two resolution strategies are used:
+
+1. **`ServiceClass` parameter** — the class is loaded directly from the
+ classpath. Works immediately on the first catalog request.
+2. **`SpringBeanName` parameter** — the bean is resolved from the Spring
+ `WebApplicationContext` via reflection (no compile-time Spring dependency
+ in the OpenAPI module). Works after Spring initialization is complete.
+
+Supported types: `int`/`long` → `integer`, `double`/`float` → `number`,
+`boolean` → `boolean`, `String` → `string`, arrays (including nested
+`double[][]`), `List<T>`, and POJOs → `object`.
+
+Explicit `mcpInputSchema` in `services.xml` always takes precedence —
+use it when you need `required` fields, `minimum`/`maximum` constraints,
+`default` values, or `description` text that reflection cannot provide.
+
+---
+
## Dependencies and Build
Track A (`axis2-mcp-bridge`) requires: