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;
020
021import javax.annotation.concurrent.ThreadSafe;
022import java.time.Duration;
023import java.util.Optional;
024
025/**
026 * Thread-safe handle for one asynchronous off-network MCP request.
027 *
028 * @author <a href="https://www.revetkn.com">Mark Allen</a>
029 */
030@ThreadSafe
031public interface McpSimulation extends AutoCloseable {
032        /**
033         * Waits up to {@code timeout} for the immutable response head.
034         * <p>
035         * A zero timeout performs a nonblocking check. Very large positive durations
036         * are saturated to the longest supported wait. Response reads are repeatable.
037         *
038         * @param timeout maximum time to wait; must not be negative
039         * @return the response when available, otherwise empty
040         * @throws InterruptedException if the waiting thread is interrupted
041         * @throws IllegalArgumentException if {@code timeout} is negative
042         * @throws NullPointerException if {@code timeout} is null
043         */
044        @NonNull
045        Optional<@NonNull McpSimulationResponse> awaitResponse(
046                        @NonNull Duration timeout) throws InterruptedException;
047
048        /**
049         * Waits up to {@code timeout} for and removes the next captured SSE item.
050         * Removing an item releases a pending-item slot but does not refund captured
051         * bytes. A zero timeout performs a nonblocking check, and very large positive
052         * durations are saturated to the longest supported wait. Unlike response and
053         * completion reads, successful item reads are destructive.
054         *
055         * @param timeout maximum time to wait; must not be negative
056         * @return the next item when available, otherwise empty
057         * @throws InterruptedException if the waiting thread is interrupted
058         * @throws IllegalArgumentException if {@code timeout} is negative
059         * @throws NullPointerException if {@code timeout} is null
060         */
061        @NonNull
062        Optional<@NonNull McpSimulationStreamItem> awaitStreamItem(
063                        @NonNull Duration timeout) throws InterruptedException;
064
065        /**
066         * Waits up to {@code timeout} for immutable terminal completion.
067         * <p>
068         * A zero timeout performs a nonblocking check. Very large positive durations
069         * are saturated to the longest supported wait. Completion reads are
070         * repeatable.
071         *
072         * @param timeout maximum time to wait; must not be negative
073         * @return completion when available, otherwise empty
074         * @throws InterruptedException if the waiting thread is interrupted
075         * @throws IllegalArgumentException if {@code timeout} is negative
076         * @throws NullPointerException if {@code timeout} is null
077         */
078        @NonNull
079        Optional<@NonNull McpSimulationCompletion> awaitCompletion(
080                        @NonNull Duration timeout) throws InterruptedException;
081
082        /**
083         * Performs a nonblocking terminal-state check.
084         *
085         * @return whether terminal completion is available
086         */
087        @NonNull
088        Boolean isComplete();
089
090        /**
091         * Simulates a client disconnect if the request remains active. Closing is
092         * idempotent, does not discard already captured values, and cannot replace an
093         * earlier terminal winner.
094         * <p>
095         * For MCP 2025-06-18 and 2025-11-25, disconnecting after progress has committed
096         * SSE detaches the response writer without canceling application work solely
097         * for that disconnect. Finite and uncommitted requests retain disconnect
098         * cancelation. Work retained after detachment remains subject to its request
099         * deadline and server shutdown; later progress and results are discarded.
100         */
101        @Override
102        void close();
103}