Skip to content

Update GenHTTP to 11.2, and serve MCP with its module - #125

Merged
Kaliumhexacyanoferrat merged 1 commit into
mainfrom
genhttp-11.2
Nov 2, 2026
Merged

Kaliumhexacyanoferrat merged 1 commit into
mainfrom
genhttp-11.2

Conversation

@Kaliumhexacyanoferrat

Copy link
Copy Markdown
Collaborator

Updates GenHTTP to 11.2 and moves the platform onto what 11.2 brings rather than keeping its own versions beside it.

The MCP endpoint is GenHTTP's MCP module

/mcp was a hand-written JSON-RPC server (McpHandler, McpProtocol, a 400-line catalogue of JSON schemas and a dispatch switch in McpTools). It is now GenHTTP.Modules.Mcp:

  • McpEndpoint builds it with DependentTools.Create(), the server's identity, the instructions (unchanged) and McpTools, taken from the container per call.
  • The 25 tools are methods of McpTools marked [Tool], described by [Summary], their typed parameters the arguments. The names, titles, hints, descriptions and answers are the same as before; each declaration forwards to the implementation that was there.
  • Fixed limits are attributes on the parameter, advertised in the schema and checked before the tool runs: [MaxLength] on a feature's name, a showcase entry and a license's author, [Range] on the rows and lines read, [AllowedValues] on a kind of data and a log level, and an enum for the editor view. An argument that does not fit is refused before the tool runs rather than read leniently: a limit above its maximum is no longer clamped, and a kind of data or a view has to be spelled as the schema says. File encodings are still checked by the rule the API uses (LambdaSource.Validate), and the log level still takes warning and debug.
  • The protocol is the module's: the stateless revision 2026-07-28 next to 2025-03-26 to 2025-11-25 (we spoke 2025-03-26 and 2025-06-18). It still answers with one JSON object and keeps no session.
  • A refusal of the platform (LambdaException) is answered as an error that says why in a sentence (Refusing), and so is an argument that does not fit. A failed deployment or merge is still an error carrying its diagnostics as structured content. The build agent's runner (builder.mjs) now takes the sentence of such an error as the step's problem, for the platform's own tools only.
  • LAMBDA_MCP_ORIGINS keeps working as it is configured: a host stands for its HTTPS origin, and an entry with a scheme is taken as the origin it names. The module compares whole origins, port included, where the old code compared hosts only. An entry the module could not read would keep the server from starting, so it is left out with a warning instead.
  • The module writes a line of its own for every tool call and every refusal. Its category is filtered below warnings (Program), so the log keeps one line per operation in the platform's words; a tool that crashes is still logged as an error.
  • Descriptions are fixed when the endpoint starts, so the two that read values at runtime now point to them instead: the size of a showcase picture (a panel limit) is in platform_guide under showcase, and the demo ids are in list_demos. The fixed numbers are written out, as the API description does (a test holds the license list to SourceLicenses).

OpenAPI: the admin token describes itself

11.2's API key concern declares its security scheme and its 401 and 403 answers. The admin routes no longer carry a hand-written header and refusals: AdminAuthentication names the scheme adminToken, and Scalar asks for the token once. A test checks that the panel's operations require it and that no other operation names a scheme.

Lambdas can offer tools to agents and describe their API

  • The MCP module joins the modules a lambda may use, and GenHTTP.Api.Content.Documentation ([Summary], [Remarks], [Tag], [Response]) joins the implicit imports, in the export too. I checked by reflection that neither brings a type name that clashes with one already imported.
  • demo-crud shows both: its routes and its input are described with [Summary]/[Remarks], and Tools.cs offers the same tasks to an agent at mcp (list_tasks, add_task, complete_task, remove_task), over the same store and validation as the API. Its docs, test table and smoke script say so.
  • demo-registration reads its session with 11.2's WithCookie instead of an extractor of its own.
  • platform_guide has toolsForAgents and describingAnApi, and the README and CLAUDE.md say the same. CLAUDE.md also records the decision behind this PR: use what the framework offers as it is meant to be used.

Breaking, in the framework

11.2 moved the documentation attributes from GenHTTP.Api.Content.Services to GenHTTP.Api.Content.Documentation. That namespace only existed in 11.1, and the owner accepted the break: a lambda that imports the old namespace no longer compiles. Since the new namespace is now imported implicitly, such a lambda only needs that using line removed.

Tests

  • New: DescriptionTests (a lambda serving MCP tools; a lambda describing its API in its own words), AnAgentKeepsTheSameTasksWithToolsOfItsOwn, OnlyABrowserOnAHostTheOperatorNamedMayUseIt, the admin security scheme, the license list.
  • Removed: the tests of protocol mechanics (version negotiation, notifications, GET, unknown version header). They are now the framework's, and CLAUDE.md says not to test the frameworks.
  • Run here: 400 tests of every class touching the change, in batches, all passing. The full suite was not run on this machine, which also serves the live instance. On main (11.1) as well, a full run here ends in request timeouts and a crashed test host, so CI is the judge of the whole.

Flagged for GenHTTP (not worked around beyond what the platform needs)

  1. No exception mapping for tools. The MCP module answers a ProviderException as a tool error and treats anything else as a crash, logged as an error with its stack trace. ProviderException is sealed, so a domain exception such as LambdaException cannot become one: every tool has to catch it (Refusing). Something like the webservices' IErrorMapper for tools would remove that.
  2. An omitted optional argument is validated against its default. [AllowedValues("a", "b")] string? x = null refuses a call that leaves x out, because null is checked against the list (MethodToolInvoker.InvokeAsync: argument.Validator?.Validate(value, …) runs after Read returned the default).
  3. A nullable reference parameter is advertised as non-nullable ("type": "string": the schema is made from the parameter's type alone). Together with 2., listing null gives "type": "string", "enum": ["a", "b", null], a schema that contradicts itself. The tools here avoid both: the log level has a default value instead of null.
  4. IEnumerable<T> parameters would be injected rather than read (GenHTTP.Modules.DependencyInjection, found by reading the source, not reproduced). DependencyInjector.Supports asks IServiceProviderIsService.IsService, which Microsoft's container answers with yes for every closed IEnumerable<T>. So a tool or webservice parameter IEnumerable<string> tags would be handed an empty sequence from the container instead of the client's value. McpTools takes List<T>, and is not affected.

🤖 Generated with Claude Code

The MCP endpoint is GenHTTP's MCP module: the tools are methods of
McpTools marked [Tool], described by [Summary], with typed parameters
whose fixed limits are attributes the module advertises and checks. The
hand-written protocol (McpHandler, McpProtocol, the JSON catalogue) is
gone; a refusal of the platform is answered as an error in a sentence.

The admin token's concern now describes itself in the OpenAPI document
(the adminToken security scheme). Lambdas may use the MCP module, and
the documentation attributes are imported for them; demo-crud offers
its tasks as tools and describes its API, demo-registration reads its
session WithCookie. The guide, README and CLAUDE.md say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Kaliumhexacyanoferrat
Kaliumhexacyanoferrat merged commit bfa64a5 into main Nov 2, 2026
3 checks passed
@Kaliumhexacyanoferrat
Kaliumhexacyanoferrat deleted the genhttp-11.2 branch November 2, 2026 12:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant