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 d140291712865f2d7d561ef87c12c52a20fc0e13
Author: Robert Lazarski <[email protected]>
AuthorDate: Tue Apr 7 02:28:34 2026 -1000

    MCP catalog Phase A3: ticker resolve endpoint in _meta + doc updates
    
    A3 — tickerResolveEndpoint in _meta:
      Reads global AxisConfiguration parameter "mcpTickerResolveService"
      (set in axis2.xml as "<parameter name="mcpTickerResolveService">
      ServiceName/operationName</parameter>"). When present, adds
      _meta.tickerResolveEndpoint = "POST /services/<value>" to the catalog
      so MCP clients can discover the ticker→assetId resolution service
      without out-of-band documentation. Field is omitted entirely when the
      parameter is not configured.  2 new tests.
    
    Update json-rpc-mcp-guide.xml:
      - Section 7 limitations table: mark A1 (descriptions), A2 (annotation
        tuning), A3 (ticker endpoint) as Implemented with config instructions
      - Section 6.1 test table: add 13 new tests for A1/A2/A3
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---
 .../apache/axis2/openapi/OpenApiSpecGenerator.java | 17 ++++++++++
 .../axis2/openapi/McpCatalogGeneratorTest.java     | 36 ++++++++++++++++++++++
 src/site/xdoc/docs/json-rpc-mcp-guide.xml          | 23 ++++++++++----
 3 files changed, 70 insertions(+), 6 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 ffcab1d0bd..4f17b655dc 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
@@ -703,6 +703,23 @@ public class OpenApiSpecGenerator {
             meta.put("authHeader", "Authorization: Bearer <token>");
             meta.put("tokenEndpoint", "POST /services/loginService/doLogin");
 
+            // Optional: natural key (ticker → assetId) resolution endpoint.
+            // Set axis2.xml global parameter "mcpTickerResolveService" to the
+            // "ServiceName/operationName" of the ticker lookup operation, 
e.g.:
+            //   <parameter name="mcpTickerResolveService">
+            //     TickerLookupService/resolveTicker
+            //   </parameter>
+            // Omitted from _meta when not configured so deployments without a
+            // ticker service don't expose a misleading endpoint reference.
+            org.apache.axis2.description.Parameter tickerParam =
+                    axisConfig.getParameter("mcpTickerResolveService");
+            if (tickerParam != null && tickerParam.getValue() != null) {
+                String tickerSvcOp = tickerParam.getValue().toString().trim();
+                if (!tickerSvcOp.isEmpty()) {
+                    meta.put("tickerResolveEndpoint", "POST /services/" + 
tickerSvcOp);
+                }
+            }
+
             com.fasterxml.jackson.databind.node.ArrayNode toolsArray = 
root.putArray("tools");
 
             Iterator<AxisService> services = 
axisConfig.getServices().values().iterator();
diff --git 
a/modules/openapi/src/test/java/org/apache/axis2/openapi/McpCatalogGeneratorTest.java
 
b/modules/openapi/src/test/java/org/apache/axis2/openapi/McpCatalogGeneratorTest.java
index 9713d7b891..33682c0651 100644
--- 
a/modules/openapi/src/test/java/org/apache/axis2/openapi/McpCatalogGeneratorTest.java
+++ 
b/modules/openapi/src/test/java/org/apache/axis2/openapi/McpCatalogGeneratorTest.java
@@ -521,6 +521,42 @@ public class McpCatalogGeneratorTest extends TestCase {
         }
     }
 
+    // ── A3: tickerResolveEndpoint in _meta 
───────────────────────────────────
+
+    /**
+     * When the global axis2 parameter {@code mcpTickerResolveService} is set,
+     * {@code _meta.tickerResolveEndpoint} must appear with the expected 
prefix.
+     * This lets MCP clients discover the ticker→assetId resolution service
+     * without out-of-band documentation.
+     */
+    public void testTickerResolveEndpointPresentWhenConfigured() throws 
Exception {
+        axisConfig.addParameter(new org.apache.axis2.description.Parameter(
+                "mcpTickerResolveService", 
"TickerLookupService/resolveTicker"));
+
+        JsonNode meta = 
MAPPER.readTree(generator.generateMcpCatalogJson(mockRequest))
+                .path("_meta");
+        assertFalse("tickerResolveEndpoint must appear when 
mcpTickerResolveService is set",
+                meta.path("tickerResolveEndpoint").isMissingNode());
+        assertTrue("tickerResolveEndpoint must start with 'POST /services/'",
+                meta.path("tickerResolveEndpoint").asText().startsWith("POST 
/services/"));
+        assertTrue("tickerResolveEndpoint must contain the configured 
service/op name",
+                meta.path("tickerResolveEndpoint").asText()
+                        .contains("TickerLookupService/resolveTicker"));
+    }
+
+    /**
+     * When {@code mcpTickerResolveService} is not configured, the catalog must
+     * not include {@code tickerResolveEndpoint} at all — deployments without a
+     * ticker service should not expose a misleading field.
+     */
+    public void testTickerResolveEndpointAbsentWhenNotConfigured() throws 
Exception {
+        // No mcpTickerResolveService parameter added
+        JsonNode meta = 
MAPPER.readTree(generator.generateMcpCatalogJson(mockRequest))
+                .path("_meta");
+        assertTrue("tickerResolveEndpoint must be absent when 
mcpTickerResolveService not set",
+                meta.path("tickerResolveEndpoint").isMissingNode());
+    }
+
     // ── A1: mcpDescription parameter 
─────────────────────────────────────────
 
     /**
diff --git a/src/site/xdoc/docs/json-rpc-mcp-guide.xml 
b/src/site/xdoc/docs/json-rpc-mcp-guide.xml
index 44c5128cf1..03a6180fe6 100644
--- a/src/site/xdoc/docs/json-rpc-mcp-guide.xml
+++ b/src/site/xdoc/docs/json-rpc-mcp-guide.xml
@@ -399,6 +399,17 @@ conformance checklist when extending the catalog or adding 
new MCP client code.<
 <tr><td><code>testAnnotationsHasReadOnlyHint</code></td><td>readOnlyHint 
boolean present</td></tr>
 <tr><td><code>testAllAnnotationHintsAreBooleans</code></td><td>All MCP 
2025-03-26 hints are booleans, not strings</td></tr>
 <tr><td><code>testMcpToolsMatchOpenApiPaths</code></td><td>Every MCP tool 
endpoint has a matching OpenAPI path</td></tr>
+<tr><td><code>testTickerResolveEndpointPresentWhenConfigured</code></td><td>_meta.tickerResolveEndpoint
 appears when mcpTickerResolveService global param is set</td></tr>
+<tr><td><code>testTickerResolveEndpointAbsentWhenNotConfigured</code></td><td>tickerResolveEndpoint
 absent from _meta when param not set (no misleading field)</td></tr>
+<tr><td><code>testOperationLevelMcpDescriptionOverridesDefault</code></td><td>mcpDescription
 on AxisOperation replaces auto-generated "ServiceName: opName"</td></tr>
+<tr><td><code>testServiceLevelMcpDescriptionUsedWhenNoOperationLevel</code></td><td>Service-level
 mcpDescription used as fallback when operation has none</td></tr>
+<tr><td><code>testOperationLevelMcpDescriptionTakesPrecedenceOverServiceLevel</code></td><td>Operation-level
 wins over service-level when both set</td></tr>
+<tr><td><code>testDescriptionFallsBackToAutoGeneratedWhenNoMcpDescriptionParam</code></td><td>Auto-generated
 fallback still produced when no mcpDescription param present</td></tr>
+<tr><td><code>testServiceLevelMcpReadOnlySetsTrueOnAnnotation</code></td><td>mcpReadOnly=true
 on service → readOnlyHint: true on all its tools</td></tr>
+<tr><td><code>testOperationLevelMcpReadOnlyOverridesServiceLevel</code></td><td>Operation-level
 mcpReadOnly takes precedence over service-level</td></tr>
+<tr><td><code>testMcpIdempotentParamSetsIdempotentHint</code></td><td>mcpIdempotent=true
 → idempotentHint: true</td></tr>
+<tr><td><code>testMcpDestructiveParamSetsDestructiveHint</code></td><td>mcpDestructive=true
 → destructiveHint: true</td></tr>
+<tr><td><code>testAnnotationDefaultsAreConservativeWhenNoParamsSet</code></td><td>All
 four hints remain false when no MCP params set (no regression)</td></tr>
 </table>
 
 <h3>6.2 Catalog HTTP Handler — McpCatalogHandlerTest (17 tests)</h3>
@@ -487,8 +498,8 @@ Each item notes whether the gap is architectural (won't be 
added to Axis2) or de
 </tr>
 <tr>
   <td>Natural language tool descriptions</td>
-  <td>Not implemented — architectural gap</td>
-  <td><code>description</code> is auto-generated as "ServiceName: 
operationName". Compare rapi-mcp's curated descriptions: "Get all assets in a 
fund with optional calculation data". If Claude or another LLM uses the catalog 
for tool selection, it will have less context. Workaround: add descriptions via 
an OpenAPI customizer.</td>
+  <td><strong>Implemented</strong> — set <code>mcpDescription</code> parameter 
on <code>&lt;operation&gt;</code> or <code>&lt;service&gt;</code> in 
services.xml</td>
+  <td>Operation-level parameter takes precedence over service-level; falls 
back to auto-generated "ServiceName: operationName".</td>
 </tr>
 <tr>
   <td>MCP Resources and Prompts</td>
@@ -497,8 +508,8 @@ Each item notes whether the gap is architectural (won't be 
added to Axis2) or de
 </tr>
 <tr>
   <td>MCP 2025-03-26 annotation tuning</td>
-  <td>Deferred</td>
-  <td>All annotations (<code>readOnlyHint</code>, 
<code>destructiveHint</code>, <code>idempotentHint</code>, 
<code>openWorldHint</code>) default to <code>false</code>. Read-only services 
like <code>GetAssetCalculationsService</code> should have <code>readOnlyHint: 
true</code>. Requires per-service configuration or introspection of service 
annotations.</td>
+  <td><strong>Implemented</strong> — set <code>mcpReadOnly</code>, 
<code>mcpIdempotent</code>, <code>mcpDestructive</code>, 
<code>mcpOpenWorld</code> on <code>&lt;operation&gt;</code> or 
<code>&lt;service&gt;</code> in services.xml</td>
+  <td>Conservative <code>false</code> defaults preserved when parameters 
absent. Operation-level overrides service-level.</td>
 </tr>
 <tr>
   <td>Streaming / SSE responses</td>
@@ -517,8 +528,8 @@ Each item notes whether the gap is architectural (won't be 
added to Axis2) or de
 </tr>
 <tr>
   <td>Natural key resolution (ticker → assetId)</td>
-  <td>Not in catalog — available via service</td>
-  <td>Ticker resolution exists as an Axis2 service (used by pyRapi's 
<code>search_assets</code> tool) but the catalog does not expose a dedicated 
resolution endpoint. Callers must know fund IDs and asset IDs or call the 
appropriate lookup service separately.</td>
+  <td><strong>Implemented</strong> — set global parameter 
<code>mcpTickerResolveService</code> in axis2.xml</td>
+  <td>When set to <code>ServiceName/operationName</code>, 
<code>_meta.tickerResolveEndpoint</code> is added to the catalog. Omitted 
entirely when not configured so deployments without a ticker service are 
unaffected.</td>
 </tr>
 <tr>
   <td>Cursor-based pagination</td>

Reply via email to