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-contributions.git
commit 2c9112298c85f0efe6cff087fee8395c10302795 Author: Robert Munteanu <[email protected]> AuthorDate: Fri Mar 13 13:30:40 2026 +0100 fix(mcp-server-contributions): remove bundle and component resources The OsgiBundleDiagnosticContribution should be enough for diagnosing OSGi-related issues --- .../impl/contribs/BundleResourceContribution.java | 142 --------------------- .../mcp/server/impl/contribs/BundleState.java | 57 --------- .../contribs/ComponentResourceContribution.java | 125 ------------------ 3 files changed, 324 deletions(-) diff --git a/src/main/java/org/apache/sling/mcp/server/impl/contribs/BundleResourceContribution.java b/src/main/java/org/apache/sling/mcp/server/impl/contribs/BundleResourceContribution.java deleted file mode 100644 index 1d37db3..0000000 --- a/src/main/java/org/apache/sling/mcp/server/impl/contribs/BundleResourceContribution.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.sling.mcp.server.impl.contribs; - -import java.util.Arrays; -import java.util.List; -import java.util.Locale; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import io.modelcontextprotocol.server.McpStatelessServerFeatures; -import io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncCompletionSpecification; -import io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncResourceSpecification; -import io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncResourceTemplateSpecification; -import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult; -import io.modelcontextprotocol.spec.McpSchema.Resource; -import io.modelcontextprotocol.spec.McpSchema.ResourceTemplate; -import io.modelcontextprotocol.spec.McpSchema.TextResourceContents; -import org.apache.sling.mcp.server.spi.McpServerContribution; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.wiring.BundleRevision; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; - -@Component -public class BundleResourceContribution implements McpServerContribution { - - private static final String URI_BUNDLES_ALL = "bundles://all"; - private static final String RESOURCE_TEMPLATE_BUNDLES_STATE_PREFIX = "bundles://state/"; - private static final String RESOURCE_TEMPLATE_BUNDLES_STATE_PATTERN = - RESOURCE_TEMPLATE_BUNDLES_STATE_PREFIX + "{state}"; - - private BundleContext ctx; - - @Activate - public BundleResourceContribution(BundleContext ctx) { - this.ctx = ctx; - } - - @Override - public List<SyncResourceSpecification> getSyncResourceSpecification() { - - return List.of(new McpStatelessServerFeatures.SyncResourceSpecification( - new Resource.Builder() - .name("bundles") - .uri(URI_BUNDLES_ALL) - .description( - "List all OSGi bundles with symbolic name, version, and state. Fragment bundles are marked with [Fragment]") - .mimeType("text/plain") - .build(), - (context, request) -> { - String bundleInfo = - Stream.of(ctx.getBundles()).map(this::describe).collect(Collectors.joining("\n")); - - TextResourceContents contents = new TextResourceContents(URI_BUNDLES_ALL, "text/plain", bundleInfo); - - return new McpSchema.ReadResourceResult(List.of(contents)); - })); - } - - @Override - public List<SyncResourceTemplateSpecification> getSyncResourceTemplateSpecification() { - return List.of(new McpStatelessServerFeatures.SyncResourceTemplateSpecification( - new ResourceTemplate.Builder() - .uriTemplate(RESOURCE_TEMPLATE_BUNDLES_STATE_PATTERN) - .name("bundles") - .build(), - (context, request) -> { - String requestedState = request.uri().substring(RESOURCE_TEMPLATE_BUNDLES_STATE_PREFIX.length()); - try { - BundleState bundleState = BundleState.valueOf(requestedState.toUpperCase(Locale.ENGLISH)); - if (!bundleState.isValid()) { - throw new IllegalArgumentException("Invalid bundle state: " + requestedState); - } - String bundleInfo = Arrays.stream(ctx.getBundles()) - .filter(b -> b.getState() == bundleState.getState()) - .map(this::describe) - .collect(Collectors.joining("\n")); - - TextResourceContents contents = - new TextResourceContents(request.uri(), "text/plain", bundleInfo); - - return new ReadResourceResult(List.of(contents)); - } catch (IllegalArgumentException e) { - return new ReadResourceResult(List.of(new TextResourceContents( - request.uri(), "text/plain", "Invalid bundle state requested: " + requestedState))); - } - })); - } - - @Override - public List<SyncCompletionSpecification> getSyncCompletionSpecification() { - - return List.of(new McpStatelessServerFeatures.SyncCompletionSpecification( - new McpSchema.ResourceReference("ref/resource", RESOURCE_TEMPLATE_BUNDLES_STATE_PATTERN), - (context, request) -> { - - // expect argument name to always be "state" - String requestedState = request.argument().value(); - List<String> states = Stream.of(BundleState.values()) - .filter(BundleState::isValid) - .map(s -> s.name().toLowerCase(Locale.ENGLISH)) - .toList(); - if (requestedState != null && !requestedState.isEmpty()) { - states = states.stream() - .filter(s -> s.startsWith(requestedState.toLowerCase(Locale.ENGLISH))) - .toList(); - } - return new McpSchema.CompleteResult( - new McpSchema.CompleteResult.CompleteCompletion(states, states.size(), false)); - })); - } - - private String describe(Bundle b) { - boolean isFragment = Optional.ofNullable(b.adapt(BundleRevision.class)).stream() - .map(br -> (br.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) - .findAny() - .orElse(false); - String additionalInfo = isFragment ? " [Fragment]" : ""; - return "Bundle " + b.getSymbolicName() + additionalInfo + " (version " + b.getVersion() + ") is in state " - + BundleState.fromState(b.getState()) + " (" + b.getState() + ")"; - } -} diff --git a/src/main/java/org/apache/sling/mcp/server/impl/contribs/BundleState.java b/src/main/java/org/apache/sling/mcp/server/impl/contribs/BundleState.java deleted file mode 100644 index 29e5bba..0000000 --- a/src/main/java/org/apache/sling/mcp/server/impl/contribs/BundleState.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.sling.mcp.server.impl.contribs; - -import org.osgi.framework.Bundle; - -/** - * Enum representing OSGi bundle states. - */ -public enum BundleState { - UNINSTALLED(Bundle.UNINSTALLED), - INSTALLED(Bundle.INSTALLED), - RESOLVED(Bundle.RESOLVED), - STARTING(Bundle.STARTING), - STOPPING(Bundle.STOPPING), - ACTIVE(Bundle.ACTIVE), - UNKNOWN(-1); - - private final int state; - - BundleState(int state) { - this.state = state; - } - - public static BundleState fromState(int state) { - for (BundleState bs : values()) { - if (bs.state == state) { - return bs; - } - } - return UNKNOWN; - } - - public boolean isValid() { - return this != UNKNOWN; - } - - public int getState() { - return state; - } -} diff --git a/src/main/java/org/apache/sling/mcp/server/impl/contribs/ComponentResourceContribution.java b/src/main/java/org/apache/sling/mcp/server/impl/contribs/ComponentResourceContribution.java deleted file mode 100644 index e0c18bc..0000000 --- a/src/main/java/org/apache/sling/mcp/server/impl/contribs/ComponentResourceContribution.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.sling.mcp.server.impl.contribs; - -import java.util.Collection; -import java.util.List; -import java.util.Locale; -import java.util.stream.Collectors; - -import io.modelcontextprotocol.server.McpStatelessServerFeatures; -import io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncResourceSpecification; -import io.modelcontextprotocol.server.McpStatelessServerFeatures.SyncResourceTemplateSpecification; -import io.modelcontextprotocol.spec.McpSchema; -import io.modelcontextprotocol.spec.McpSchema.ReadResourceResult; -import io.modelcontextprotocol.spec.McpSchema.Resource; -import io.modelcontextprotocol.spec.McpSchema.ResourceTemplate; -import io.modelcontextprotocol.spec.McpSchema.TextResourceContents; -import org.apache.sling.mcp.server.spi.McpServerContribution; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.runtime.ServiceComponentRuntime; -import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO; - -@Component -public class ComponentResourceContribution implements McpServerContribution { - - private static String getStateString(int state) { - return switch (state) { - case 1 -> "UNSATISFIED_CONFIGURATION"; - case 2 -> "UNSATISFIED_REFERENCE"; - case 4 -> "SATISFIED"; - case 8 -> "ACTIVE"; - case 16 -> "REGISTERED"; - case 32 -> "FACTORY"; - case 64 -> "DISABLED"; - case 128 -> "ENABLING"; - case 256 -> "ENABLED"; - case 512 -> "DISABLING"; - default -> "UNKNOWN"; - }; - } - - @Reference - private ServiceComponentRuntime scr; - - @Activate - public ComponentResourceContribution() {} - - @Override - public List<SyncResourceSpecification> getSyncResourceSpecification() { - - return List.of(new McpStatelessServerFeatures.SyncResourceSpecification( - new Resource.Builder() - .name("component") - .uri("component://") - .description("OSGi component status") - .mimeType("text/plain") - .build(), - (context, request) -> { - Collection<ComponentDescriptionDTO> components = scr.getComponentDescriptionDTOs(); - String componentInfo = components.stream() - .map(c -> { - String state = scr.getComponentConfigurationDTOs(c).stream() - .map(config -> getStateString(config.state)) - .collect(Collectors.joining(", ")); - return "Component " + c.name + " is in state(s): " + state; - }) - .collect(Collectors.joining("\n")); - - TextResourceContents contents = - new TextResourceContents("component://", "text/plain", componentInfo); - - return new McpSchema.ReadResourceResult(List.of(contents)); - })); - } - - @Override - public List<SyncResourceTemplateSpecification> getSyncResourceTemplateSpecification() { - return List.of(new McpStatelessServerFeatures.SyncResourceTemplateSpecification( - new ResourceTemplate.Builder() - .uriTemplate("components://state/{state}") - .name("components") - .build(), - (context, request) -> { - String componentInfo = ""; - String uri = request.uri().toLowerCase(Locale.ENGLISH); - - if (uri.startsWith("components://state/")) { - String requestedState = uri.substring("components://state/".length()); - Collection<ComponentDescriptionDTO> components = scr.getComponentDescriptionDTOs(); - - componentInfo = components.stream() - .flatMap(c -> scr.getComponentConfigurationDTOs(c).stream() - .filter(config -> getStateString(config.state) - .toLowerCase(Locale.ENGLISH) - .equals(requestedState)) - .map(config -> "Component " + c.name + " is in state: " - + getStateString(config.state))) - .collect(Collectors.joining("\n")); - } - - TextResourceContents contents = - new TextResourceContents(request.uri(), "text/plain", componentInfo); - - return new ReadResourceResult(List.of(contents)); - })); - } -}
