This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch issue/fix-cursor-mcp-registration in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-mcp-server.git
commit 208170af19c873a6a9ec1e9a9688fc73d6c807bd Author: Robert Munteanu <[email protected]> AuthorDate: Fri Feb 6 22:39:25 2026 +0100 fix: work around issue in recent Cursor versions that prevents MCP server registration Cursor tries to register for resource updates even if we don't advertise that capability. This causes an internal error to be returned and blocks the MCP server activation. We work around it by using reflection to register a no-op handler, which seems to make Cursor happy. --- .../apache/sling/mcp/server/impl/McpServlet.java | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/main/java/org/apache/sling/mcp/server/impl/McpServlet.java b/src/main/java/org/apache/sling/mcp/server/impl/McpServlet.java index a7ae71b..643265c 100644 --- a/src/main/java/org/apache/sling/mcp/server/impl/McpServlet.java +++ b/src/main/java/org/apache/sling/mcp/server/impl/McpServlet.java @@ -28,7 +28,9 @@ import io.modelcontextprotocol.common.McpTransportContext; import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.schema.jackson.DefaultJsonSchemaValidator; import io.modelcontextprotocol.server.McpServer; +import io.modelcontextprotocol.server.McpStatelessRequestHandler; import io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncPromptSpecification; +import io.modelcontextprotocol.server.McpStatelessServerHandler; import io.modelcontextprotocol.server.McpStatelessSyncServer; import io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport; import io.modelcontextprotocol.spec.McpSchema; @@ -52,6 +54,9 @@ import org.osgi.service.component.annotations.ReferencePolicy; import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.Designate; import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; import static org.osgi.service.component.annotations.ReferenceCardinality.MULTIPLE; import static org.osgi.service.component.annotations.ReferencePolicyOption.GREEDY; @@ -80,6 +85,7 @@ public class McpServlet extends SlingJakartaAllMethodsServlet { private static final long serialVersionUID = 1L; private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + private final Logger logger = LoggerFactory.getLogger(getClass()); private McpStatelessSyncServer syncServer; private HttpServletStatelessServerTransport transportProvider; private MethodHandle doGetMethod; @@ -138,6 +144,10 @@ public class McpServlet extends SlingJakartaAllMethodsServlet { .build()) .build(); + // workaround for https://github.com/modelcontextprotocol/java-sdk/issues/776 + // cursor tries to register for resource updates even if we don't advertise that capability + tryRegisterNoopResourcesSubscribeHandler(); + contributions.stream() .map(McpServerContribution::getSyncToolSpecification) .flatMap(List::stream) @@ -159,6 +169,28 @@ public class McpServlet extends SlingJakartaAllMethodsServlet { .forEach(syncPrompt -> syncServer.addPrompt(syncPrompt)); } + private void tryRegisterNoopResourcesSubscribeHandler() { + try { + MethodHandles.Lookup transportLookup = + MethodHandles.privateLookupIn(HttpServletStatelessServerTransport.class, LOOKUP); + MethodHandle mcpHandlerGetter = transportLookup.findGetter( + HttpServletStatelessServerTransport.class, "mcpHandler", McpStatelessServerHandler.class); + Object mcpHandler = mcpHandlerGetter.invoke(transportProvider); + + Class<?> handlerClass = mcpHandler.getClass(); + MethodHandles.Lookup handlerLookup = MethodHandles.privateLookupIn(handlerClass, LOOKUP); + MethodHandle requestHandlersGetter = handlerLookup.findGetter(handlerClass, "requestHandlers", Map.class); + Map<String, McpStatelessRequestHandler<?>> handlers = + (Map<String, McpStatelessRequestHandler<?>>) requestHandlersGetter.invoke(mcpHandler); + + handlers.put(McpSchema.METHOD_RESOURCES_SUBSCRIBE, (context, params) -> Mono.just(Map.of())); + } catch (Throwable t) { + logger.warn( + "Failed to register MCP resources subscribe handler, non-compliant clients requesting resource updates might fail", + t); + } + } + @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = GREEDY, cardinality = MULTIPLE) protected void bindPrompt(DiscoveredPrompt prompt, Map<String, Object> properties) { syncServer.addPrompt(new SyncPromptSpecification(prompt.asPrompt(), (c, r) -> {
