This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-mcp-server.git
The following commit(s) were added to refs/heads/master by this push:
new a6ac6c9 feat: make bundle compatible with javax.servlet APIs (#2)
a6ac6c9 is described below
commit a6ac6c9f7ce04ea9268efc0b1afd3062fb263799
Author: Robert Munteanu <[email protected]>
AuthorDate: Wed Mar 4 15:51:04 2026 +0100
feat: make bundle compatible with javax.servlet APIs (#2)
Since the Servlets from the SDK use the jakarta.servlet APIs we embed those
APIs and the felix wrappers in a secondary artifact with the 'legacy'
classifier. 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.
---
README.md | 3 +
bnd-legacy.bnd | 18 ++++
bnd.bnd | 3 +
pom.xml | 103 ++++++++++++++++++++-
.../appended-resources-legacy/META-INF/LICENSE | 13 +++
src/main/appended-resources-legacy/META-INF/NOTICE | 2 +
src/main/appended-resources/META-INF/LICENSE | 14 +++
.../apache/sling/mcp/server/impl/McpServlet.java | 61 ++++++++----
8 files changed, 197 insertions(+), 20 deletions(-)
diff --git a/README.md b/README.md
index 60b78f3..b189d5d 100644
--- a/README.md
+++ b/README.md
@@ -18,3 +18,6 @@ $ mvn -f whiteboard/mcp-server-contributions/ install
sling:install
Then open up your coding assistant tool and add an remote MCP server with
location http://localhost:8080/mcp .
+## Legacy artifact
+
+For applications still using the older slf4j 1.x and javax.servlet APIs a
classifier with the 'legacy' classifier is built.
diff --git a/bnd-legacy.bnd b/bnd-legacy.bnd
new file mode 100644
index 0000000..8a6fd89
--- /dev/null
+++ b/bnd-legacy.bnd
@@ -0,0 +1,18 @@
+# 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, \
+ 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
diff --git a/bnd.bnd b/bnd.bnd
index c7ccc7c..a4ad25e 100644
--- a/bnd.bnd
+++ b/bnd.bnd
@@ -1,3 +1,6 @@
# workaround for https://github.com/modelcontextprotocol/java-sdk/issues/562
Private-Package: io.modelcontextprotocol.json.jackson, \
io.modelcontextprotocol.json.schema.jackson
+
+# Strictly control exports to prevent bnd from exporting packages that from
embedded dependencies
+-exportcontents: org.apache.sling.mcp.server.spi
diff --git a/pom.xml b/pom.xml
index ab1074a..1208b57 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,6 +42,8 @@
<sling.java.version>17</sling.java.version>
<!-- Run with
-Dslingfeature.app.vmOption=-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=30303
for debug mode-->
<slingfeature.app.vmOption />
+ <!-- Directory for legacy variant classes -->
+
<legacy.classes.directory>${project.build.directory}/classes-legacy</legacy.classes.directory>
</properties>
<dependencies>
@@ -93,7 +95,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 +104,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>
@@ -266,7 +278,94 @@
</launches>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <excludes combine.children="append">
+ <exclude>bnd-legacy.bnd</exclude>
+
<exclude>src/main/appended-resources/META-INF/*</exclude>
+
<exclude>src/main/appended-resources-legacy/META-INF/*</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
</plugins>
</build>
+ <profiles>
+ <!-- legacy compatibility (slf4j 1.x, javax.servlet) -->
+ <profile>
+ <id>legacy-compat</id>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <build>
+ <plugins>
+ <!-- Append legacy-specific license content to
bnd-generated LICENSE/NOTICE -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>append-legacy-license</id>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <phase>prepare-package</phase>
+ <configuration>
+ <target>
+ <concat append="true"
destfile="${legacy.classes.directory}/META-INF/LICENSE">
+ <filelist
dir="src/main/appended-resources-legacy/META-INF" files="LICENSE" />
+ </concat>
+ <concat append="true"
destfile="${legacy.classes.directory}/META-INF/NOTICE">
+ <filelist
dir="src/main/appended-resources-legacy/META-INF" files="NOTICE" />
+ </concat>
+ </target>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- Configure bnd-maven-plugin to generate an additional
jar based on a different bnd file -->
+ <plugin>
+ <groupId>biz.aQute.bnd</groupId>
+ <artifactId>bnd-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>bnd-process-legacy</id>
+ <goals>
+ <goal>bnd-process</goal>
+ </goals>
+ <configuration>
+ <bndfile>bnd-legacy.bnd</bndfile>
+
<manifestPath>${legacy.classes.directory}/META-INF/MANIFEST.MF</manifestPath>
+
<outputDir>${legacy.classes.directory}</outputDir>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- Configure jar plugin to create legacy variant from
classes-legacy -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>legacy-jar</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ <configuration>
+ <classifier>legacy</classifier>
+
<classesDirectory>${legacy.classes.directory}</classesDirectory>
+ <archive>
+
<manifestFile>${legacy.classes.directory}/META-INF/MANIFEST.MF</manifestFile>
+ </archive>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
</project>
diff --git a/src/main/appended-resources-legacy/META-INF/LICENSE
b/src/main/appended-resources-legacy/META-INF/LICENSE
new file mode 100644
index 0000000..21d103a
--- /dev/null
+++ b/src/main/appended-resources-legacy/META-INF/LICENSE
@@ -0,0 +1,13 @@
+
+
+For Jakarta Servlet API 6.1.0 (jakarta.servlet:jakarta.servlet-api)
+
+This product bundles Jakarta Servlet API 6.1.0, which is available under
+"Eclipse Public License v. 2.0 OR GPL-2.0 WITH Classpath-exception-2.0"
+license. For details, see https://jakarta.ee/specifications/servlet/
+
+
+For Apache Felix HTTP Wrappers 6.1.0
(org.apache.felix:org.apache.felix.http.wrappers)
+
+This product bundles Apache Felix HTTP Wrappers 6.1.0, which is available
+under the Apache License 2.0. For details, see https://felix.apache.org/
diff --git a/src/main/appended-resources-legacy/META-INF/NOTICE
b/src/main/appended-resources-legacy/META-INF/NOTICE
new file mode 100644
index 0000000..fc65cac
--- /dev/null
+++ b/src/main/appended-resources-legacy/META-INF/NOTICE
@@ -0,0 +1,2 @@
+Apache Felix Http Wrappers
+Copyright 2006-2024 The Apache Software Foundation
diff --git a/src/main/appended-resources/META-INF/LICENSE
b/src/main/appended-resources/META-INF/LICENSE
new file mode 100644
index 0000000..04e3bb7
--- /dev/null
+++ b/src/main/appended-resources/META-INF/LICENSE
@@ -0,0 +1,14 @@
+
+
+
+APACHE SLING MCP SERVER SUBCOMPONENTS:
+
+The Apache Sling MCP Server includes a number of subcomponents
+with separate copyright notices and license terms. Your use of the source
+code for these subcomponents is subject to the terms and conditions
+of the following licenses.
+
+For MCP SDK 0.17.0 (io.modelcontextprotocol:mcp-json-jackson2)
+
+This product bundles MCP SDK 0.17.0, which is available under a "MIT"
+license. For details, see https://github.com/modelcontextprotocol/java-sdk
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;
+ }
+ }
}