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 — 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 — an authenticated principal, an authorization decision, a price, a resource id — 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 — a {@code JsonMap}/ 043 * {@code JsonList}/boxed primitive/{@code String} — 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 — 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}