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><operation></code> or <code><service></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><operation></code> or <code><service></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>
