Server data from the Official MCP Registry
Write, deploy and host small C# web services and sites at a public address.
About
Write, deploy and host small C# web services and sites at a public address.
Remote endpoints: streamable-http: https://genhttp.dev/mcp
Security Report
Valid MCP server (1 strong, 0 medium validity signals). No known CVEs in dependencies. Imported from the Official MCP Registry. 1 finding(s) downgraded by scanner intelligence.
Endpoint verified · Open access · 1 issue found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Found in Source Code
Found by scanning the linked source code. This listing connects to a hosted endpoint, so none of this runs on your machine: it describes what the server software does where it is hosted.
How to Connect
Remote Plugin
No local installation needed. Your AI client connects to the remote endpoint directly.
Add this to your MCP configuration to connect:
{
"mcpServers": {
"dev-genhttp-lambda": {
"url": "https://genhttp.dev/mcp"
}
}
}Documentation
View on GitHubFrom the project's GitHub README.
GenHTTP Lambda
Write a C# snippet that returns a GenHTTP IHandler, press deploy, and get a
public URL that serves it. The snippet is compiled with Roslyn at runtime and
the resulting handler is hosted by the same [GenHTTP][genhttp] server that
serves this application.
return Inline.Create()
.Get(() => "Hello from my lambda!");
Quick start
docker compose up --build
The application is then available at http://localhost:8080/. Its data - the
SQLite database, the stored code, and the workspaces and databases of the
lambdas - lives in a named volume mounted at /data. The lambdas answer at
subdomains of LAMBDA_HOSTING_URL, one each (see
Where the lambdas answer).
Developing locally
Two processes: the .NET server, and Vite serving the frontend with hot reload.
# the server, on http://localhost:8080/
dotnet run --project src/GenHTTP.Lambda
# the frontend, on http://localhost:5173/ (proxies /api, /lambda and /features to the server)
cd src/Frontend && npm install && npm run dev
Work against http://localhost:5173/ while developing. The lambdas answer at
subdomains of localhost named after their keys - http://quiz.localhost:8080/ -
which browsers resolve without a record. The server on its own answers with a
placeholder page until a frontend has been built into its web root, which is
what npm run build does:
cd src/Frontend && npm run build # writes src/GenHTTP.Lambda/wwwroot
English is the source of every page; the other languages are translated by script and agent, so that only what changed is read and written:
cd src/Frontend
npm run translations -- extract # a request per language in .translations/
# one `translator` agent per request (.claude/agents/translator.md, on Sonnet)
npm run translations -- apply # the answers into the catalogs, checked
npm run translations -- check # every language against English
CLAUDE.md ("How a change is translated") has the rules.
Build and test everything the way CI does:
dotnet build GenHTTP.Lambda.slnx -c Release
dotnet test GenHTTP.Lambda.slnx -c Release
The tests compile and execute real lambdas against a real server on a random port, each against its own temporary data directory.
Routes
| Route | Serves |
|---|---|
/ | the landing page |
/build | the text box for somebody who wants an app built, no code |
/ship | for developers: connect an agent and publish what it built |
/docs | the guide for owners |
/showcase | the lambdas their owners chose to show |
/source | the lambdas whose owners published their code |
/source/:publicKey | the published code of one: its files, documentation, tests and changes, per version |
/source/:publicKey.git | the published code as a git repository, read only |
/enterprise, /terms, /privacy, /imprint | the company pages |
/editor/create | the creation assistant |
/editor/:privateKey | the editor for one lambda |
/editor/:privateKey/:name.git | the lambda as a git repository, to clone and push to (any name; the editor offers the public key) |
/lambda/:publicKey/… | where a lambda answered once: a permanent redirect to where it answers now, with the rest of the path and the query |
/features/:feature | the preview of a feature, while it is online |
any path, at {publicKey}.{hosting domain} | the deployed handler - or, while the lambda answers at a domain of its own, a permanent redirect there |
| any path, at a lambda's own domain | the deployed handler of a premium lambda with that domain |
| the hosting domain itself | a page saying what is hosted there, leading to the site |
/api/v1/ | everything the editor calls, see below |
/mcp | the same, for agents |
/admin | every lambda on the server, for whoever runs it |
API
Browsable at /api/v1/scalar/, described by /api/v1/openapi.json (or
.yaml). The description is the reference for whoever calls the API: it opens
with an introduction (getting started, keys, files, limits, errors), groups
the operations by what they are about, and says of each what it does, what it
leaves alone and every way it is refused. It is written with GenHTTP's
documentation attributes beside the code (see ApiDocumentation), and
ApiDocumentationTests keeps it complete.
A lambda is addressed by its editor key, which is also what authorizes the
call. Things that can be listed, read, created or changed are resources; what
is a process rather than a thing is a verb in the path. A refusal is answered
with a status and { status, error, message }, where error is Invalid
(400), Forbidden (403), NotFound (404), Conflict (409) or TooMany (429).
| Endpoint | Does |
|---|---|
POST /lambdas | creates a lambda (optionally with view: Full or Simple) |
GET / PATCH / DELETE /lambdas/:privateKey | reads, changes (its key, its view), removes it |
GET /lambdas/:privateKey/export | the newest version as a runnable .NET 10 project with a Dockerfile (zip), with its database, see below |
GET / POST /lambdas/:privateKey/versions | lists versions, saves a new one (optionally with specification and change) |
GET /lambdas/:privateKey/versions/:version | reads one version (?folder=docs/ for its documentation alone, resources/ for its resources) |
GET /lambdas/:privateKey/versions/:version/zip | one version's files as a zip (?layout=project laid out as a clone) |
POST /lambdas/:privateKey/versions/zip | saves a zip of all files as a new version (?layout=project for one laid out as a clone) |
POST /lambdas/:privateKey/versions/changes | changes some files of the newest version, as a new one |
GET /lambdas/:privateKey/deployment | what is online, and until when |
POST /lambdas/:privateKey/deployment/start / stop | puts a version online, takes it off |
GET /lambdas/:privateKey/deployment/history | every stretch it was online, and what ended it |
GET /lambdas/:privateKey/summary | the dashboard: state, traffic, problems, storage, what the documentation says |
GET /lambdas/:privateKey/traffic | requests by minute and quarter hour, statuses, paths |
GET /lambdas/:privateKey/logs | its own log, followed with ?since=; no visitor addresses |
GET /lambdas/:privateKey/agent | the agent's change of it - under way, or the last one - and how many are left today |
POST /lambdas/:privateKey/agent/start / stop | asks the agent for a change, stops it |
GET /lambdas/:privateKey/data | the kinds of data it keeps, and how full each is |
GET / PUT / DELETE /lambdas/:privateKey/data/:kind | one kind (database, workspace, secrets); switches it on; switches it off and deletes what it held |
GET /lambdas/:privateKey/database | its database: tables and views, their columns and rows, how full it is, whether the code connects |
GET /lambdas/:privateKey/database/tables/:table | a page of one table's rows, newest first (offset, limit, order, descending) |
GET /lambdas/:privateKey/secrets | the secrets by name, which names the code reads, and which of those have no value |
PUT / DELETE /lambdas/:privateKey/secrets/:name | stores a value ({ "value": … }), never read back; removes one |
GET /lambdas/:privateKey/files | lists the workspace |
GET / PUT / DELETE /lambdas/:privateKey/files/:path | one file, its path encoded (a%2Fb.txt), as base64 up to 32 MB |
GET / PUT /lambdas/:privateKey/files/:path/content | one file as it is, streamed, however large |
PUT /lambdas/:privateKey/folders/:path | makes a folder |
GET / POST /lambdas/:privateKey/features | lists the features, starts one (name, specification, base) |
GET / PATCH / DELETE /lambdas/:privateKey/features/:feature | one feature with its files (?folder= as for a version); changes its name, notes or base; deletes it |
PUT /lambdas/:privateKey/features/:feature/files | replaces its files (?deploy=true puts its preview online) |
POST /lambdas/:privateKey/features/:feature/changes | changes some of its files |
GET / PUT /lambdas/:privateKey/features/:feature/zip | its files as a zip; a zip put back into it (both with ?layout=project) |
POST /lambdas/:privateKey/features/:feature/preview/start / stop | puts its preview online, takes it off |
POST /lambdas/:privateKey/features/:feature/merge | makes it the next version and deletes it (deploy to put that online) |
GET /lambdas/:privateKey/features/:feature/logs | what its preview has been doing |
GET /lambdas/:privateKey/features/:feature/data, POST …/data/refresh | its copy of the data; a fresh copy of the lambda's |
…/features/:feature/workspace[/:path[/content]], …/folders/:path | its copy of the workspace, as files and folders are the lambda's |
…/features/:feature/secrets[/:name] | its copy of the secrets, as secrets are the lambda's |
…/features/:feature/database[/tables/:table] | its copy of the database, as database is the lambda's |
POST /lambdas/:privateKey/code/check | compiles without saving |
POST /lambdas/:privateKey/code/semantics, completions, definition | what the editor asks the compiler |
GET /keys/:publicKey | whether a key is free, and if not, online |
GET /demos | the demos, and the keys to read them with |
GET / PUT / DELETE /lambdas/:privateKey/showcase | its entry on the showcase: read, list it (title, description, picture), take it off |
GET /lambdas/:privateKey/domain, PUT / DELETE | the domain of a premium lambda, whether it is served, and what it resolves to |
GET /showcases, GET /showcases/:publicKey/image | what /showcase lists, and a picture |
GET / PUT / DELETE /lambdas/:privateKey/analytics | who visits it, by day, over the last days (at most what its tier shows); switches counting on; switches it off and deletes what was counted, see Analytics |
GET / PUT / DELETE /lambdas/:privateKey/source | whether its code is published and under which license, the licenses on offer; publishes it or changes the license (license, author); takes it down |
GET /sources | what /source lists (search, order: stars, updated, published, skip, take) |
GET /sources/:publicKey | a published source: what it is, its license, where it runs, every version and what it changed, a ticket to star it with |
GET /sources/:publicKey/versions/:version | the files of a version as its project has them, packed on the first request |
GET /sources/:publicKey/versions/:version/files/:path, …/raw/:path | one file, its path encoded: its text; as it is (?download=1 to save it) |
GET /sources/:publicKey/versions/:version/zip | a version as a project to download |
POST /sources/:publicKey/star | stars it, or takes the star back (ticket, starred) |
POST /builds, GET /builds/:id | the text box on /build |
GET /system | terms, limits, starters, build agent, where a lambda answers (lambdaUrl) |
GET /telemetry, /logs, /admin/... | for whoever runs the installation |
Creating a lambda asks what somebody would like to build and offers the demos
in those words - "Keep track of things", "Let people sign up" - next to an empty
lambda. Picking a demo starts the new lambda as a copy of it, which is theirs to
change. The files are in Resources/Templates and listed in TemplateCatalog.
A lambda has two keys. The public one names its address - quiz answers at
https://quiz.genhttp.run/ - and may be changed; the private one is the
editor link and is shown only to whoever created the lambda - anyone holding
it can edit, deploy and delete. What the API answers about a lambda carries
both its address below the hosting domain, publicUrl, and where to link to
it, address - its own domain while it answers at one, publicUrl otherwise
(see Where the lambdas answer).
A lambda may also return a websocket rather than a document, in any of the
three flavours the module offers - Websocket.Functional(), .Reactive() and
.Imperative(). The handshake is an ordinary request and is bounded by the
execution timeout like any other; everything after it belongs to the
connection, which is why a socket may stay open far longer than a lambda is
given to answer. FrameType, which the imperative flavour reads off every
frame, is one of the names a lambda is given, so no import is needed.
A lambda may offer tools to AI agents itself, over the Model Context Protocol:
Tools.Create() with a class whose methods are marked [Tool], added at a
path such as mcp, which an agent then connects to. And a webservice of its
own describes itself as OpenAPI, in the words of [Summary], [Remarks],
[Tag] and [Response] on its routes and records. The MCP module and those
attributes are imported for every lambda and in its export; demo-crud shows
both, and the guide says how (toolsForAgents, describingAnApi).
The editor says when both free tier timers run out: a hint under the public URL for the deployment, and a chip beside the buttons for the lambda itself, which opens an explanation of how each one is extended.
Where the lambdas answer
Every lambda answers at a subdomain of one domain, the hosting domain, named
after its public key: https://quiz.genhttp.run/, the whole of the host its
own. LAMBDA_HOSTING_URL names that domain with its scheme and port; left
empty it is this server on localhost - http://quiz.localhost:8080/ - which
browsers resolve without a record in the DNS.
A host of its own rather than a path below the platform's, so that what
somebody built is a site of its own: a page of it cannot read what the
platform keeps in a browser - the admin token, for one - nor what another
lambda keeps there, and its paths start at the root of its host, so a
browser asking for /favicon.ico asks the lambda. The editor, the API, the
agents' endpoint and git stay where they are, on the platform.
- Its old address.
/lambda/{key}/…on the platform is answered with a permanent redirect to where the lambda answers now, with the rest of the path and the query as they were sent, byte for byte - a signature over them still matches - Moved Permanently for a read, Permanent Redirect for anything else, which a client sends again with the same method and body. It is answered before anything else the server does: no request line, no telemetry, no upgrade to HTTPS, since the address it sends to is secure already. A browser keeps it for a day rather than for good, because where a lambda answers changes when it gets a domain or loses one. A key nobody has is sent on as well - the address moved, whether or not something is there - while a path below/lambda/that could never have been a key is not found. - A domain of its own. A premium lambda with a domain answers there and
only there: its subdomain sends visitors on to the domain the same way, so
there is one address for search engines to keep and for visitors to pass
on. Removing the domain, or the tier that serves it, has the subdomain
answer again. For the same reason
www.in front of the domain sends its visitors on to it -www.shop.example.comtoshop.example.com- unless the owner entered the domain withwww., which is then where it answers, and nothing is sent there from the name without; or unless thatwww.is the domain of another lambda, or one of the platform's own names. - Nothing there. A subdomain nothing is online at - a key nobody has, a
lambda that is offline, a name no key can be - is told so in a plain page
that leads to the site, or in a line of JSON for anything but a browser.
The hosting domain itself, and
www.in front of it, has a page of its own that says what is hosted there and leads to the site, in the language the browser prefers; its words are inpages.json(hosting:/andhosting:/:key) and translated with the rest. Both link the site's legal notice and privacy policy, named as the site names them. Neither is one of the site's pages: none of the site's scripts is ever served at the hosting domain. - Keys are host names. A key is a label of the hosting domain, so the
names mail clients and servers look for below a domain (
mail,autoconfig,autodiscover,mta-sts) cannot be claimed, nor can a key starting withxn--, which a browser reads as an international name. A lambda is found by its key as it is spelled, whatever the rules for claiming one say today, so a rule made later takes nothing offline. - Previews stay on the platform. The preview of a feature answers at
/features/{feature}/, as it did, which is why a lambda links relatively. It is the one place a lambda's code still runs on the platform's host - with what the platform keeps in that browser in reach of its scripts - until previews get hosts of their own as well.
The request log names the host in front of the path - GET quiz.genhttp.run/api/items - as it does for a domain of its own, and the
traffic of a lambda counts its visitors by the host they came to.
What it takes is a wildcard record in the DNS - *.genhttp.run, and
genhttp.run for the page at its root, pointing at the server - and a
certificate for both names (see The hosting domain).
The hosting domain belongs on the Public Suffix List:
until it is there, a lambda can set a cookie for the whole domain, which the
browser then sends to every other lambda as well.
Versions, features and data
A lambda keeps three kinds of things, and they live differently.
A version is the program: its code - the C# it is compiled from, and
whatever else is kept with it, its documentation and its tests among it - and
its resources, which it reads and serves while it runs, the front end and
the database's migrations among them (see Code and resources).
A version never changes once it is saved, which is
what makes every one worth keeping - any of them can be compared with, and put back online
exactly as it was. Saving files (POST …/versions, write_code) makes a new
one.
A feature is a change worked on beside the lambda. It branches off a
version - the newest, unless another is named - with a copy of its files and a
copy of the lambda's data, and is changed in place as often as it takes; it has
no versions of its own. Its preview is built from its files against its copy of
the data and answers at /features/{feature}/, a random key nobody guesses,
told to search engines with X-Robots-Tag: noindex and a Disallow in
robots.txt. It serves what was deployed until it is deployed again, a restart
included, is counted neither in the lambda's traffic nor in its log, and on the
free tier goes offline like a deployment does when nobody works on it. Once it
does what was asked it is merged: its files become the next version, with
its notes, and the feature goes - preview, copy of the data and all; the
lambda's own data is not touched. Only a feature based on the newest version
can be merged, so that a merge never undoes a version saved after the feature
began - by the owner, an agent, or the merge of another feature. There is no
merging or rebasing machinery: whoever works on the feature brings a newer
version's changes in and then says so by moving its base (PATCH, or
update_feature), and the merge takes the lambda's lock and checks the base
once more, so two merges cannot both win. A lambda may have ten features open
(a limit of its tier, set in the panel); a demo has none. Each holds a full copy of the
workspace and of the database, taken on a thread of its own and swapped in
whole, so ten features of a premium lambda with a full workspace take ten times
its room on disk.
Data is what the program keeps: the database, a SQLite database for its
records; the workspace, a private directory of files; and the secrets. It
belongs to the lambda rather than to a version - every version reads and
writes the same data, and deploying, rolling back or merging never touches it.
It goes with the lambda, or when that kind of data is switched off, which
deletes what it held - the copies features work on included. Each kind is
switched on before the lambda can use it (PUT …/data/:kind, enable_data):
the workspace is on unless it was switched off, and a lambda whose workspace
is off is compiled with one that refuses every call and says why; the database
and the secrets are off until somebody switches them on. An agent may switch a kind on when what it builds
needs one; switching off deletes, so only the owner does that. The database
and the workspace share one allowance of room, a limit of the tier: each may
grow as far as the other leaves it. The editor has a Data section of its
own, beside Versions. Every kind is one view of it, picked from a row of
pills under its title, and each is shown the same way - what it is, whether it
is on, how full it is - with only what it holds drawn its own way.
Deploying picks a version (the latest by default), builds it and makes it live, and a lambda has at most one deployment at a time. A deployment that does not compile leaves the one before it standing - its resources included, which are written back after the failed attempt replaced them.
The editor is written for people who had an app built rather than for developers, so it calls a feature a draft, its copy of the data test data, and merging it putting it online - which merges it and deploys the version it becomes in one step. Nothing in it says merge, base or branch; a feature behind the newest version is out of date, and the agent is offered to bring it up to date. The drafts have a section of their own, shown once there is one, and opened on one the editor becomes the draft's: the sidebar holds it, with the buttons that try it and put it online, and its views are its documentation, its code, its tests, its test data and its preview's log. Showcase, domain, figures and deployments stay with the lambda. A version offers to start a draft from it, the save dialog of the code offers to save what was typed as a new draft instead of a version, and a change the agent leaves as a draft can be tried and put online from the Change section.
Code and resources
A version is any number of files, in two parts:
lambda.cs the snippet: what it returns is served
Store.cs more C#, compiled beside it
models/Item.cs C# in a folder, compiled the same
docs/, tests/ what is written about it (see below)
frontend/, … anything else: kept with the version, never compiled or served
resources/ what it reads and serves while it runs
Its code is every file that is not a resource. Its .cs files are
compiled into the lambda, in whichever folder they are, as a C# project
compiles its own - so the types can be laid out in folders - and nothing else
is: every other file, of any kind - its documentation and its tests, what a
front end is built from, scripts, configuration - is kept with the version and
never compiled or served. C# that is not part of the lambda, a test or a tool
of its own, therefore does not belong in the code as a .cs file. How the
code is arranged is the agent's to decide; the editor only reads docs/ and
tests/ as what they are (below).
Its resources, in resources/, are what it reads and serves while it
runs: pages, scripts, styles, pictures, the database's migrations. The
snippet reaches them through Resources, which is the other half of
Workspace - Resources.App("web") serves resources/web/ as a single page
application, Resources.Root + "migrations" is where Evolve finds the
migrations. A version saved before resources were called that still compiles:
Assets is the same thing under its old name.
What a version may come to is one allowance of room, its code and its resources together - a limit of its tier, set in the panel (see Limits). There is no separate limit for C#: the room bounds what the compiler is given as well.
A C# file is named with letters, digits, dashes, underscores and dots,
starting with a letter and ending in .cs, 40 characters at most, in folders
named as the rest of the code. A resource keeps the
rules files that are served have always had: letters, digits, dashes,
underscores and dots, an extension, no name starting with a dot, six folders
deep at most. The rest of the code may be named as tools name their files -
dot files, and the brackets, parentheses, plus signs and the like some tools
read meaning off - with no spaces, no .git folder, sixteen folders deep at
most. The names a project gives its own files at the top (Dockerfile,
.gitignore, .dockerignore, LICENSE, AGENTS.md,
CLAUDE.md, a project file, and the folders Platform, bin, obj,
workspace and database) are refused, since an export or a clone has them;
so are .lambda/ and assets/, which name where the documentation and the
served files used to be, with an answer that says where they go now.
Only the compiled C# and where the resources are identify a build, so a version that changes nothing else - its documentation, its tests, what it is built from - builds its handler from the assembly already loaded rather than compiling again.
Lambdas saved before this layout kept their served files at the top of a
version and their documentation, tests and build folder in .lambda/
(docs/, tests/, build/). Nothing on disk was rewritten: a version is
read in the layout it was saved in, so the old ones are translated as they are
read - site/app.js becomes resources/site/app.js, .lambda/docs/product.md
becomes docs/product.md, .lambda/build/package.json becomes
build/package.json - and saved in the new one when they are next saved (an
envelope with "version": 2, LambdaSource.Parse). C# that was in .lambda/
was never compiled and would be now, so it keeps what it holds under a name
that is not: .lambda/tests/Smoke.cs becomes tests/Smoke.cs.txt. A
feature's files are read the same way. In git, a lambda's history keeps the commits its clones already
have; the move is a commit of its own on top of each (see
Working with git).
What it is built from
Some of a lambda may be made by a build tool rather than written as it is served or compiled: a front end that is compiled or bundled, styles compiled from another language, code generated from a description. What the tool makes is part of the program like anything else - resources, or code - and what it makes them from is part of the code, in a folder of its own that the agent names. Nothing about those files is assumed: they are whatever the tool works from, in whatever layout it wants.
frontend/ what it is built from, in whatever shape the tool wants
frontend/README.md how it is built, and where the build goes
frontend/.gitignore what the build installs or keeps for itself, left out
resources/web/ what the build wrote, if it makes resources
*.cs what it wrote, if it makes code
The platform builds nothing. It never runs a build tool or installs what one needs, on a push or anywhere else, and never checks that the program is what those files build to. Whoever changes it - an agent with a shell, in a clone most of the time - changes them, runs the build where it works, and saves both together, in one version or one feature. A change to the sources that is not built changes nothing; a change made in what the build wrote is undone by the next build. This keeps the platform what it is - a server that hosts what it is given - and the tools the agent's, whichever they are.
What a build installs, caches or writes for itself - node_modules, target
or .venv, for example - is no part of a version, and every tool names it
differently, so the folder says what it is, in its own .gitignore files. A
clone follows them because git does; a zip put back through the API follows
them too, the way git add would (IgnoredPaths, git's rules: patterns at any
depth or anchored, folders only, !, **, the deeper file having the last
word, nothing coming back from a folder left out) - but keeps what the lambda
already has, as git keeps what it tracks. Only the .gitignore files of the
code count: a resource never has a name starting with a dot. A zip
is counted as it is sent, before any .gitignore leaves something out, so it
holds what a commit would - never what a build installed, which is no part of
it and would only make the upload large. A save that names a file keeps it, as
git keeps a file added on purpose; one that runs over the allowance names the
largest folder of the code, and says that its .gitignore keeps out what a
build installed.
One layout to build in. A build writes by paths relative to itself - from
frontend/, into ../resources/web - and a clone, an export and the zip of a
version or a feature lay the files out the same way, so it finds them wherever
it runs. The zip can be read back as a commit of a clone would be read too,
?layout=project on the download and on the upload: the platform's files
skipped, what the repository's .gitignore leaves out left out (bin/,
obj/), anything with no place in a lambda refused, and Project.cs that was
not touched kept as the snippet it was, to the byte. Agents that build are
steered to a clone or to that layout; agents with only the tools cannot build,
and never need it. A save through the tools that replaces every file says so
when it left out a folder of the code the version had - the sources of a
front end, most often - since those are dropped with it.
The build agent behind /build has no shell: it writes its front ends as plain
HTML, CSS and JavaScript, and leaves the sources of one it finds alone. The
demos have none either: they teach how a lambda is put together, with front
ends of plain files a reader follows without a build tool.
Documentation and tests
Every version keeps what is written about it beside its program, so that whoever changes it next - an agent, most of the time - starts from what the app is for and what has to keep working, which the code does not say:
| File | What it holds |
|---|---|
docs/product.md | what the app is, who it is for, what people do with it and why - in the terms of whoever asked for it; its first paragraph is the app in a sentence or two |
docs/decisions.md | the technical decisions, and why they were made |
docs/… | more pages, and pictures the pages show |
tests/README.md | how the app is tested automatically: what has to keep working, how each of it is checked, how to run the scripts |
tests/… | the scripts and the test data they use |
They are files of the version rather than fields in the database, because
they describe that version of the program: saved, compared in the history,
rolled back, copied into a feature and merged with it like any other file, in
the zip and in the export, with no table and no migration. Rolling back brings
back the documentation that was true of the version. They are code like any
other file that is not a resource - never compiled, whatever they are called
(a test written in C# is kept as a script, not as a .cs file), never served, left out of what identifies a
build, and counted towards what a version may come to. docs/ and tests/
are a convention, not a kind of file: nothing about them is special but that
the editor reads them as what they are, and the agents are told to keep them.
A zip of a version or a feature holds them, which is how an agent working
locally reads and writes them without further calls. The agents are told everywhere they decide something - the instructions,
the tool descriptions, the answers and platform_guide
(documentationAndTests) - to read them before changing a lambda, to write
all three pages with a new lambda, to update what a change affects in the same
save, and to run the tests against a feature's preview before merging it -
all of it in proportion to the lambda: a small one gets a few lines a page and
one quick check, not a test suite. read_lambda hands them over first and apart from the program's files, within
a budget of their own, and every save, deployment and preview that leaves a
page out says which. Every demo has all three and a script its tests run,
checked by a test.
In the editor they are two sections. Documentation is second in the sidebar, after the overview, in both views; Tests is in the full view only. Both are one component over their folder: the pages rendered to be read, each a pill, with the other files behind a Files pill, and the version picked as it is in the code. A page the version changed is marked, and shows the difference on request. On the newest version or in a draft a page can be edited, beside a preview of it; saved, it is the next version (a draft saves in place), put online with it when the newest version was online. The simple view calls the documentation About and shows the product page alone, with a way to have the agent correct it rather than to edit it. The overview of either view opens with the product page's first paragraph. The full one ends with how much of its two allowances the lambda uses - its code and resources, and its data - each a meter that opens the section it counts.
With fifteen sections and more, the full view's sidebar is in groups: the overview and the documentation on their own, then how people find it and what they get to see of it (showcase, open source, domain) - what the lambda is to everybody else, right under what it is - then where a change is made, under Develop (Change, the drafts, the code, the tests), the program and what it keeps (data, versions), and how it runs (deployments, stats, logs). On a phone the groups are a rule apart in the row of sections.
Code, in the full view only, is every file of a version: the code and the
resources as two trees beside the editor, each with what it comes to and
whether the public can reach it, and under them what the version comes to of
its allowance. It opens on the newest version, or a draft's files in a draft;
an older one is picked above it. A file of either part can be added, uploaded,
removed and edited there, and saved as the next version (a draft saves in
place and puts its preview online). Links to the sections it replaced -
/files and /build - open it.
Databases
Records - entries, accounts, orders, votes - go in the database: a SQLite file of the lambda's own, which its code opens a connection to and reads and writes through [Entity Framework Core][efcore] - or in plain SQL, where that is simpler. Its schema is migrations shipped with the version and applied by [Evolve][evolve] as the lambda starts, never Entity Framework's own:
// resources/migrations/V1__Create_notes.sql: CREATE TABLE notes (id INTEGER PRIMARY KEY, text TEXT NOT NULL);
using (var connection = Database.GetConnection())
{
new Evolve(connection) { Locations = [Resources.Root + "migrations"], IsEraseDisabled = true }.Migrate();
}
return Inline.Create().Get("count", () =>
{
using var db = new Notes(Database.GetConnection());
return db.Entries.Count();
});
public class Note
{
public long Id { get; set; }
public string Text { get; set; }
}
// maps the table the migration made, on the connection it is handed
public class Notes(SqliteConnection connection) : DbContext
{
public DbSet<Note> Entries => Set<Note>();
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite(connection, contextOwnsConnection: true);
protected override void OnModelCreating(ModelBuilder model) => model.Entity<Note>().ToTable("notes");
}
Database.GetConnection() hands out an open SqliteConnection of
Microsoft.Data.Sqlite, taken from a pool, which the caller disposes of - or a
context it is handed, with contextOwnsConnection: true - one per request,
never one shared between requests. The connection comes into the context
from outside, because inside a DbContext, Database is the context's own
(the compiler's message says so, and names LambdaEnvironment.Database).
Microsoft.Data.Sqlite, Microsoft.EntityFrameworkCore and EvolveDb are
imported in every file, like the GenHTTP modules. Agents are told to use it
synchronously - ToList and SaveChanges, never ToListAsync or
SaveChangesAsync - because SQLite answers synchronously whatever the method
is called, and the engine has an asynchronous model of its own that a blocking
call here does not disturb; this is said, not enforced. The demos all keep
their records like this, demo-crud the plainest of them.
Documentation truncated — see the full README on GitHub.
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Rea
Freeby Morluto · Developer Tools
Reverse engineer anything from your terminal or agent with one CLI and MCP server.
Paperclip
Freeby Paperclipai · Developer Tools
Trending hip-hop artist momentum scores across four cultural dimensions.
Fetch
Freeby Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
