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.rest.server.mcp.v20260728;
018
019
020/**
021 * The dispatcher-owned payload sealed into an opaque {@code requestState} token by {@link RequestStateCodec}.
022 *
023 * <p>
024 * Never wire-visible directly &mdash;
025 * {@link org.apache.juneau.bean.mcp.v20260728.InputRequiredResult#getRequestState()} carries only the sealed
026 * string form. This type exists so the round counter (SEP-2322's max-rounds cap) and expiry travel *inside*
027 * the sealed token itself, per {@code McpRevision}'s documented statelessness: no server-side session exists
028 * to hold them instead.
029 *
030 * <p>
031 * <b>The sealed continuation is the only trusted per-round carrier.</b> This is what makes it security-relevant:
032 * the {@code continuation} sealed here is authenticated (AEAD) and cannot be altered by the client between
033 * rounds, whereas the request {@code arguments} a handler sees on RESUME come from the follow-up request and are
034 * fully client-controlled (see {@link McpMrtrResumeContext}). Anything a handler must be able to trust across
035 * rounds &mdash; an authenticated principal, an authorization decision, a price, a resource id &mdash; must live
036 * <i>inside</i> this continuation, never be re-derived from the per-round arguments.
037 *
038 * <p>
039 * <b>Continuation type fidelity.</b> {@code continuation} is declared as {@code Object} because it is
040 * handler-opaque, but {@link AeadRequestStateCodec#unseal} reconstructs it via generic JSON deserialization
041 * ({@code Json.to(json, McpRequestState.class)}). A handler that pauses with a {@code Map} or ordinary-bean
042 * continuation therefore gets back a <i>generic JSON</i> value on resume &mdash; a {@code JsonMap}/
043 * {@code JsonList}/boxed primitive/{@code String} &mdash; never the original Java type. A handler that needs its
044 * original type back should not reconstruct it by hand: it retrieves the resume context from the
045 * {@code BeanStore} and calls {@link McpMrtrResumeContext#continuationAs(Class)}, which converts the generic JSON
046 * value back to any Juneau-marshallable target type in one call.
047 *
048 * @param continuation The handler-opaque continuation value, exactly as thrown in
049 *    {@code McpInputRequiredSignal#getContinuation()} (or, on a subsequent pause, exactly as the handler
050 *    returned it a second time). Must be Juneau-marshallable (ordinary bean, {@code Map}, {@code String}, or
051 *    similar). See the type-fidelity note above: it round-trips as generic JSON, not its original Java type.
052 * @param method The JSON-RPC method this state was sealed for ({@code tools/call}, {@code prompts/get}, or
053 *    {@code resources/read}). Part of the AAD contract (see {@link RequestStateCodec}); also checked directly
054 *    by the dispatcher against the follow-up request's own method as an extra sanity check.
055 * @param round The 1-based round counter: 1 on the first PAUSE, incremented on every subsequent PAUSE.
056 * @param expiresAtMs Absolute expiry timestamp in epoch milliseconds.
057 * @param jti A fresh, per-token random identifier minted on every PAUSE (see {@code McpRevision#pause}), used
058 *    only to detect replay of THIS exact token. Opt-in: consulted by the dispatcher only when an operator has
059 *    wired a {@link org.apache.juneau.commons.concurrent.ReplayCache} via {@link McpMrtrConfig#setReplayCache(org.apache.juneau.commons.concurrent.ReplayCache)}; otherwise it rides in the
060 *    sealed plaintext unused. Not wire-visible on its own &mdash; like every other field on this record, it only
061 *    ever travels inside the opaque, AEAD-sealed {@code requestState} string.
062 * @param argumentsHash A canonical hash of the original request's {@code arguments} (RFC 8785 JSON
063 *    Canonicalization Scheme, then SHA-256, then base64url), captured at PAUSE time and re-verified against the
064 *    RESUME request's own {@code arguments} on every subsequent round (see {@code McpRevision#resolveMrtrContext}).
065 *    Always populated (unlike {@link #jti()}, this check is not opt-in): a call with absent or empty
066 *    {@code arguments} (for example {@code resources/read}'s exact-path branch, which takes none) hashes the
067 *    canonical empty object {@code "{}"} rather than leaving this field unset, so PAUSE and RESUME agree even when
068 *    there is nothing to hash.
069 */
070public record McpRequestState(Object continuation, String method, int round, long expiresAtMs, String jti, String argumentsHash) {
071}