001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.bean.mcp.v20260728; 018 019import static org.apache.juneau.commons.utils.CollectionUtils.*; 020import static org.apache.juneau.commons.utils.Shorts.*; 021 022import java.util.*; 023 024import org.apache.juneau.commons.bean.*; 025import org.apache.juneau.marshall.*; 026 027/** 028 * Opaque per-request MCP metadata carried under the JSON-RPC {@code params._meta} property. 029 * 030 * <p> 031 * Every v2 request is independently negotiated from its own {@code _meta}; no handshake or session state exists. 032 * Negotiation and identity keys use the exact {@code io.modelcontextprotocol/}-prefixed wire names mandated by the 033 * {@code 2026-07-28} schema; W3C trace-context keys ({@code traceparent} / {@code tracestate} / {@code baggage}) 034 * are the schema's explicit exception and remain bare. Unrecognized metadata members round-trip losslessly, in 035 * insertion order, through the {@code "*"} dynamic-property triplet ({@link #extraKeys()}, {@link #get(String)}, 036 * {@link #set(String, Object)}). 037 * 038 * <p> 039 * The exact wire-key constants below are the single source of truth for negotiation/identity/trace metadata keys; 040 * the {@code 2026-07-28} adapter, its validation messages, and bean/adapter tests consume these constants rather 041 * than repeating the literal strings. 042 */ 043@Marshalled 044public class RequestMeta { 045 046 /** Exact wire key for the required negotiated protocol version. */ 047 public static final String KEY_PROTOCOL_VERSION = "io.modelcontextprotocol/protocolVersion"; 048 049 /** Exact wire key for the optional client implementation identity. */ 050 public static final String KEY_CLIENT_INFO = "io.modelcontextprotocol/clientInfo"; 051 052 /** Exact wire key for the required client capability advertisement. */ 053 public static final String KEY_CLIENT_CAPABILITIES = "io.modelcontextprotocol/clientCapabilities"; 054 055 /** Exact wire key for the deprecated, accepted-but-ignored client log level. */ 056 public static final String KEY_LOG_LEVEL = "io.modelcontextprotocol/logLevel"; 057 058 /** Bare (unprefixed) W3C trace-context parent key. */ 059 public static final String KEY_TRACEPARENT = "traceparent"; 060 061 /** Bare (unprefixed) W3C trace-context state key. */ 062 public static final String KEY_TRACESTATE = "tracestate"; 063 064 /** Bare (unprefixed) W3C baggage key. */ 065 public static final String KEY_BAGGAGE = "baggage"; 066 067 /** 068 * Exact wire key carrying the JSON-RPC id of the {@code subscriptions/listen} request on every 069 * notification frame's {@code _meta} on that stream. The terminal {@link SubscriptionsListenResult} 070 * frame carries {@link ResultMeta} (not this type) instead but reuses the same wire key via its 071 * untyped {@code set(String, Object)} extension. 072 */ 073 public static final String KEY_SUBSCRIPTION_ID = "io.modelcontextprotocol/subscriptionId"; 074 075 private String protocolVersion; 076 private Implementation clientInfo; 077 private ClientCapabilities clientCapabilities; 078 private String logLevel; 079 private String traceparent; 080 private String tracestate; 081 private String baggage; 082 private Map<String,Object> extra; 083 084 /** 085 * Protocol version requested by the client. 086 * 087 * @return The protocol version, or {@code null} if not set. 088 */ 089 @BeanProp(KEY_PROTOCOL_VERSION) 090 public String getProtocolVersion() { 091 return protocolVersion; 092 } 093 094 /** 095 * Sets the protocol version. 096 * 097 * @param value The new value. Can be <jk>null</jk> to unset the property. 098 * @return This object (for method chaining). 099 */ 100 @BeanProp(KEY_PROTOCOL_VERSION) 101 public RequestMeta setProtocolVersion(String value) { 102 protocolVersion = value; 103 return this; 104 } 105 106 /** 107 * Client implementation identity. 108 * 109 * <p> 110 * Optional (the schema's {@code SHOULD}, not {@code MUST}); validated only when present. 111 * 112 * @return The client info, or {@code null} if not set. 113 */ 114 @BeanProp(KEY_CLIENT_INFO) 115 public Implementation getClientInfo() { 116 return clientInfo; 117 } 118 119 /** 120 * Sets the client implementation identity. 121 * 122 * @param value The new value. Can be <jk>null</jk> to unset the property. 123 * @return This object (for method chaining). 124 */ 125 @BeanProp(KEY_CLIENT_INFO) 126 public RequestMeta setClientInfo(Implementation value) { 127 clientInfo = value; 128 return this; 129 } 130 131 /** 132 * Client capability advertisement. 133 * 134 * @return The client capabilities, or {@code null} if not set. 135 */ 136 @BeanProp(KEY_CLIENT_CAPABILITIES) 137 public ClientCapabilities getClientCapabilities() { 138 return clientCapabilities; 139 } 140 141 /** 142 * Sets the client capability advertisement. 143 * 144 * @param value The new value. Can be <jk>null</jk> to unset the property. 145 * @return This object (for method chaining). 146 */ 147 @BeanProp(KEY_CLIENT_CAPABILITIES) 148 public RequestMeta setClientCapabilities(ClientCapabilities value) { 149 clientCapabilities = value; 150 return this; 151 } 152 153 /** 154 * Deprecated client log level. 155 * 156 * <p> 157 * Accepted and parsed for backward compatibility, but otherwise ignored by the {@code 2026-07-28} adapter. 158 * 159 * @return The log level, or {@code null} if not set. 160 */ 161 @BeanProp(KEY_LOG_LEVEL) 162 public String getLogLevel() { 163 return logLevel; 164 } 165 166 /** 167 * Sets the deprecated client log level. 168 * 169 * @param value The new value. Can be <jk>null</jk> to unset the property. 170 * @return This object (for method chaining). 171 */ 172 @BeanProp(KEY_LOG_LEVEL) 173 public RequestMeta setLogLevel(String value) { 174 logLevel = value; 175 return this; 176 } 177 178 /** 179 * W3C trace-context parent (bare key; not renamed under the {@code io.modelcontextprotocol/} prefix). 180 * 181 * @return The traceparent value, or {@code null} if not set. 182 */ 183 public String getTraceparent() { 184 return traceparent; 185 } 186 187 /** 188 * Sets the W3C trace-context parent. 189 * 190 * @param value The new value. Can be <jk>null</jk> to unset the property. 191 * @return This object (for method chaining). 192 */ 193 public RequestMeta setTraceparent(String value) { 194 traceparent = value; 195 return this; 196 } 197 198 /** 199 * W3C trace-context state (bare key; not renamed under the {@code io.modelcontextprotocol/} prefix). 200 * 201 * @return The tracestate value, or {@code null} if not set. 202 */ 203 public String getTracestate() { 204 return tracestate; 205 } 206 207 /** 208 * Sets the W3C trace-context state. 209 * 210 * @param value The new value. Can be <jk>null</jk> to unset the property. 211 * @return This object (for method chaining). 212 */ 213 public RequestMeta setTracestate(String value) { 214 tracestate = value; 215 return this; 216 } 217 218 /** 219 * W3C baggage (bare key; not renamed under the {@code io.modelcontextprotocol/} prefix). 220 * 221 * @return The baggage value, or {@code null} if not set. 222 */ 223 public String getBaggage() { 224 return baggage; 225 } 226 227 /** 228 * Sets the W3C baggage. 229 * 230 * @param value The new value. Can be <jk>null</jk> to unset the property. 231 * @return This object (for method chaining). 232 */ 233 public RequestMeta setBaggage(String value) { 234 baggage = value; 235 return this; 236 } 237 238 /** 239 * Extension metadata member keyset. 240 * 241 * <p> 242 * Covers any metadata member other than the negotiation/identity/trace properties declared above. Preserves 243 * insertion order. 244 * 245 * @return All extension keys on this metadata object. Never <jk>null</jk>. 246 */ 247 @BeanProp("*") 248 public Set<String> extraKeys() { 249 return extra == null ? Collections.emptySet() : u(extra.keySet()); 250 } 251 252 /** 253 * Extension metadata member getter. 254 * 255 * @param property The property name. Must not be <jk>null</jk>. 256 * @return The property value, or <jk>null</jk> if the property does not exist or is not set. 257 */ 258 @BeanProp("*") 259 public Object get(String property) { 260 return extra == null ? null : extra.get(property); 261 } 262 263 /** 264 * Extension metadata member setter. 265 * 266 * <p> 267 * The extension map is lazily initialized on first use and preserves insertion order across repeated calls. 268 * 269 * @param property The property name. Must not be <jk>null</jk>. 270 * @param value The new value for the property. Can be <jk>null</jk> (stored as <jk>null</jk>). 271 * @return This object (for method chaining). 272 */ 273 @BeanProp("*") 274 public RequestMeta set(String property, Object value) { 275 if (extra == null) 276 extra = map(); 277 extra.put(property, value); 278 return this; 279 } 280}