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;
018
019
020import java.util.*;
021import java.util.function.*;
022
023import org.apache.juneau.commons.inject.*;
024import static org.apache.juneau.commons.utils.Shorts.*;
025
026/**
027 * Handler for a single MCP tool.
028 *
029 * <p>
030 * Implementations declare a {@link #descriptor() descriptor} (returned from {@code tools/list}) and
031 * a {@link #call(Map, BeanStore) call} body invoked when a matching {@code tools/call} arrives. Both
032 * use the revision-neutral model, so a handler compiles against exactly one protocol revision's
033 * worth of assumptions: none.
034 *
035 * <p>
036 * The {@link BeanStore} argument is the per-request bean store, letting handlers look up additional
037 * services (or the underlying {@code RestRequest}) without this interface depending on REST runtime types.
038 *
039 * <p>
040 * This is a genuine two-method contract, not a single-method one: both {@link #descriptor()} and
041 * {@link #call(Map, BeanStore)} are abstract, so it is deliberately <b>not</b> annotated
042 * {@code @FunctionalInterface} and a bare lambda cannot implement it. Routing calls {@link #descriptor()}
043 * on every registered handler for both {@code tools/list} and {@code tools/call}, so a handler with no
044 * usable descriptor is not a valid handler; making {@link #descriptor()} abstract catches that at compile
045 * time instead of the first request to reach it at runtime. Use {@link #of(McpToolSpec, BiFunction)} for
046 * the common case of wiring both in a single expression.
047 */
048public interface McpToolHandler {
049
050   /**
051    * Builds a handler from a fixed descriptor and a {@code call} lambda.
052    *
053    * <p>
054    * The sanctioned one-expression path for registering a tool: unlike a bare lambda (which cannot
055    * satisfy this two-method interface at all), this factory wires both {@link #descriptor()} and
056    * {@link #call(Map, BeanStore)} from a single call.
057    *
058    * @param descriptor The static descriptor returned from every {@link #descriptor()} call. Must not be
059    *    <jk>null</jk> and must carry a non-blank {@link McpToolSpec#getName() name} (routing matches
060    *    incoming {@code tools/call} requests on name, so a nameless tool would be silently unreachable).
061    * @param call The call body, invoked with the incoming arguments and the per-request bean store. Must not be <jk>null</jk>.
062    * @return A new handler wiring both. Never <jk>null</jk>.
063    */
064   static McpToolHandler of(McpToolSpec descriptor, BiFunction<Map<String,Object>,BeanStore,McpToolOutcome> call) {
065      reqnn("descriptor", descriptor);
066      reqnb("descriptor.getName()", descriptor.getName());
067      reqnn("call", call);
068      return new McpToolHandler() {
069         @Override public McpToolSpec descriptor() {
070            return descriptor;
071         }
072
073         @Override public McpToolOutcome call(Map<String,Object> arguments, BeanStore ctx) {
074            return call.apply(arguments, ctx);
075         }
076      };
077   }
078
079   /**
080    * Returns the static descriptor for this tool.
081    *
082    * <p>
083    * The {@link McpToolSpec#getName() name} value is used by the bound {@link McpRevision} to route
084    * incoming {@code tools/call} requests, so each handler in an {@link McpServerConfig} must use a
085    * unique name.
086    *
087    * @return The tool descriptor. Never {@code null}.
088    */
089   McpToolSpec descriptor();
090
091   /**
092    * Invokes the tool.
093    *
094    * @param arguments The arguments object passed in the JSON-RPC params (never {@code null}; empty map when omitted).
095    * @param ctx Per-request bean store. Never {@code null}.
096    * @return The call result. Never {@code null}.
097    */
098   McpToolOutcome call(Map<String,Object> arguments, BeanStore ctx);
099}