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}