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.annotation; 018 019import org.jspecify.annotations.NonNull; 020 021import java.lang.annotation.ElementType; 022import java.lang.annotation.Retention; 023import java.lang.annotation.RetentionPolicy; 024import java.lang.annotation.Target; 025 026/** 027 * Publishes and enforces an MCP custom mirrored header for one typed tool 028 * input property. 029 * <p> 030 * The annotation name is the suffix of the corresponding 031 * {@code Mcp-Param-*} request header. For example, 032 * {@code @McpHeader(name = "Region")} 033 * publishes {@code x-mcp-header: "Region"} on the derived property schema and 034 * requires {@code Mcp-Param-Region} to agree with that property's request-body 035 * value whenever the value is present and non-null. If the body property is 036 * absent or {@code null}, the corresponding request header must also be absent; 037 * a mirrored header never supplies or injects a tool argument. 038 * <p> 039 * A method-parameter declaration must also carry {@link McpToolArgument} and 040 * belong to an {@link McpTool} method. A record-component declaration applies 041 * when that component is reached from a typed tool input solely through record 042 * properties; it may also carry {@link McpToolProperty} to customize its 043 * published property metadata. Mirrored properties must derive to the JSON Schema type 044 * {@code string}, {@code boolean}, or {@code integer}. Soklet 045 * rejects unsupported placements, other property types, invalid HTTP 046 * field-name suffixes, and case-insensitive name collisions while deriving the 047 * tool schema. 048 * <p> 049 * Integer safety is enforced on each request value. Types such as {@code long}, {@link Long}, and 050 * {@link java.math.BigInteger} are accepted without narrowing their derived 051 * schemas. Each mirrored integer value must be between 052 * {@code -9007199254740991} and {@code 9007199254740991}, inclusive. Its header 053 * must use canonical decimal spelling: {@code 0}, or digits without leading 054 * zeros, optionally preceded by {@code -}; {@code +1}, {@code 01}, and 055 * {@code -0} are invalid. A mathematically integral JSON number such as 056 * {@code 1.0} or {@code 1e0} can match the header {@code 1}. 057 * A mismatch produces the fixed HTTP 400 / JSON-RPC {@code -32020} 058 * {@code "Header mismatch"} response after admission and before handler entry. 059 * <p> 060 * Mirrored headers require MCP {@code 2026-07-28}. Endpoint construction rejects 061 * a tool with mirrored-header declarations that selects either supported 062 * {@code 2025} revision. 063 * <p> 064 * Mirrored headers can expose argument values to HTTP infrastructure. Do not 065 * mirror credentials, secrets, or other sensitive values unless that exposure 066 * is explicitly intended and protected by the deployment. 067 * 068 * @author <a href="https://www.revetkn.com">Mark Allen</a> 069 */ 070@Target({ ElementType.PARAMETER, ElementType.RECORD_COMPONENT }) 071@Retention(RetentionPolicy.RUNTIME) 072public @interface McpHeader { 073 /** 074 * The non-empty HTTP field-name token appended to {@code Mcp-Param-}. 075 * 076 * @return mirrored-header suffix 077 */ 078 @NonNull 079 String name(); 080}