You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2026/02/13 11:12:40 UTC

(sling-org-apache-sling-mcp-server) 01/01: feat: make bundle compatible with javax.servlet APIs

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 <ro...@apache.org>
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;
+        }
+    }
 }