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}