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 * http://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 */ 016 017package com.soklet; 018 019import org.jspecify.annotations.NonNull; 020import org.jspecify.annotations.Nullable; 021 022import javax.annotation.concurrent.ThreadSafe; 023 024import static java.util.Objects.requireNonNull; 025 026/** 027 * Immutable accepted or rejected MCP admission decision. Decision 028 * carriers are safe for concurrent access; application-owned values reachable 029 * through an accepted identity retain their own thread-safety contracts. 030 * 031 * <p> 032 * This is a closed domain; exhaustive switches are supported, and any new alternative requires an explicit reviewed contract amendment. 033 * 034 * @author <a href="https://www.revetkn.com">Mark Allen</a> 035 */ 036@ThreadSafe 037public sealed interface McpAdmissionDecision permits McpAdmissionDecision.Accepted, 038 McpAdmissionDecision.Rejected { 039 /** 040 * Creates an accepted decision carrying an application identity. 041 * 042 * @param identity admitted identity 043 * @return accepted decision 044 */ 045 @NonNull 046 static Accepted accepted(@NonNull McpAdmissionIdentity identity) { 047 return new Accepted(identity); 048 } 049 050 /** 051 * Creates an accepted decision carrying the canonical anonymous identity. 052 * 053 * @return anonymous accepted decision 054 */ 055 @NonNull 056 static Accepted accepted() { 057 return accepted(McpAdmissionIdentity.anonymousInstance()); 058 } 059 060 /** 061 * Creates a rejected decision. 062 * 063 * @param rejection safe client-visible rejection 064 * @return rejected decision 065 */ 066 @NonNull 067 static Rejected rejected(@NonNull McpAdmissionRejection rejection) { 068 return new Rejected(rejection); 069 } 070 071 /** 072 * Accepted admission decision carrying an admitted identity. 073 * 074 * @author <a href="https://www.revetkn.com">Mark Allen</a> 075 */ 076 @ThreadSafe 077 public final class Accepted implements McpAdmissionDecision { 078 @NonNull 079 private final McpAdmissionIdentity identity; 080 081 /** 082 * Creates an accepted decision. 083 * 084 * @param identity admitted identity 085 */ 086 private Accepted(@NonNull McpAdmissionIdentity identity) { 087 this.identity = requireNonNull(identity); 088 } 089 090 /** @return admitted identity */ 091 @NonNull 092 public McpAdmissionIdentity getIdentity() { 093 return this.identity; 094 } 095 096 /** @return whether this decision carries the same admitted identity */ 097 @Override 098 public boolean equals(@Nullable Object other) { 099 if (this == other) 100 return true; 101 if (!(other instanceof Accepted accepted)) 102 return false; 103 return this.identity.equals(accepted.identity); 104 } 105 106 /** @return value-based hash code */ 107 @Override 108 public int hashCode() { 109 return this.identity.hashCode(); 110 } 111 112 /** @return redacted diagnostic rendering */ 113 @Override 114 @NonNull 115 public String toString() { 116 return "Accepted{identity=<redacted>}"; 117 } 118 } 119 120 /** 121 * Rejected admission decision carrying a safe client-visible rejection. 122 * 123 * @author <a href="https://www.revetkn.com">Mark Allen</a> 124 */ 125 @ThreadSafe 126 public final class Rejected implements McpAdmissionDecision { 127 @NonNull 128 private final McpAdmissionRejection rejection; 129 130 /** 131 * Creates a rejected decision. 132 * 133 * @param rejection safe client-visible rejection 134 */ 135 private Rejected(@NonNull McpAdmissionRejection rejection) { 136 this.rejection = requireNonNull(rejection); 137 } 138 139 /** @return safe client-visible rejection */ 140 @NonNull 141 public McpAdmissionRejection getRejection() { 142 return this.rejection; 143 } 144 145 /** @return whether this decision carries the same rejection */ 146 @Override 147 public boolean equals(@Nullable Object other) { 148 if (this == other) 149 return true; 150 if (!(other instanceof Rejected rejected)) 151 return false; 152 return this.rejection.equals(rejected.rejection); 153 } 154 155 /** @return value-based hash code */ 156 @Override 157 public int hashCode() { 158 return this.rejection.hashCode(); 159 } 160 161 /** @return redacted diagnostic rendering */ 162 @Override 163 @NonNull 164 public String toString() { 165 return "Rejected{rejection=<redacted>}"; 166 } 167 } 168}