Skip to content

declarative configuration

github-actions[bot] edited this page Sep 15, 2026 · 1 revision

🏷️ Declarative configuration

Four native method annotations register ordinary MCP handlers (TachyonAnnotationProvider#register). This page owns their contracts. Stability markers in the same package are not configuration annotations; see api-stability.

🔌 Registration

TachyonServer.builder()
        .annotations(a -> a.register(service))
        .build();

AnnotationContext defaults to the native provider; withProvider(p) switches subsequent registrations (AnnotationContext#currentProvider). Builder registrations delegate to server.annotations(...), using the server's registries and configured codecs (DefaultServerBuilder#applyAnnotationRegistrations, DefaultTachyonServer#annotations).

Metadata class and invocation receiver can differ. The invoker resolves a callable method once and invokes the proxy, preserving advice (TachyonAnnotationProvider#register, MethodInvoker#invocationMethod). Bean discovery/lifecycle: spring-boot. External annotation providers: integrations.

⚙️ Shared rules

Rule Contract Proof
Discovery Class hierarchy and public interfaces; synthetic/bridge methods skipped. Private annotated methods rejected. AnnotationInvocationSupport#discoverMethods
Feature keys One MCP annotation per method. Duplicate feature keys within one service fail registration; keys are scoped by feature kind. TachyonAnnotationProvider#register, TachyonAnnotationProvider#claim
Names/descriptions Tool, resource, and prompt names default to method names when blank. Blank descriptions become absent. TachyonAnnotationProvider#nameOf, TachyonAnnotationProvider#emptyToNull
Context InteractionContext is injected in any position, excluded from arguments. MethodInvoker#namedBindings, MethodInvoker#isContext
Named binding Compile with -parameters; missing names fail registration. Named values are required unless JSpecify @Nullable or Optional-family typed. MethodInvoker#namedBindings, JavaTypeSchemas#isOptional
Scalars Resource/prompt/named-completion arguments support String, numeric/boolean primitives and wrappers, enums, or Optional-family wrappers of those. Enums bind by exact constant name() (same values as tool schema enum), independent of JSON SPI; other values ⇒ must be one of [...]. MethodInvoker#namedBindings, MethodInvoker#coerce, MethodInvoker#enumConstant, AnnotationInvocationSupport#JSON_SCHEMA_TYPES, AnnotationInvocationSupport#requireBindable
Failures Missing required values and decode failures become InvalidArgumentException. Handler exceptions are unwrapped for normal dispatch. MethodInvoker#invoke, MethodInvoker#bindNamed

🛠️ Tools — @McpTool

Options: name, description (McpTool).

@McpTool(description = "Add two integers")
int add(int left, int right) {
    return left + right;
}

After excluding context, a single record/POJO/Map parameter receives the whole arguments object. Otherwise arguments bind by parameter name, including decoded object-valued arguments (MethodInvoker#forTool).

Input schema follows the binding shape; a whole-arguments Map<String, V> keeps its additionalProperties value schema (generic type, not erased Map.class). An object-classified declared return type supplies an output schema through JsonSchema.generate; schema internals belong in json-layer (MethodInvoker#inputSchema, TachyonAnnotationProvider#registerTool).

📄 Resources — @McpResource

Options: required uri, plus name, description, mimeType (McpResource).

@McpResource(uri = "config://mode", mimeType = "text/plain")
String mode() {
    return "production";
}

@McpResource(uri = "users://{id}")
String user(String id) {
    return "User " + id;
}

No URI variables ⇒ static resource, no named arguments. URI variables ⇒ resource template; the template variable set must exactly equal the non-context parameter-name set; missing or extra names fail registration. Blank URI fails registration (TachyonAnnotationProvider#registerResource).

For mapped contents, nonblank mimeType wins. Otherwise an object-classified declared return type selects application/json; other types leave MIME unset. Explicit ResourceContents retains its own fields (TachyonAnnotationProvider#mimeTypeOf, ResultMappers#resourceContents).

💬 Prompts — @McpPrompt

Options: name, description, role (default Role.USER) (McpPrompt).

@McpPrompt(name = "trip", description = "Plan a trip")
String trip(String city, @Nullable String country) {
    return "Plan a trip to " + city + (country == null ? "" : ", " + country);
}

Each named scalar becomes an advertised prompt argument with its required flag. Values bind from the prompt request (MethodInvoker#promptArguments, TachyonAnnotationProvider#registerPrompt). The annotation role applies only to generated messages; explicit messages/results keep their own roles (ResultMappers#promptResult).

✨ Completions — @McpCompletion

Options: exactly one nonblank prompt name or resource URI/template, matched verbatim. One completion method per target per service; prompt and resource targets are distinct (McpCompletion, TachyonAnnotationProvider#registerCompletion).

@McpCompletion(prompt = "trip")
List<String> completeCity(String city, @Nullable String country) {
    return searchCities(city, country);
}

First non-context parameter must be String. Its reflection name selects the completed argument; its value is current partial text, including empty text. Later scalars bind resolved siblings using the shared required/nullable rules. Partial text overrides stale context for the same argument (MethodInvoker#forCompletion, MethodInvoker#invokeCompletion).

Different argument ⇒ empty result without invoking the named handler (TachyonAnnotationProvider#registerCompletion).

For several arguments on one target, take one CompletionRequest, optionally with context. It exposes argument identity, partial text, siblings, and metadata. Cannot mix full request and named arguments (McpCompletion, MethodInvoker#forCompletion). Registration uses existing completion registries; no schema generation (TachyonAnnotationProvider#registerCompletion).

Registration order per service: all tools/resources/prompts first, then completions. A named completion argument must exist on its target when the target is known — declared by the same service, or already in the prompt/resource-template registry. Unknown ⇒ @McpCompletion argument 'x' is not declared by prompt 'p' [..] at build. Target not yet registered (another service later) ⇒ unchecked (TachyonAnnotationProvider#register, TachyonAnnotationProvider#registeredArguments).

🔤 Enum auto-completion

Prompt or resource-template enum arguments (incl. Optional<Enum>) get a completion handler for free: case-insensitive prefix over constant name()s; other arguments ⇒ empty. Skipped when the same service declares @McpCompletion for that target (TachyonAnnotationProvider#registerEnumCompletion, MethodInvoker#enumArguments).

⚠️ Registry is last-write-wins per target; an explicit completion from a different service registered before the enum prompt gets replaced. See findings.

🎁 Result mapping

Annotation Accepted results and mapping Null behavior Proof
@McpTool ToolResult passes through; ContentBlock becomes content; scalars/enums become text; Iterable/array becomes JSON text; other objects become structured content. void/null ⇒ empty content ResultMappers#toolResult
@McpResource ResourceContents passes through; String ⇒ text, byte[] ⇒ blob, other objects ⇒ JSON text at the requested URI. Invocation error ResultMappers#resourceContents
@McpPrompt PromptResult passes through; String/ContentBlock/object ⇒ one message (object as JSON text); PromptMessage preserves its fields; List maps each element to a message. Null result or list element ⇒ invocation error ResultMappers#promptResult
@McpCompletion CompletionResult passes through with total/hasMore/metadata; List<String> ⇒ candidates. Other declared return types fail registration. Null result or null/non-string list element ⇒ invocation error ResultMappers#requireCompletionReturnType

🧭 Related pages

  • configuration — builder groups and defaults.
  • feature-registries — runtime registries and capability resolution.
  • testing — declarative wire coverage and registration-validation tests.

📄 source .llm-wiki/concepts/declarative-configuration.md · updated 2026-09-15 · verified at 9eec1092 · tags [concept, configuration, annotations]

Clone this wiki locally