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.client.mcp; 018 019import java.util.*; 020 021import org.apache.juneau.bean.jsonrpc.*; 022 023/** 024 * An opt-in, off-by-default response cache for MCP client results. 025 * 026 * <p> 027 * Keyed by an opaque {@code scope} (a partition key — a revision-specific adapter defines its own scope wire 028 * vocabulary, e.g. the {@code 2026-07-28} adapter's {@code org.apache.juneau.bean.mcp.v20260728.McpCacheScope} 029 * {@code "private"}/{@code "public"} cache-hint values; this neutral core treats the string as fully opaque and 030 * defines no scope constants of its own) plus a {@code key} (typically method + canonical params, computed by 031 * the caller). 032 * 033 * <p> 034 * <b>Stored value shape:</b> this SPI makes no assumption about what a cached {@code value} is — a caller may 035 * store the raw JSON-RPC {@code result} (e.g. via {@link #putResult(String, String, JsonRpcResponse, long)}) or 036 * a typed result bean it has already re-marshaled (e.g. the {@code 2026-07-28} client adapter, which caches the 037 * typed bean returned to its caller). Whatever shape a caller stores under a given {@code key} is exactly the 038 * shape it must expect back from {@link #get(String, String)} for that same {@code key} — this interface does 039 * not normalize or re-marshal on either side. 040 * 041 * <p> 042 * <b>Identity:</b> implementations must return the exact {@code value} instance passed to {@link #put(String, 043 * String, Object, long)} from a subsequent {@link #get(String, String)} for the same partition/key (process-local 044 * implementations naturally satisfy this; an external/serializing store would not, and a caller layering one in 045 * must re-marshal on read to preserve this contract). Cached values may be handed out to more than one caller 046 * concurrently; callers must not mutate a returned value. 047 * 048 * <p> 049 * Implementations are free to be process-local ({@link InMemoryMcpResponseCache}, the built-in default) or backed 050 * by an external store; the SPI itself makes no assumption about storage. 051 * 052 * @since 10.0.0 053 */ 054public interface McpResponseCache { 055 056 /** 057 * Looks up a cached value. 058 * 059 * @param scope The cache-scope partition key. Must not be <jk>null</jk>. 060 * @param key The cache key (typically method + canonical params). Must not be <jk>null</jk>. 061 * @return The cached value, or {@link Optional#empty()} if absent or expired. 062 */ 063 Optional<Object> get(String scope, String key); 064 065 /** 066 * Stores a value. 067 * 068 * @param scope The cache-scope partition key. Must not be <jk>null</jk>. 069 * @param key The cache key (typically method + canonical params). Must not be <jk>null</jk>. 070 * @param value The value to cache. Must not be <jk>null</jk>. 071 * @param ttlMs The time-to-live in milliseconds. A value {@code <= 0} means the entry never expires. 072 */ 073 void put(String scope, String key, Object value, long ttlMs); 074 075 /** 076 * Convenience entry point for an adapter that wants to cache the raw JSON-RPC {@code result} straight off a 077 * {@link JsonRpcResponse}: stores it, or does nothing if the response is <jk>null</jk>, carries a JSON-RPC 078 * {@code error}, or has a <jk>null</jk> {@code result}. 079 * 080 * <p> 081 * Callers never need to check for an error response themselves before caching, since this method never 082 * forwards one to {@link #put(String, String, Object, long)}. Note that this caches the raw wire 083 * {@code result} (e.g. a {@code Map}), not a typed result bean — an adapter that instead caches an 084 * already-re-marshaled typed bean (as the {@code 2026-07-28} client adapter does) should call 085 * {@link #put(String, String, Object, long)} directly instead of this method. 086 * 087 * @param scope The cache-scope partition key. Must not be <jk>null</jk>. 088 * @param key The cache key (typically method + canonical params). Must not be <jk>null</jk>. 089 * @param response The JSON-RPC response to conditionally cache. May be <jk>null</jk>. 090 * @param ttlMs The time-to-live in milliseconds. A value {@code <= 0} means the entry never expires. 091 */ 092 default void putResult(String scope, String key, JsonRpcResponse response, long ttlMs) { 093 if (response == null || response.getError() != null) 094 return; 095 var result = response.getResult(); 096 if (result == null) 097 return; 098 put(scope, key, result, ttlMs); 099 } 100 101 /** 102 * Removes all cached entries across all scopes. 103 */ 104 void clear(); 105}