From 6c1a592ad7080f397aef8cd7acece66176f6345f Mon Sep 17 00:00:00 2001 From: Deval Patel Date: Wed, 8 Jul 2026 15:04:53 +0100 Subject: [PATCH 1/3] Update MXBean to API project --- .../mcp/monitor/McpOperationStatsMXBean.java | 126 ++++++++++++++++++ .../monitor/McpSessionStatisticsMXBean.java | 80 +++++++++++ .../openliberty/mcp/monitor/package-info.java | 15 +++ 3 files changed, 221 insertions(+) create mode 100644 dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpOperationStatsMXBean.java create mode 100644 dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpSessionStatisticsMXBean.java create mode 100644 dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/package-info.java diff --git a/dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpOperationStatsMXBean.java b/dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpOperationStatsMXBean.java new file mode 100644 index 00000000000..1d8b283733f --- /dev/null +++ b/dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpOperationStatsMXBean.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2026 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package io.openliberty.mcp.monitor; + +/** + * Management interface for MCP operation statistics. + *

+ * This MXBean exposes monitoring information for MCP operations, including + * operation attributes, invocation count, and total duration. + *

+ * Each MXBean instance represents statistics for a unique set of MCP operation + * attributes, such as method name, tool name, error type, and response status. + * The values are intended to be consumed through JMX by administrators, + * monitoring tools, and applications that need to observe MCP server behavior. + */ +public interface McpOperationStatsMXBean { + + /** + * Returns the MCP JSON-RPC method name associated with this operation. + * + * @return the MCP method name, for example {@code tools/call}, or + * {@code null} if the method name is not available. + */ + String getMcpMethodName(); + + /** + * Returns the error type associated with this operation. + * + * @return the error type, or {@code null} if no error type was recorded. + */ + String getErrorType(); + + /** + * Returns the GenAI prompt name associated with this operation. + * + * @return the GenAI prompt name, or {@code null} if not applicable. + */ + String getGenAiPromptName(); + + /** + * Returns the GenAI tool name associated with this operation. + * + * @return the GenAI tool name, or {@code null} if not applicable. + */ + String getGenAiToolName(); + + /** + * Returns the JSON-RPC response status code associated with this operation. + * + * @return the JSON-RPC response status code, or {@code null} if unavailable. + */ + String getRpcResponseStatusCode(); + + /** + * Returns the GenAI operation name associated with this operation. + * + * @return the GenAI operation name, or {@code null} if unavailable. + */ + String getGenAiOperationName(); + + /** + * Returns the JSON-RPC protocol version associated with this operation. + * + * @return the JSON-RPC protocol version, or {@code null} if unavailable. + */ + String getJsonrpcProtocolVersion(); + + /** + * Returns the MCP protocol version associated with this operation. + * + * @return the MCP protocol version, or {@code null} if unavailable. + */ + String getMcpProtocolVersion(); + + /** + * Returns the network protocol name used for this operation. + * + * @return the network protocol name, or {@code null} if unavailable. + */ + String getNetworkProtocolName(); + + /** + * Returns the network protocol version used for this operation. + * + * @return the network protocol version, or {@code null} if unavailable. + */ + String getNetworkProtocolVersion(); + + /** + * Returns the network transport used for this operation. + * + * @return the network transport, or {@code null} if unavailable. + */ + String getNetworkTransport(); + + /** + * Returns the MCP resource URI associated with this operation. + * + * @return the MCP resource URI, or {@code null} if unavailable. + */ + String getMcpResourceUri(); + + /** + * Returns the number of times this operation has been recorded. + * + * @return the operation count. + */ + long getCount(); + + /** + * Returns the total recorded duration for this operation. + *

+ * The duration is reported in nanoseconds. + * + * @return the total operation duration in nanoseconds. + */ + double getDuration(); +} \ No newline at end of file diff --git a/dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpSessionStatisticsMXBean.java b/dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpSessionStatisticsMXBean.java new file mode 100644 index 00000000000..9c054d0d398 --- /dev/null +++ b/dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpSessionStatisticsMXBean.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2026 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.mcp.monitor; + +/** + * Management interface for MCP session statistics. + *

+ * This MXBean exposes monitoring information for MCP sessions, including + * protocol and network attributes, session count, and total session duration. + *

+ * The values are intended to be consumed through JMX by administrators, + * monitoring tools, and applications that need to observe MCP server behavior. + */ +public interface McpSessionStatisticsMXBean { + + /** + * Returns the error type associated with the session. + * + * @return the error type, or {@code null} if no error type was recorded. + */ + String getErrorType(); + + /** + * Returns the JSON-RPC protocol version associated with the session. + * + * @return the JSON-RPC protocol version, or {@code null} if unavailable. + */ + String getJsonrpcProtocolVersion(); + + /** + * Returns the MCP protocol version associated with the session. + * + * @return the MCP protocol version, or {@code null} if unavailable. + */ + String getMcpProtocolVersion(); + + /** + * Returns the network protocol name used for the session. + * + * @return the network protocol name, or {@code null} if unavailable. + */ + String getNetworkProtocolName(); + + /** + * Returns the network protocol version used for the session. + * + * @return the network protocol version, or {@code null} if unavailable. + */ + String getNetworkProtocolVersion(); + + /** + * Returns the network transport used for the session. + * + * @return the network transport, or {@code null} if unavailable. + */ + String getNetworkTransport(); + + /** + * Returns the number of sessions recorded for this set of attributes. + * + * @return the session count. + */ + long getCount(); + + /** + * Returns the total recorded session duration. + *

+ * The duration is reported in nanoseconds. + * + * @return the total session duration in nanoseconds. + */ + double getDuration(); +} \ No newline at end of file diff --git a/dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/package-info.java b/dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/package-info.java new file mode 100644 index 00000000000..1d221ff540e --- /dev/null +++ b/dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/package-info.java @@ -0,0 +1,15 @@ +/******************************************************************************* + * Copyright (c) 2025 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +/** + * MCP tools, resources and prompts can be declared using the annotations in this package. + */ +@com.ibm.websphere.ras.annotation.TraceOptions(traceGroup = "MCP") +@org.osgi.annotation.versioning.Version("1.0") +package io.openliberty.mcp.monitor; \ No newline at end of file From c8894600676fb39dab0870822077b5cc568e42bb Mon Sep 17 00:00:00 2001 From: Deval Patel Date: Wed, 8 Jul 2026 17:17:10 +0100 Subject: [PATCH 2/3] Renaming files for consistency --- .../mcp/internal/monitor/McpOperationStatistics.java | 6 +++--- ...onStatsMXBean.java => McpOperationStatisticsMXBean.java} | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/{McpOperationStatsMXBean.java => McpOperationStatisticsMXBean.java} (98%) diff --git a/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/internal/monitor/McpOperationStatistics.java b/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/internal/monitor/McpOperationStatistics.java index 9017e37df51..fce46ccaeaf 100644 --- a/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/internal/monitor/McpOperationStatistics.java +++ b/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/internal/monitor/McpOperationStatistics.java @@ -16,7 +16,7 @@ import com.ibm.websphere.ras.TraceComponent; import io.openliberty.mcp.internal.monitor.metrics.McpOperationStatAttributes; -import io.openliberty.mcp.monitor.McpOperationStatsMXBean; +import io.openliberty.mcp.monitor.McpOperationStatisticsMXBean; /** * Records statistics for MCP (Model Context Protocol) operations. @@ -25,10 +25,10 @@ * A new instance is created for each unique combination of operation attributes (method name, * tool name, error type, etc.). All instances are managed by {@link McpStatsMonitorImpl}. *

- * The statistics are exposed via JMX through the {@link McpOperationStatsMXBean} interface + * The statistics are exposed via JMX through the {@link McpOperationStatisticsMXBean} interface * and can be consumed by monitoring systems like MicroProfile Metrics and MicroProfile Telemetry. */ -public class McpOperationStatistics extends Meter implements McpOperationStatsMXBean { +public class McpOperationStatistics extends Meter implements McpOperationStatisticsMXBean { private static final TraceComponent tc = Tr.register(McpOperationStatistics.class); private final String mcpMethodName; diff --git a/dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpOperationStatsMXBean.java b/dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpOperationStatisticsMXBean.java similarity index 98% rename from dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpOperationStatsMXBean.java rename to dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpOperationStatisticsMXBean.java index 1d8b283733f..47ee5c79060 100644 --- a/dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpOperationStatsMXBean.java +++ b/dev/io.openliberty.mcp/src/io/openliberty/mcp/monitor/McpOperationStatisticsMXBean.java @@ -21,7 +21,7 @@ * The values are intended to be consumed through JMX by administrators, * monitoring tools, and applications that need to observe MCP server behavior. */ -public interface McpOperationStatsMXBean { +public interface McpOperationStatisticsMXBean { /** * Returns the MCP JSON-RPC method name associated with this operation. From 08be7befd0b0ef9dfd3f6b0cc8c00e5cd1c2a994 Mon Sep 17 00:00:00 2001 From: Deval Patel Date: Fri, 10 Jul 2026 14:44:44 +0100 Subject: [PATCH 3/3] fixes review comments --- .../io.openliberty.mcpServer-1.0.feature | 1 + .../monitor/McpOperationStatistics.java | 16 +- .../monitor/McpSessionStatistics.java | 16 +- .../mcp/monitor/McpOperationStatsMXBean.java | 143 ------------- .../mcp/monitor/McpSessionStatsMXBean.java | 97 --------- .../openliberty/mcp/monitor/package-info.java | 15 -- .../mcpstatstests/McpMXBeanApiAccessTest.java | 194 ++++++++++++++++++ .../McpOperationStatsJMXTest.java | 9 - .../mcpstatstests/McpSessionStatsJMXTest.java | 19 -- .../internal/fat/monitor/McpMonitorTest.java | 4 - 10 files changed, 207 insertions(+), 307 deletions(-) delete mode 100644 dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/monitor/McpOperationStatsMXBean.java delete mode 100644 dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/monitor/McpSessionStatsMXBean.java delete mode 100644 dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/monitor/package-info.java create mode 100644 dev/io.openliberty.mcp.internal.monitor/test/io/openliberty/mcp/internal/monitor/mcpstatstests/McpMXBeanApiAccessTest.java diff --git a/dev/com.ibm.websphere.appserver.features/visibility/public/mcpServer-1.0/io.openliberty.mcpServer-1.0.feature b/dev/com.ibm.websphere.appserver.features/visibility/public/mcpServer-1.0/io.openliberty.mcpServer-1.0.feature index b171958d28b..e877d4633f7 100644 --- a/dev/com.ibm.websphere.appserver.features/visibility/public/mcpServer-1.0/io.openliberty.mcpServer-1.0.feature +++ b/dev/com.ibm.websphere.appserver.features/visibility/public/mcpServer-1.0/io.openliberty.mcpServer-1.0.feature @@ -6,6 +6,7 @@ IBM-App-ForceRestart: install, \ IBM-API-Package: \ io.openliberty.mcp.annotations; type="ibm-api", \ io.openliberty.mcp.features; type="ibm-api", \ + io.openliberty.mcp.monitor; type="ibm-api", \ io.openliberty.mcp.tools; type="ibm-api", \ org.mcpjava.server; type="stable", \ org.mcpjava.server.completion; type="stable", \ diff --git a/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/internal/monitor/McpOperationStatistics.java b/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/internal/monitor/McpOperationStatistics.java index fce46ccaeaf..997d527ed8b 100644 --- a/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/internal/monitor/McpOperationStatistics.java +++ b/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/internal/monitor/McpOperationStatistics.java @@ -9,9 +9,7 @@ *******************************************************************************/ package io.openliberty.mcp.internal.monitor; -import com.ibm.websphere.monitor.meters.Counter; import com.ibm.websphere.monitor.meters.Meter; -import com.ibm.websphere.monitor.meters.StatisticsMeter; import com.ibm.websphere.ras.Tr; import com.ibm.websphere.ras.TraceComponent; @@ -43,8 +41,8 @@ public class McpOperationStatistics extends Meter implements McpOperationStatist */ private final String genAiOperationName, jsonrpcProtocolVersion, mcpProtocolVersion, networkProtocolName, networkProtocolVersion, networkTransport, mcpResourceUri; - private Counter operationCount; - private StatisticsMeter operationDuration; + private com.ibm.websphere.monitor.meters.Counter operationCount; + private com.ibm.websphere.monitor.meters.StatisticsMeter operationDuration; public McpOperationStatistics(McpOperationStatAttributes mcpStatAttributes) { this.mcpMethodName = mcpStatAttributes.mcpMethodName(); @@ -60,10 +58,10 @@ public McpOperationStatistics(McpOperationStatAttributes mcpStatAttributes) { this.networkTransport = mcpStatAttributes.networkTransport(); this.mcpResourceUri = mcpStatAttributes.mcpResourceUri(); - operationCount = new Counter(); + operationCount = new com.ibm.websphere.monitor.meters.Counter(); operationCount.setDescription(Tr.formatMessage(tc, "mcp.operation.count.description")); - operationDuration = new StatisticsMeter(); + operationDuration = new com.ibm.websphere.monitor.meters.StatisticsMeter(); operationDuration.setDescription(Tr.formatMessage(tc, "mcp.operation.duration.description")); operationDuration.setUnit("nanoseconds"); @@ -143,8 +141,7 @@ public long getCount() { return count; } - @Override - public Counter getCountDetails() { + public com.ibm.websphere.monitor.jmx.Counter getCountDetails() { return operationCount; } @@ -153,8 +150,7 @@ public double getDuration() { return operationDuration.getTotal(); } - @Override - public StatisticsMeter getDurationDetails() { + public com.ibm.websphere.monitor.jmx.StatisticsMeter getDurationDetails() { return operationDuration; } diff --git a/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/internal/monitor/McpSessionStatistics.java b/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/internal/monitor/McpSessionStatistics.java index b636f348d12..3e5918d2100 100644 --- a/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/internal/monitor/McpSessionStatistics.java +++ b/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/internal/monitor/McpSessionStatistics.java @@ -16,9 +16,9 @@ import com.ibm.websphere.ras.TraceComponent; import io.openliberty.mcp.internal.monitor.metrics.McpSessionStatAttributes; -import io.openliberty.mcp.monitor.McpSessionStatsMXBean; +import io.openliberty.mcp.monitor.McpSessionStatisticsMXBean; -public class McpSessionStatistics extends Meter implements McpSessionStatsMXBean { +public class McpSessionStatistics extends Meter implements McpSessionStatisticsMXBean { private static final TraceComponent tc = Tr.register(McpSessionStatistics.class); @@ -96,21 +96,17 @@ public long getCount() { return sessionCount.getCurrentValue(); } - @Override - public com.ibm.websphere.monitor.jmx.Counter getCountDetails() { - return sessionCount; - } - @Override public double getDuration() { return sessionDuration.getTotal(); } - @Override + public com.ibm.websphere.monitor.jmx.Counter getCountDetails() { + return sessionCount; + } + public com.ibm.websphere.monitor.jmx.StatisticsMeter getDurationDetails() { return sessionDuration; } } - -// Made with Bob diff --git a/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/monitor/McpOperationStatsMXBean.java b/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/monitor/McpOperationStatsMXBean.java deleted file mode 100644 index 2c8ada59cb4..00000000000 --- a/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/monitor/McpOperationStatsMXBean.java +++ /dev/null @@ -1,143 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2026 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package io.openliberty.mcp.monitor; - -import com.ibm.websphere.monitor.jmx.Counter; -import com.ibm.websphere.monitor.jmx.StatisticsMeter; - -/** - * Management interface for MCP operation statistics. - *

- * This MXBean exposes monitoring information for MCP operations, including - * operation attributes, invocation count, and total duration. - *

- * Each MXBean instance represents statistics for a unique set of MCP operation - * attributes, such as method name, tool name, error type, and response status. - * The values are intended to be consumed through JMX by administrators, - * monitoring tools, and applications that need to observe MCP server behavior. - */ -public interface McpOperationStatsMXBean { - - /** - * Returns the MCP JSON-RPC method name associated with this operation. - * This value is always present as it is a required attribute. - * - * @return the MCP method name, for example {@code tools/call} - */ - String getMcpMethodName(); - - /** - * Returns the error type associated with this operation. - * - * @return the error type, or {@code null} if no error type was recorded. - */ - String getErrorType(); - - /** - * Returns the GenAI prompt name associated with this operation. - * - * @return the GenAI prompt name, or {@code null} if not applicable. - */ - String getGenAiPromptName(); - - /** - * Returns the GenAI tool name associated with this operation. - * - * @return the GenAI tool name, or {@code null} if not applicable. - */ - String getGenAiToolName(); - - /** - * Returns the JSON-RPC response status code associated with this operation. - * - * @return the JSON-RPC response status code, or {@code null} if unavailable. - */ - String getRpcResponseStatusCode(); - - /** - * Returns the GenAI operation name associated with this operation. - * - * @return the GenAI operation name, or {@code null} if unavailable. - */ - String getGenAiOperationName(); - - /** - * Returns the JSON-RPC protocol version associated with this operation. - * - * @return the JSON-RPC protocol version, or {@code null} if unavailable. - */ - String getJsonrpcProtocolVersion(); - - /** - * Returns the MCP protocol version associated with this operation. - * - * @return the MCP protocol version, or {@code null} if unavailable. - */ - String getMcpProtocolVersion(); - - /** - * Returns the network protocol name used for this operation. - * - * @return the network protocol name, or {@code null} if unavailable. - */ - String getNetworkProtocolName(); - - /** - * Returns the network protocol version used for this operation. - * - * @return the network protocol version, or {@code null} if unavailable. - */ - String getNetworkProtocolVersion(); - - /** - * Returns the network transport used for this operation. - * - * @return the network transport, or {@code null} if unavailable. - */ - String getNetworkTransport(); - - /** - * Returns the MCP resource URI associated with this operation. - * - * @return the MCP resource URI, or {@code null} if unavailable. - */ - String getMcpResourceUri(); - - /** - * Returns the number of times this operation has been recorded. - * - * @return the operation count. - */ - long getCount(); - - /** - * Returns more detail about the count of recorded operation executions - * - * @return operation count details - */ - Counter getCountDetails(); - - /** - * Returns the total recorded duration for this operation. - *

- * The duration is reported in nanoseconds. - * - * @return the total operation duration in nanoseconds. - */ - double getDuration(); - - /** - * Returns statistical details on the duration the recorded executions of this operation. - * - * @return statistical details about the duration of executions of this operation - */ - StatisticsMeter getDurationDetails(); -} diff --git a/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/monitor/McpSessionStatsMXBean.java b/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/monitor/McpSessionStatsMXBean.java deleted file mode 100644 index cbc3c410bdc..00000000000 --- a/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/monitor/McpSessionStatsMXBean.java +++ /dev/null @@ -1,97 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2026 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package io.openliberty.mcp.monitor; - -import com.ibm.websphere.monitor.jmx.Counter; -import com.ibm.websphere.monitor.jmx.StatisticsMeter; - -/** - * Management interface for MCP session statistics. - *

- * This MXBean exposes monitoring information for MCP sessions, including - * protocol and network attributes, session count, and total session duration. - *

- * The values are intended to be consumed through JMX by administrators, - * monitoring tools, and applications that need to observe MCP server behavior. - */ -public interface McpSessionStatsMXBean { - - /** - * Returns the error type associated with the session. - * - * @return the error type, or {@code null} if no error type was recorded. - */ - String getErrorType(); - - /** - * Returns the JSON-RPC protocol version associated with the session. - * - * @return the JSON-RPC protocol version, or {@code null} if unavailable. - */ - String getJsonrpcProtocolVersion(); - - /** - * Returns the MCP protocol version associated with the session. - * - * @return the MCP protocol version, or {@code null} if unavailable. - */ - String getMcpProtocolVersion(); - - /** - * Returns the network protocol name used for the session. - * - * @return the network protocol name, or {@code null} if unavailable. - */ - String getNetworkProtocolName(); - - /** - * Returns the network protocol version used for the session. - * - * @return the network protocol version, or {@code null} if unavailable. - */ - String getNetworkProtocolVersion(); - - /** - * Returns the network transport used for the session. - * - * @return the network transport, or {@code null} if unavailable. - */ - String getNetworkTransport(); - - /** - * Returns the number of sessions recorded for this set of attributes. - * - * @return the session count. - */ - long getCount(); - - /** - * Returns more detail about the session count - * - * @return session count details - */ - Counter getCountDetails(); - - /** - * Returns the total recorded session duration. - *

- * The duration is reported in nanoseconds. - * - * @return the total session duration in nanoseconds. - */ - double getDuration(); - - /** - * Returns statistical details on the duration of sessions. - * - * @return statistical details about the duration sessions - */ - public StatisticsMeter getDurationDetails(); -} diff --git a/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/monitor/package-info.java b/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/monitor/package-info.java deleted file mode 100644 index 8e5714e5072..00000000000 --- a/dev/io.openliberty.mcp.internal.monitor/src/io/openliberty/mcp/monitor/package-info.java +++ /dev/null @@ -1,15 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2026 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -/** - * - */ -@com.ibm.websphere.ras.annotation.TraceOptions(traceGroup = "MCP") -@org.osgi.annotation.versioning.Version("1.0") -package io.openliberty.mcp.monitor; \ No newline at end of file diff --git a/dev/io.openliberty.mcp.internal.monitor/test/io/openliberty/mcp/internal/monitor/mcpstatstests/McpMXBeanApiAccessTest.java b/dev/io.openliberty.mcp.internal.monitor/test/io/openliberty/mcp/internal/monitor/mcpstatstests/McpMXBeanApiAccessTest.java new file mode 100644 index 00000000000..daf2c22cdfa --- /dev/null +++ b/dev/io.openliberty.mcp.internal.monitor/test/io/openliberty/mcp/internal/monitor/mcpstatstests/McpMXBeanApiAccessTest.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * Copyright (c) 2026 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.mcp.internal.monitor.mcpstatstests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.lang.management.ManagementFactory; + +import javax.management.JMX; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import io.openliberty.mcp.internal.monitor.McpOperationStatistics; +import io.openliberty.mcp.internal.monitor.McpSessionStatistics; +import io.openliberty.mcp.internal.monitor.metrics.McpOperationStatAttributes; +import io.openliberty.mcp.internal.monitor.metrics.McpSessionStatAttributes; +import io.openliberty.mcp.monitor.McpOperationStatisticsMXBean; +import io.openliberty.mcp.monitor.McpSessionStatisticsMXBean; + +/** + * Tests that the MXBeans are accessible through the public API interfaces in + * io.openliberty.mcp.monitor, as an application would access them via JMX. + * + * Application code only sees the API interfaces — it never references the + * internal implementation classes. These tests simulate that by obtaining a + * typed MXBean proxy via {@link JMX#newMXBeanProxy} and interacting solely + * through the interface. + */ +public class McpMXBeanApiAccessTest { + + private MBeanServer mbs; + private ObjectName operationName; + private ObjectName sessionName; + + @Before + public void setUp() throws Exception { + mbs = ManagementFactory.getPlatformMBeanServer(); + operationName = new ObjectName("io.openliberty.mcp.test:type=McpOperationStatistics,name=apitest"); + sessionName = new ObjectName("io.openliberty.mcp.test:type=McpSessionStatistics,name=apitest"); + } + + @After + public void tearDown() throws Exception { + if (mbs.isRegistered(operationName)) { + mbs.unregisterMBean(operationName); + } + if (mbs.isRegistered(sessionName)) { + mbs.unregisterMBean(sessionName); + } + } + + // ----------------------------------------------------------------------- + // McpOperationStatisticsMXBean + // ----------------------------------------------------------------------- + + /** + * Registers an McpOperationStatistics MBean, then accesses it through + * a typed proxy using only the public McpOperationStatisticsMXBean interface — + * the same way application code would use JMX to observe MCP operation stats. + */ + @Test + public void testOperationMXBeanAccessibleViaPublicInterface() throws Exception { + McpOperationStatAttributes attrs = McpOperationStatAttributes.builder() + .withMcpMethodName("tools/call") + .withGenAiToolName("myTool") + .withRpcResponseStatusCode("ok") + .withJsonrpcProtocolVersion("2.0") + .withMcpProtocolVersion("V_2025_11_25") + .withNetworkProtocolName("HTTP") + .withNetworkProtocolVersion("1.1") + .withNetworkTransport("tcp") + .build().get(); + + McpOperationStatistics impl = new McpOperationStatistics(attrs); + impl.incrementOperationCountBy(3); + impl.addOperationTimeStat(500_000_000L); // 0.5 s in nanos + + mbs.registerMBean(impl, operationName); + + // Application accesses the MBean only through the public API interface + McpOperationStatisticsMXBean proxy = JMX.newMXBeanProxy(mbs, operationName, McpOperationStatisticsMXBean.class); + + assertNotNull("Proxy must not be null", proxy); + assertEquals("tools/call", proxy.getMcpMethodName()); + assertEquals("myTool", proxy.getGenAiToolName()); + assertEquals("ok", proxy.getRpcResponseStatusCode()); + assertEquals("2.0", proxy.getJsonrpcProtocolVersion()); + assertEquals("V_2025_11_25", proxy.getMcpProtocolVersion()); + assertEquals("HTTP", proxy.getNetworkProtocolName()); + assertEquals("1.1", proxy.getNetworkProtocolVersion()); + assertEquals("tcp", proxy.getNetworkTransport()); + assertEquals(3L, proxy.getCount()); + assertEquals(500_000_000.0, proxy.getDuration(), 1.0); + } + + /** + * Verifies that optional attributes return null when not set, + * accessed through the public McpOperationStatisticsMXBean interface. + */ + @Test + public void testOperationMXBeanNullOptionalAttributesViaPublicInterface() throws Exception { + McpOperationStatAttributes attrs = McpOperationStatAttributes.builder() + .withMcpMethodName("initialize") + .build().get(); + + mbs.registerMBean(new McpOperationStatistics(attrs), operationName); + + McpOperationStatisticsMXBean proxy = JMX.newMXBeanProxy(mbs, operationName, McpOperationStatisticsMXBean.class); + + assertEquals("initialize", proxy.getMcpMethodName()); + assertNull("errorType should be null when not set", proxy.getErrorType()); + assertNull("genAiToolName should be null when not set", proxy.getGenAiToolName()); + assertNull("genAiPromptName should be null when not set", proxy.getGenAiPromptName()); + assertNull("mcpResourceUri should be null when not set", proxy.getMcpResourceUri()); + assertEquals(0L, proxy.getCount()); + assertEquals(0.0, proxy.getDuration(), 0.0); + } + + // ----------------------------------------------------------------------- + // McpSessionStatisticsMXBean + // ----------------------------------------------------------------------- + + /** + * Registers an McpSessionStatistics MBean, then accesses it through + * a typed proxy using only the public McpSessionStatisticsMXBean interface — + * the same way application code would use JMX to observe MCP session stats. + */ + @Test + public void testSessionMXBeanAccessibleViaPublicInterface() throws Exception { + McpSessionStatAttributes attrs = McpSessionStatAttributes.builder() + .withErrorType("timeout") + .withJsonrpcProtocolVersion("2.0") + .withMcpProtocolVersion("V_2025_11_25") + .withNetworkProtocolName("HTTP") + .withNetworkProtocolVersion("1.1") + .withNetworkTransport("tcp") + .build().get(); + + McpSessionStatistics impl = new McpSessionStatistics(attrs); + impl.incrementSessionCountBy(2); + impl.addSessionDurationStat(1_000_000_000L); // 1 s + impl.addSessionDurationStat(2_000_000_000L); // 2 s + + mbs.registerMBean(impl, sessionName); + + // Application accesses the MBean only through the public API interface + McpSessionStatisticsMXBean proxy = JMX.newMXBeanProxy(mbs, sessionName, McpSessionStatisticsMXBean.class); + + assertNotNull("Proxy must not be null", proxy); + assertEquals("timeout", proxy.getErrorType()); + assertEquals("2.0", proxy.getJsonrpcProtocolVersion()); + assertEquals("V_2025_11_25", proxy.getMcpProtocolVersion()); + assertEquals("HTTP", proxy.getNetworkProtocolName()); + assertEquals("1.1", proxy.getNetworkProtocolVersion()); + assertEquals("tcp", proxy.getNetworkTransport()); + assertEquals(2L, proxy.getCount()); + assertEquals(3_000_000_000.0, proxy.getDuration(), 1.0); + } + + /** + * Verifies that optional session attributes return null when not set, + * accessed through the public McpSessionStatisticsMXBean interface. + */ + @Test + public void testSessionMXBeanNullOptionalAttributesViaPublicInterface() throws Exception { + McpSessionStatAttributes attrs = McpSessionStatAttributes.builder() + .build().get(); + + mbs.registerMBean(new McpSessionStatistics(attrs), sessionName); + + McpSessionStatisticsMXBean proxy = JMX.newMXBeanProxy(mbs, sessionName, McpSessionStatisticsMXBean.class); + + assertNull("errorType should be null when not set", proxy.getErrorType()); + assertNull("jsonrpcProtocolVersion should be null when not set", proxy.getJsonrpcProtocolVersion()); + assertNull("mcpProtocolVersion should be null when not set", proxy.getMcpProtocolVersion()); + assertNull("networkProtocolName should be null when not set", proxy.getNetworkProtocolName()); + assertNull("networkTransport should be null when not set", proxy.getNetworkTransport()); + assertEquals(0L, proxy.getCount()); + assertEquals(0.0, proxy.getDuration(), 0.0); + } +} diff --git a/dev/io.openliberty.mcp.internal.monitor/test/io/openliberty/mcp/internal/monitor/mcpstatstests/McpOperationStatsJMXTest.java b/dev/io.openliberty.mcp.internal.monitor/test/io/openliberty/mcp/internal/monitor/mcpstatstests/McpOperationStatsJMXTest.java index 6d740f3b517..17ee05c3565 100644 --- a/dev/io.openliberty.mcp.internal.monitor/test/io/openliberty/mcp/internal/monitor/mcpstatstests/McpOperationStatsJMXTest.java +++ b/dev/io.openliberty.mcp.internal.monitor/test/io/openliberty/mcp/internal/monitor/mcpstatstests/McpOperationStatsJMXTest.java @@ -18,7 +18,6 @@ import javax.management.MBeanServer; import javax.management.ObjectName; -import javax.management.openmbean.CompositeData; import org.junit.After; import org.junit.Before; @@ -107,15 +106,9 @@ public void testUpdateMetricsThroughJMX() throws Exception { Long count = (Long) mbs.getAttribute(objectName, "Count"); assertEquals("Count should be updated", Long.valueOf(5), count); - var countData = (CompositeData) mbs.getAttribute(objectName, "CountDetails"); - assertEquals("Count should be updated", 5L, countData.get("currentValue")); - Double duration = (Double) mbs.getAttribute(objectName, "Duration"); assertEquals("Duration should be updated", 1_000_000_000.0, duration, 1.0); - var durationData = (CompositeData) mbs.getAttribute(objectName, "DurationDetails"); - assertEquals("Duration should be updated", 1_000_000_000.0, (Double) durationData.get("mean"), 1.0); - assertEquals("DurationDetails count should be updated", Long.valueOf(1), durationData.get("count")); } @Test @@ -131,9 +124,7 @@ public void testGetAllAttributes() throws Exception { assertNotNull(mbs.getAttribute(objectName, "NetworkProtocolVersion")); assertNotNull(mbs.getAttribute(objectName, "NetworkTransport")); assertNotNull(mbs.getAttribute(objectName, "Count")); - assertNotNull(mbs.getAttribute(objectName, "CountDetails")); assertNotNull(mbs.getAttribute(objectName, "Duration")); - assertNotNull(mbs.getAttribute(objectName, "DurationDetails")); } @Test diff --git a/dev/io.openliberty.mcp.internal.monitor/test/io/openliberty/mcp/internal/monitor/mcpstatstests/McpSessionStatsJMXTest.java b/dev/io.openliberty.mcp.internal.monitor/test/io/openliberty/mcp/internal/monitor/mcpstatstests/McpSessionStatsJMXTest.java index aba8ba7008e..a608eff2963 100644 --- a/dev/io.openliberty.mcp.internal.monitor/test/io/openliberty/mcp/internal/monitor/mcpstatstests/McpSessionStatsJMXTest.java +++ b/dev/io.openliberty.mcp.internal.monitor/test/io/openliberty/mcp/internal/monitor/mcpstatstests/McpSessionStatsJMXTest.java @@ -18,7 +18,6 @@ import javax.management.MBeanServer; import javax.management.ObjectName; -import javax.management.openmbean.CompositeData; import org.junit.After; import org.junit.Before; @@ -98,15 +97,9 @@ public void testReadSessionAttributesThroughJMX() throws Exception { Long count = (Long) mbs.getAttribute(objectName, "Count"); assertEquals("Should read count through JMX", Long.valueOf(0), count); - var countData = (CompositeData) mbs.getAttribute(objectName, "CountDetails"); - assertEquals("Count should be updated", 0L, countData.get("currentValue")); - Double duration = (Double) mbs.getAttribute(objectName, "Duration"); assertEquals("Should read duration through JMX", 0.0, duration, 0.001); - var durationData = (CompositeData) mbs.getAttribute(objectName, "DurationDetails"); - assertEquals("Duration should be updated", 0.0, (Double) durationData.get("mean"), 1.0); - assertEquals("DurationDetails count should be updated", Long.valueOf(0), durationData.get("count")); } @Test @@ -121,15 +114,9 @@ public void testUpdateSessionMetricsThroughJMX() throws Exception { Long count = (Long) mbs.getAttribute(objectName, "Count"); assertEquals("Session count should be updated", Long.valueOf(3), count); - var countData = (CompositeData) mbs.getAttribute(objectName, "CountDetails"); - assertEquals("Count should be updated", 3L, countData.get("currentValue")); - Double duration = (Double) mbs.getAttribute(objectName, "Duration"); assertEquals("Session duration should be updated", 5_000_000_000.0, duration, 1.0); - var durationData = (CompositeData) mbs.getAttribute(objectName, "DurationDetails"); - assertEquals("Duration should be updated", 5_000_000_000.0, (Double) durationData.get("mean"), 1.0); - assertEquals("DurationDetails count should be updated", Long.valueOf(1), durationData.get("count")); } @Test @@ -144,9 +131,7 @@ public void testGetAllSessionAttributes() throws Exception { assertNotNull(mbs.getAttribute(objectName, "NetworkProtocolVersion")); assertNotNull(mbs.getAttribute(objectName, "NetworkTransport")); assertNotNull(mbs.getAttribute(objectName, "Count")); - assertNotNull(mbs.getAttribute(objectName, "CountDetails")); assertNotNull(mbs.getAttribute(objectName, "Duration")); - assertNotNull(mbs.getAttribute(objectName, "DurationDetails")); } @Test @@ -192,9 +177,5 @@ public void testMultipleSessionUpdates() throws Exception { assertEquals("Total duration should be sum of all sessions", 6_000_000_000.0, duration, 1.0); - var durationData = (CompositeData) mbs.getAttribute(objectName, "DurationDetails"); - assertEquals("Duration should be updated", 2_000_000_000.0, (Double) durationData.get("mean"), 1.0); - assertEquals("DurationDetails count should be updated", Long.valueOf(3), durationData.get("count")); - assertEquals("DurationDetails total should be updated", 6_000_000_000.0, durationData.get("total")); } } diff --git a/dev/io.openliberty.mcp.internal_fat/fat/src/io/openliberty/mcp/internal/fat/monitor/McpMonitorTest.java b/dev/io.openliberty.mcp.internal_fat/fat/src/io/openliberty/mcp/internal/fat/monitor/McpMonitorTest.java index 0ae35cbe8e3..688fa9239f4 100644 --- a/dev/io.openliberty.mcp.internal_fat/fat/src/io/openliberty/mcp/internal/fat/monitor/McpMonitorTest.java +++ b/dev/io.openliberty.mcp.internal_fat/fat/src/io/openliberty/mcp/internal/fat/monitor/McpMonitorTest.java @@ -771,10 +771,6 @@ public void testInitializeMetrics() throws Exception { // Verify it was called at least once long count = (Long) mbeanServer.getAttribute(mbean, "Count"); - - // Get CountDetails to see internal state - Object countDetails = mbeanServer.getAttribute(mbean, "CountDetails"); - assertTrue("Initialize should have been called at least once", count >= 1); // Verify duration was recorded