This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch issue/javax-servlet-compat in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-mcp-server.git
commit d71df6e2892b8625449aea0db18b89cd4174f8ae Author: Robert Munteanu <[email protected]> AuthorDate: Fri Feb 13 12:10:08 2026 +0100 feat: make bundle compatible with javax.servlet APIs Since the Servlets from the SDK use the jakarta.servlet APIs we embed those APIs and the felix wrappers in the bundle. This allows MCP SDK servlets to function unaltered and the Sling McpServlet to use the old javax.servlet API. We also restrict slf4j imports to work with the older Sling commons.log version on the premise that projects that compile against slf4j 2.0 don't actually use any of the new APIs and can function with 1.7.x as well. --- bnd.bnd | 19 ++++++- pom.xml | 14 ++++- .../apache/sling/mcp/server/impl/McpServlet.java | 61 +++++++++++++++------- 3 files changed, 72 insertions(+), 22 deletions(-) diff --git a/bnd.bnd b/bnd.bnd index f1ef54d..15a8895 100644 --- a/bnd.bnd +++ b/bnd.bnd @@ -1,5 +1,20 @@ -# workaround for https://github.com/modelcontextprotocol/java-sdk/issues/562 +# 1. workaround for https://github.com/modelcontextprotocol/java-sdk/issues/562 +# 2. compatibility with javax.servlet Private-Package: io.modelcontextprotocol.json.jackson, \ - io.modelcontextprotocol.json.schema.jackson + io.modelcontextprotocol.json.schema.jackson, \ + jakarta.servlet, \ + jakarta.servlet.annotation, \ + jakarta.servlet.descriptor, \ + jakarta.servlet.http, \ + org.apache.felix.http.jakartawrappers, \ + org.apache.felix.http.javaxwrappers + +# compatibility with slf4j 1.7 and 2.0. A bit of the gamble since we assume that libraries +# that compile against slf4j 2.x only use 1.7 APIs +Import-Package: org.slf4j;version="[1.7,2)", \ + * + +# Strictly control exports to prevent bnd from exporting packages that from embedded dependencies +-exportcontents: org.apache.sling.mcp.server.spi Sling-Initial-Content: SLING-INF/libs/sling/mcp/prompts;path:=/libs/sling/mcp/prompts;overwrite:=true \ No newline at end of file diff --git a/pom.xml b/pom.xml index ab1074a..f623c04 100644 --- a/pom.xml +++ b/pom.xml @@ -93,7 +93,7 @@ <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.api</artifactId> - <version>3.0.2</version> + <version>2.27.6</version> <scope>provided</scope> </dependency> <dependency> @@ -102,10 +102,20 @@ <version>2.10.2</version> <scope>provided</scope> </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> - <scope>provided</scope> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.http.wrappers</artifactId> + <scope>compile</scope> </dependency> <dependency> <groupId>io.modelcontextprotocol.sdk</groupId> 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 643265c..2ba0354 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 @@ -18,6 +18,9 @@ */ package org.apache.sling.mcp.server.impl; +import javax.servlet.Servlet; +import javax.servlet.ServletException; + import java.io.IOException; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -35,13 +38,11 @@ import io.modelcontextprotocol.server.McpStatelessSyncServer; import io.modelcontextprotocol.server.transport.HttpServletStatelessServerTransport; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities; -import jakarta.servlet.Servlet; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.apache.sling.api.SlingJakartaHttpServletRequest; -import org.apache.sling.api.SlingJakartaHttpServletResponse; -import org.apache.sling.api.servlets.SlingJakartaAllMethodsServlet; +import org.apache.felix.http.jakartawrappers.HttpServletRequestWrapper; +import org.apache.felix.http.jakartawrappers.HttpServletResponseWrapper; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.SlingHttpServletResponse; +import org.apache.sling.api.servlets.SlingAllMethodsServlet; import org.apache.sling.mcp.server.spi.McpServerContribution; import org.apache.sling.servlets.annotations.SlingServletPaths; import org.jetbrains.annotations.NotNull; @@ -64,7 +65,7 @@ import static org.osgi.service.component.annotations.ReferencePolicyOption.GREED @Component(service = Servlet.class) @SlingServletPaths(value = {McpServlet.ENDPOINT}) @Designate(ocd = McpServlet.Config.class) -public class McpServlet extends SlingJakartaAllMethodsServlet { +public class McpServlet extends SlingAllMethodsServlet { @ObjectClassDefinition(name = "Apache Sling MCP Server Configuration") public @interface Config { @@ -102,8 +103,11 @@ public class McpServlet extends SlingJakartaAllMethodsServlet { transportProvider = HttpServletStatelessServerTransport.builder() .messageEndpoint(ENDPOINT) .jsonMapper(jsonMapper) - .contextExtractor(request -> McpTransportContext.create( - Map.of("resourceResolver", ((SlingJakartaHttpServletRequest) request).getResourceResolver()))) + .contextExtractor(request -> McpTransportContext.create(Map.of( + "resourceResolver", + ((BridgedJakartaHttpServletRequest) request) + .getSlingRequest() + .getResourceResolver()))) .build(); MethodHandles.Lookup privateLookup = @@ -113,12 +117,16 @@ public class McpServlet extends SlingJakartaAllMethodsServlet { HttpServletStatelessServerTransport.class, "doGet", java.lang.invoke.MethodType.methodType( - void.class, HttpServletRequest.class, HttpServletResponse.class)); + void.class, + jakarta.servlet.http.HttpServletRequest.class, + jakarta.servlet.http.HttpServletResponse.class)); doPostMethod = privateLookup.findVirtual( HttpServletStatelessServerTransport.class, "doPost", java.lang.invoke.MethodType.methodType( - void.class, HttpServletRequest.class, HttpServletResponse.class)); + void.class, + jakarta.servlet.http.HttpServletRequest.class, + jakarta.servlet.http.HttpServletResponse.class)); String serverVersion = config.serverVersion(); if (serverVersion == null || serverVersion.isEmpty()) { @@ -205,11 +213,13 @@ public class McpServlet extends SlingJakartaAllMethodsServlet { } @Override - protected void doGet( - @NotNull SlingJakartaHttpServletRequest request, @NotNull SlingJakartaHttpServletResponse response) + protected void doGet(@NotNull SlingHttpServletRequest request, @NotNull SlingHttpServletResponse response) throws ServletException, IOException { try { - doGetMethod.invoke(transportProvider, request, response); + doGetMethod.invoke( + transportProvider, + new BridgedJakartaHttpServletRequest(request), + new HttpServletResponseWrapper(response)); } catch (ServletException | IOException | RuntimeException | Error e) { throw e; } catch (Throwable t) { @@ -218,11 +228,13 @@ public class McpServlet extends SlingJakartaAllMethodsServlet { } @Override - protected void doPost( - @NotNull SlingJakartaHttpServletRequest request, @NotNull SlingJakartaHttpServletResponse response) + protected void doPost(@NotNull SlingHttpServletRequest request, @NotNull SlingHttpServletResponse response) throws ServletException, IOException { try { - doPostMethod.invoke(transportProvider, request, response); + doPostMethod.invoke( + transportProvider, + new BridgedJakartaHttpServletRequest(request), + new HttpServletResponseWrapper(response)); } catch (ServletException | IOException | RuntimeException | Error e) { throw e; } catch (Throwable t) { @@ -236,4 +248,17 @@ public class McpServlet extends SlingJakartaAllMethodsServlet { syncServer.close(); } } + + static class BridgedJakartaHttpServletRequest extends HttpServletRequestWrapper { + private SlingHttpServletRequest slingRequest; + + public BridgedJakartaHttpServletRequest(SlingHttpServletRequest request) { + super(request); + this.slingRequest = request; + } + + public SlingHttpServletRequest getSlingRequest() { + return slingRequest; + } + } }
