-
Notifications
You must be signed in to change notification settings - Fork 10
feature registries
Verdict: each MCP feature = public façade in tachyon-api (Tools, Resources, Prompts, Completions, Tasks) + Default*Registry in core + static *MethodHandlers.register(map, …) JSON-RPC adapters. Sync fn wrapped into async fn at registration; dispatch always async. Registries are live — register/unregister after start() fires list_changed.
TachyonServer.annotations(...) (see declarative-configuration) feeds annotated objects into these same registries with configured codecs, after construction (DefaultTachyonServer#annotations). Used by Spring after singleton initialization; spring-boot.
Rule set lives in docs/architecture/guidance.md (AGENTS.md mandates reading it before changing SAMs/registry names). Observed in code:
| Façade | Sync SAM | Async SAM | Extras | Proof |
|---|---|---|---|---|
| Tools | ToolFn (throws) | AsyncToolFn | typed register(Class<I>,Class<O>, …, TypedToolFn) auto-generates schemas via JsonSchema.generate
|
Tools |
| Resources | ResourceFn | AsyncResourceFn | templates, unregisterByUri, findByUri, notifyResourceUpdated
|
Resources |
| Prompts | PromptFn | AsyncPromptFn | static List<PromptMessage> overload |
Prompts |
| Completions | CompletionFn | AsyncCompletionFn | keyed by prompt name or uri/template | Completions |
All: register(Descriptor, fn) + register(Consumer<Descriptor.Builder>, fn), unregister(name), find(name), descriptors() (name-sorted).
Sync adapters assert VT: HandlerFutures.assumeVirtualThread() (Java assert) e.g. DefaultToolRegistry#register, AbstractToolHandler.handle guardrail AbstractToolHandler#handle.
| Registry | Storage | Notes | Proof |
|---|---|---|---|
AbstractRegistry<D,R> |
ConcurrentHashMap by name + ChangeSupport
|
addItem replaces; list(limit,cursor,filter) name-sorted |
AbstractRegistry |
DefaultToolRegistry |
extends above | name [a-zA-Z0-9_\-./]+ ≤64 (SEP-986); input schema root type: object; output schema object; x-mcp-header rules; desc > 2048 warn |
DefaultToolRegistry |
DefaultPromptRegistry |
extends above | DefaultPromptRegistry#registerAsync |
|
DefaultResourceRegistry |
immutable Index(byUri) swapped under ReentrantLock (volatile read) + templates CHM + subscriptions CHM |
URI = identity; same URI other name ⇒ IAE; cursor key = b64(name)+b64(uri); template dup name ⇒ IAE | Index |
DefaultCompletionRegistry |
2 CHMs | no change events | DefaultCompletionRegistry |
Mode OFF ⇒ registration silently skipped (debug log) in every registry.
DefaultTachyonServer.resolveCapabilities DefaultTachyonServer:
-
Mode.ON⇒ advertise;OFF⇒ no;AUTO⇒ advertise iff registry non-empty (computed per request, so post-start registrations show up). -
tasksadvertised if enabled or any tooltaskSupport ∉ {null, FORBIDDEN}. -
loggingplain boolean;logging/setLevelhandler only registered when trueDefaultTachyonServer#registerDefaults. -
listChangedflags →setupChangeListenersbroadcastnotifications/*/list_changedto ACTIVE sessions and push tosubscriptions/listenstreamsDefaultTachyonServer#host.
Pagination.paginate(sorted, limit, cursor, key) Pagination#paginate: cursor = key of last item of previous page; unknown cursor ⇒ cursorValid=false ⇒ invalidParams("Invalid cursor"). Default page size 50 (Pagination#DEFAULT_PAGE_SIZE), overridable per feature (FeatureConfig.pageSize). resources/templates/list not paginated (any cursor ⇒ invalid) ResourcesTemplatesListHandler#handle.
ToolMethodHandlers.ToolsCallHandler ToolsCallHandler:
- Unknown or extension-disabled tool ⇒
invalidParams("Unknown tool: …")(hides extension tools). - Input schema violations ⇒ invalid params with joined errors.
- Emits DEBUG log
tachyon.toolsstarted/completed (visible only if log level permits)ToolsCallHandler#sendLogging. -
ToolResultsealed:Success | Error | InputRequired | TaskToolResult. -
Success.structuredValuePOJO serialized viaPayloadSerializer→JsonDocument, then output schema validation; violation ⇒ToolResult.error(msg)(tool-level error, not JSON-RPC), outcomePayloadFailureOperationOutcome. - Exceptions:
InvalidArgumentException⇒ invalid params with message; bareIllegalArgumentException⇒ "Invalid params" (message hidden); else internal error → errors. - Task path → tasks.
-
resources/read: invalid URI (blank, >8192,URISyntaxException) ⇒ invalid params; exact URI match first, then most specific template (longest literal after stripping{…}, tie → name)DefaultResourceRegistry#isValidResourceUri,DefaultResourceRegistry#matchTemplate; not found ⇒RESOURCE_NOT_FOUNDwith{uri}data. - Template parsing: RFC 6570-ish
UriTemplate(UriTemplate#create) →Map<String, UriTemplateValue>. -
resources/subscribe|unsubscribeneed session;notifyResourceUpdated(uri)→ subscribed sessions +subscriptions/listenstreams; dead session ids pruned lazilyDefaultResourceRegistry. - MIME guessing from bundled
mime-types.csv(ext,mime,isText)MimeTypes.
-
prompts/get: promptinputSchema(derived from arguments) validated with input validatorPromptsGetHandler#handleAsync; resultPromptResult.Messages | InputRequired. -
completion/complete: no handler ⇒ empty result (not error); >100 values truncated +hasMore=trueCompletionCompleteHandler#MAX_VALUES,CompletionCompleteHandler#handleAsync. - Completion registration is last-write-wins per target (
DefaultCompletionRegistry#registerForPromptAsync). Derived handlers (enum auto-completion) instead go through the@InternalApiCompletionRegistry#registerForPromptIfAbsent/#registerForResourceIfAbsent, so an explicit handler is never clobbered. PublicCompletionsdeliberately has no if-absent method — a registration context carrying a caller's ownCompletionscannot take a derived handler and fails fast — see declarative-configuration.
SubscriptionsListenHandler SubscriptionsListenHandler + SubscriptionRegistry .../features/subscriptions/SubscriptionRegistry.java:
- Only when mapper
supportsSubscriptionsListen;taskIdsfilter needs tasks extension. -
activateunder lock: register + acknotifications/subscriptions/acknowledgedas first event (SEP-2575)SubscriptionRegistry#activate. - Returned future and Observation both span the whole stream lifetime; completes on disconnect (
Cancelled) or genuine transport failure (StreamFailed(causeType, cause?)), never early at establishmentSubscriptionsListenHandler#handleAsync. - Terminal continuation threading and ack timestamp publication: observability.
- Disconnect ⇒ remove + cancel; shutdown
closeAll⇒ gracefulresultType: completeresultSubscriptionRegistry#closeAll.
registerDefaults DefaultTachyonServer#registerDefaults: initialize, server/discover, ping, subscriptions/listen, tools/resources/tasks/prompts/completion handlers, optional logging/setLevel. DiscoverHandler returns supported versions desc + capabilities + identity + advertised extensions DiscoverHandler#handle.
Related: tasks, extensions, json-layer, request-lifecycle.
📄 source .llm-wiki/concepts/feature-registries.md · updated 2026-09-18 · verified at 847c8d6a · tags [concept, tools, resources, prompts, completions]
🧭 Start
⚙️ Concepts (cross-cutting)
- request-lifecycle
- netty-pipeline
- protocol-versions
- sessions
- sse-streams
- feature-registries
- tasks
- extensions
- json-layer
- errors
- concurrency
- declarative-configuration
- configuration
- security-guards
- observability
- api-stability
📦 Modules