Add stateless field to registry server types#89
Conversation
Registry entries cannot declare that a server is stateless (POST-only, no SSE/GET). Users must pass --stateless manually on every thv run invocation. Adding the field to the type system lets registries declare this property so it applies automatically. Fixes stacklok#88 Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Greg Katz <gkatz@indeed.com>
532c262 to
4201eaf
Compare
|
Hey @gkatz2, thanks for the thorough write-up and for following up on the I've been thinking about this, and I'm hesitant to promote stateless to a first-class registry property. My concern is that stateless mode is really a runtime behavior of the server. Whether it accepts only POST and doesn't serve SSE on GET feels like something the proxy should be able to detect automatically rather than requiring explicit declaration. For example, when thv run starts proxying a server, it could probe the server's behavior (e.g., attempt a GET and observe a 405 or lack of SSE response) and automatically enable stateless mode. This would mean:
Before we add this to the type system, could you explore whether automatic detection at runtime is feasible for your use case? |
|
Thanks for the thoughtful feedback, @jerm-dro! Auto-detection is a good idea and the spec is clear (MUST return 405 on GET for stateless servers), so it would work for compliant servers. Two complications make it insufficient as the sole mechanism: some frameworks don't return 405 by default when a route is only registered for POST (e.g., Express.js returns 404), and intermediary proxies can return their own responses before the request reaches the MCP server. In those cases you'd still need an explicit override. The registry field covers all cases declaratively. The operator knows their server is stateless and declares it, same as transport type or OAuth config. Even if auto-detection lands later, the registry field gives operators a reliable override when detection gets it wrong. What do you think? |
|
Thanks for the context @gkatz2. I had not considered the possibility that auto-detection would fail. For the Express.js example, would it be possible to fix address the root cause upstream rather than working around it here? Is that much effort? I'm curious what @rdimitrov thinks about this. What's the cost/resistance to including this workaround? |
Summary
--statelessmanually on everythv runinvocation. Adding the field to the type system lets registries declare this property so it applies automatically.Stateless booltoBaseServerMetadata,ServerExtensions, both legacy registry schema definitions (serverandremote_server), and the publisher-provided schema. Updates bidirectional converters to preserve the field through round-trips.Fixes #88
Design notes
Why
BaseServerMetadata(shared) rather thanRemoteServerMetadataonly? Stateless mode applies to both remote servers and container workloads — stacklok/toolhive#4515 explicitly supports both. Placing the field onBaseServerMetadataalongsideTransportis consistent with how other transport-level properties are modeled. The trade-off is thatImageMetadatainherits a field that's irrelevant for stdio containers, but this mirrors existing fields likeTargetPortandProxyPortthat only apply to HTTP transports.Interface addition.
GetStateless()is added toServerMetadata, following the established pattern for all 14 existingBaseServerMetadatagetters. UnlikeProxyPort(which predates the interface and is duplicated on both concrete types),Statelessfollows the dominant pattern. No downstream repo implementsServerMetadatadirectly — all consumers use the concrete types, which inherit the getter through embedding.Test plan
GetStateless()throughServerMetadatainterfacetask lint-fix— 0 issuestask test— all pass with race detectiontask license-check— all headers presentGenerated with Claude Code