diff --git a/api/src/org/labkey/api/mcp/AbstractAgentAction.java b/api/src/org/labkey/api/mcp/AbstractAgentAction.java index 0affd4c5517..a8bb158aab1 100644 --- a/api/src/org/labkey/api/mcp/AbstractAgentAction.java +++ b/api/src/org/labkey/api/mcp/AbstractAgentAction.java @@ -85,9 +85,18 @@ protected String handleEscape(String prompt) @Override public void validateForm(F form, Errors errors) { + if (!McpService.get().isAIFeaturesEnabled()) + { + errors.reject(ERROR_GENERIC, "Agent actions are not available."); + return; + } + String prompt = form.getPrompt(); if (prompt != null && prompt.length() > MAX_PROMPT_CHAR_LENGTH) + { errors.rejectValue("prompt", ERROR_GENERIC, "Prompt cannot exceed " + MAX_PROMPT_CHAR_LENGTH + " characters."); + return; + } // Only honor a client-supplied conversationId if this agent previously issued this session that // id. Otherwise, generate a fresh one. This prevents a same-session caller from splicing into diff --git a/api/src/org/labkey/api/mcp/McpService.java b/api/src/org/labkey/api/mcp/McpService.java index 4717e859b78..06afb51e63a 100644 --- a/api/src/org/labkey/api/mcp/McpService.java +++ b/api/src/org/labkey/api/mcp/McpService.java @@ -99,6 +99,7 @@ public interface McpService extends ToolCallbackProvider Logger LOG = LogHelper.getLogger(McpService.class, "MCP registration exceptions"); String ENABLE_MCP_SERVER_FLAG = "enableMcpServer"; + String ENABLE_AI_FEATURES = "enableAIFeatures"; // Interface for MCP classes that we will "ingest" using Spring annotations. Provides a few helper methods. interface McpImpl @@ -120,9 +121,6 @@ default User getUser(ToolContext toolContext) // Every MCP resource should call this on every invocation default void incrementResourceRequestCount(String resource) { - if (!get().isEnabled()) - throw new RuntimeException("The MCP server is not enabled for external requests. Consider toggling the optional feature flag."); - get().incrementResourceRequestCount(resource); } } @@ -145,8 +143,19 @@ default boolean isEnabled() return OptionalFeatureService.get().isFeatureEnabled(ENABLE_MCP_SERVER_FLAG); } + default boolean isAIFeaturesEnabled() + { + return OptionalFeatureService.get().isFeatureEnabled(ENABLE_AI_FEATURES); + } + boolean isReady(); + // Convenience for in-product AI features: the AI feature flag is on AND the service has started. + default boolean isAIFeaturesReady() + { + return isAIFeaturesEnabled() && isReady(); + } + // Register MCPs in Module.startup() default void register(McpImpl mcp) { diff --git a/api/src/org/labkey/api/mcp/NoopMcpService.java b/api/src/org/labkey/api/mcp/NoopMcpService.java index 2a0ff1c2f9a..1923c770ccd 100644 --- a/api/src/org/labkey/api/mcp/NoopMcpService.java +++ b/api/src/org/labkey/api/mcp/NoopMcpService.java @@ -42,6 +42,12 @@ public boolean isEnabled() return false; } + @Override + public boolean isAIFeaturesEnabled() + { + return false; + } + @Override public boolean isReady() { diff --git a/api/src/org/labkey/api/util/PageFlowUtil.java b/api/src/org/labkey/api/util/PageFlowUtil.java index 520a6c2d171..b4a80bf1bed 100644 --- a/api/src/org/labkey/api/util/PageFlowUtil.java +++ b/api/src/org/labkey/api/util/PageFlowUtil.java @@ -2265,8 +2265,7 @@ public static JSONObject jsInitObject(ContainerUser context, @Nullable PageConfi if (AppProps.getInstance().isOptionalFeatureEnabled(NotificationMenuView.EXPERIMENTAL_NOTIFICATION_MENU) && user != null) json.put("notifications", Map.of("unreadCount", NotificationService.get().getUnreadNotificationCountByUser(null, user.getUserId()))); - if (McpService.get().isEnabled()) - json.put("mcpReady", McpService.get().isReady()); + json.put("mcpReady", McpService.get().isAIFeaturesReady()); JSONObject defaultHeaders = new JSONObject(); defaultHeaders.put("X-ONUNAUTHORIZED", "UNAUTHORIZED"); diff --git a/core/src/org/labkey/core/admin/sql/SqlScriptController.java b/core/src/org/labkey/core/admin/sql/SqlScriptController.java index 575d44a361a..42305e175b7 100644 --- a/core/src/org/labkey/core/admin/sql/SqlScriptController.java +++ b/core/src/org/labkey/core/admin/sql/SqlScriptController.java @@ -1122,7 +1122,7 @@ protected void renderScript(SqlScript script, PrintWriter out) protected void renderButtons(SqlScript script, PrintWriter out) { out.println(PageFlowUtil.button("Reorder Script").href(getScriptURL(ReorderScriptAction.class, script))); - if (McpService.get().isReady()) + if (McpService.get().isAIFeaturesReady()) { out.println( PageFlowUtil.button("Clean Up Script") @@ -1259,7 +1259,7 @@ protected abstract class BaseAIScriptAction extends ScriptAction protected ModelAndView getScriptView(SqlScript script) { McpService mcpService = McpService.get(); - if (mcpService.isReady()) + if (mcpService.isAIFeaturesReady()) { String prompt = getPrompt(DbScope.getLabKeyScope().getSqlDialect(), script); HttpSession session = getViewContext().getSession(); diff --git a/devtools/src/org/labkey/devtools/TestController.java b/devtools/src/org/labkey/devtools/TestController.java index c7171d20189..d4b39982f9b 100644 --- a/devtools/src/org/labkey/devtools/TestController.java +++ b/devtools/src/org/labkey/devtools/TestController.java @@ -1285,7 +1285,7 @@ public static class ChatAction extends SimpleViewAction @Override public ModelAndView getView(Object o, BindException errors) { - if (null == McpService.get() || !McpService.get().isReady()) + if (null == McpService.get() || !McpService.get().isAIFeaturesReady()) return HtmlView.of("Service is not ready yet."); getPageConfig().setTemplate(PageConfig.Template.Dialog); return new JspView<>("/org/labkey/devtools/view/chat.jsp"); diff --git a/devtools/src/org/labkey/devtools/view/chat.jsp b/devtools/src/org/labkey/devtools/view/chat.jsp index 2799776390d..19b8defd8e5 100644 --- a/devtools/src/org/labkey/devtools/view/chat.jsp +++ b/devtools/src/org/labkey/devtools/view/chat.jsp @@ -15,10 +15,6 @@ * limitations under the License. */ %> -<%@ page import="org.labkey.api.util.DOM" %> -<%@ page import="java.util.stream.Stream" %> -<%@ page import="static org.labkey.api.util.DOM.*" %> -<%@ page import="static org.labkey.api.util.DOM.Attribute.*" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %>