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}