001/* 002 * Copyright 2022-2026 Revetware LLC. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.soklet; 017 018import org.jspecify.annotations.NonNull; 019 020import javax.annotation.concurrent.ThreadSafe; 021import java.util.List; 022import java.util.Locale; 023 024import static java.util.Objects.requireNonNull; 025 026/** 027 * Immutable, framework-supplied input to a Skills variant selector. 028 * 029 * <p>Only accessible, discoverable registrations are exposed, in their configured 030 * group order. The application-local group key is available without exposing the 031 * unfiltered group. Candidate identity is retained so a selector can return an 032 * exact supplied registration. This context is selection input, not an access 033 * grant or a second localization negotiation. 034 * 035 * @author <a href="https://www.revetkn.com">Mark Allen</a> 036 */ 037@ThreadSafe 038public final class McpSkillVariantSelectionContext { 039 @NonNull 040 private final String skillGroupKey; 041 @NonNull 042 private final List<@NonNull McpSkillRegistration> skillRegistrations; 043 @NonNull 044 private final List<Locale.@NonNull LanguageRange> languageRanges; 045 046 private McpSkillVariantSelectionContext(@NonNull String skillGroupKey, 047 @NonNull List<@NonNull McpSkillRegistration> skillRegistrations, 048 @NonNull List<Locale.@NonNull LanguageRange> languageRanges) { 049 this.skillGroupKey = requireNonNull(skillGroupKey); 050 this.skillRegistrations = List.copyOf(requireNonNull(skillRegistrations)); 051 this.languageRanges = List.copyOf(requireNonNull(languageRanges)); 052 } 053 054 @NonNull 055 static McpSkillVariantSelectionContext from(@NonNull String skillGroupKey, 056 @NonNull List<@NonNull McpSkillRegistration> skillRegistrations, 057 @NonNull List<Locale.@NonNull LanguageRange> languageRanges) { 058 return new McpSkillVariantSelectionContext(skillGroupKey, skillRegistrations, languageRanges); 059 } 060 061 /** @return exact application-defined, endpoint-local group key */ 062 @NonNull 063 public String getSkillGroupKey() { return this.skillGroupKey; } 064 065 /** @return immutable accessible, discoverable candidates in configured group order */ 066 @NonNull 067 public List<@NonNull McpSkillRegistration> getSkillRegistrations() { return this.skillRegistrations; } 068 069 /** 070 * Returns the immutable bounded client language preferences and exclusions. 071 * The original parsed order and weights, including zero weights, are retained. 072 * An empty list indicates no express language constraint under the bounded 073 * request parsing rules, not a newly selected or inferred locale. 074 * 075 * @return immutable bounded language ranges 076 */ 077 @NonNull 078 public List<Locale.@NonNull LanguageRange> getLanguageRanges() { return this.languageRanges; } 079 080 /** @return diagnostic rendering without group, candidate, or language data */ 081 @Override 082 @NonNull 083 public String toString() { return "McpSkillVariantSelectionContext[redacted]"; } 084}