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; 020import org.jspecify.annotations.Nullable; 021 022import javax.annotation.concurrent.ThreadSafe; 023 024/** 025 * Immutable handler result identifying a durably created MCP task. 026 * 027 * <p>The result carries only a task ID. Before returning a task handle to the 028 * client, Soklet asks the configured {@link McpTaskManager} for that ID and 029 * requires a readable, authorized task whose origin matches the current 030 * invocation. Applications must therefore commit both their work description 031 * and the framework-supplied {@link McpTaskOrigin} before returning this value. 032 * 033 * <p>The type parameter preserves the original typed tool output contract for 034 * annotation processing and programmatic registration. It is not an in-memory 035 * future, callback, or worker result. 036 * 037 * @param <R> eventual structured tool output type 038 * @author <a href="https://www.revetkn.com">Mark Allen</a> 039 */ 040@ThreadSafe 041public final class McpTaskCreatedResult<R> implements McpOperationResult { 042 @NonNull 043 private final String taskId; 044 045 /** 046 * Creates a result for a task that is already durably readable. 047 * 048 * @param taskId nonblank task identifier without carriage-return or newline 049 * characters 050 * @param <R> eventual structured tool output type 051 * @return immutable task-created result 052 * @throws NullPointerException if {@code taskId} is null 053 * @throws IllegalArgumentException if {@code taskId} is blank or contains a 054 * carriage-return or newline character 055 */ 056 @NonNull 057 public static <R> McpTaskCreatedResult<@NonNull R> fromTaskId( 058 @NonNull String taskId) { 059 return new McpTaskCreatedResult<>(taskId); 060 } 061 062 private McpTaskCreatedResult(@NonNull String taskId) { 063 this.taskId = McpTask.requireTaskId(taskId); 064 } 065 066 /** @return nonblank durable task identifier */ 067 @NonNull 068 public String getTaskId() { 069 return this.taskId; 070 } 071 072 /** @return whether the other result carries the same task ID */ 073 @Override 074 public boolean equals(@Nullable Object other) { 075 if (this == other) 076 return true; 077 if (!(other instanceof McpTaskCreatedResult<?> result)) 078 return false; 079 return this.taskId.equals(result.taskId); 080 } 081 082 /** @return task-ID hash code */ 083 @Override 084 public int hashCode() { 085 return this.taskId.hashCode(); 086 } 087 088 /** @return a diagnostic rendering that does not expose the task ID */ 089 @Override 090 @NonNull 091 public String toString() { 092 return "McpTaskCreatedResult{taskId=<redacted>}"; 093 } 094}