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.
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 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 | the deployed handler |
/features/:feature | the preview of a feature, while it is online |
| any path, at a lambda's own domain | the deployed handler of a premium lambda with that domain |
/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/. 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.
| 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=.lambda/ for its documentation and tests alone) |
GET /lambdas/:privateKey/versions/:version/zip | one version's files as a zip |
POST /lambdas/:privateKey/versions/zip | saves a zip of all files as a new version |
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 |
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/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 |
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 is part of its URL 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.
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.
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.
Versions, features and data
A lambda keeps three kinds of things, and they live differently.
A version is the program: every C# file and every asset, the front end
included - and, beside it in .lambda/, its documentation and its tests (see
below). 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 editor has
a Data section of its own, beside Files, which holds the files of a
version. 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 assets 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.
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 |
|---|---|
.lambda/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 |
.lambda/docs/decisions.md | the technical decisions, and why they were made |
.lambda/docs/… | more pages, and pictures the pages show |
.lambda/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 |
.lambda/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. A dot folder, because the
root of a version is the root its assets are served from, where a leading dot
means "not served" - and no asset has ever been allowed a name starting with
one, so no version saved before could have meant anything else by it. The
documentation and the tests are one kind of file, context
(LambdaSource.IsContext), whichever of the two they are: never compiled,
whatever they are called (a test in C# is not code), never served, and left out
of what identifies a build, so a version that only changes them builds to the
same assembly. Their names are held to docs/ and tests/ below .lambda/,
with the rules of an asset but no extension needed; a page is text. They count
towards the allowance of the assets, since every version carries its own copy
of them as it does of its assets.
A zip of a version or a feature holds them, which is how an agent working
locally reads and writes them without further calls - .lambda is a hidden
folder, so a zip is made of the folder's contents (zip -r ../f.zip .), not of
*. 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 for its files. 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, and the full one says which of the three pages the version has.
With fifteen sections, 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 (Change, the drafts, the code, the tests), the program and what it keeps (files, data, versions), and how it runs (deployments, stats, logs). On a phone the groups are a rule apart in the row of sections.
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:
// 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 = [Assets.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.
- Off until switched on. Switching it on (the editor,
PUT …/data/database,enable_data) makes it: an empty file, in write-ahead logging, so readers never wait for a writer. Switching it off deletes it, the copies of the features included. Either way the lambda and its previews are started again on their next request, so what they do as they start - migrating - is done against what is there now. A lambda created as a copy of a demo that keeps one starts with its own, switched on. - The one file it opens. A lambda does not make connections of its own.
The code guard refuses
new SqliteConnection(...)- also as a target-typednew(...), through an alias or a class derived from it, which is asked of the compiler rather than read off the text - and the names that would point a connection elsewhere or load native code into it (ConnectionString,SetConnectionString,LoadExtension,DbProviderFactories, …). Of Entity Framework, a lambda reaches the namespace a context is written in and the three describing a model takes (Metadata.Builders,ChangeTracking,Storage.ValueConversion), not itsInfrastructure,Storage,Internalor the rest, where a context's services and options are.UseSqliteis refused with anything but a connection, and so areEnsureCreated,EnsureDeletedand Entity Framework'sMigrate- the schema is Evolve's - all asked of the compiler.dynamicis refused too: it binds members at runtime, which is everything the guard reads the code for, skipped. The connection it is handed carries an authorizer SQLite asks about every statement:ATTACHandVACUUM INTO, which reach other files, are refused, as are the pragmas that would point it at other directories or lift its quota, and SQLite's defensive mode is on. The handle underneath is a type the code cannot name. - Its room. 256 MB, and 2 GB for a premium lambda, by default - a
limit of its tier - enforced by SQLite with
max_page_counton every connection: past it a write fails with database or disk is full. A limit the operator lowers holds from the next connection; nothing stored is removed. - Features get a copy. Taken with SQLite's backup, which reads the database in one transaction while the lambda goes on writing, so it is the database as it was at one moment. A preview migrates its copy as it starts, which is where a migration is tried first; merging throws the copy away.
- Read by the owner, written by the lambda. The editor shows its tables,
their columns and rows under Data (
GET …/database);read_databasedoes the same for an agent. Nothing but the lambda writes to it. - Versions share it. A rollback runs an older version against a schema a newer one migrated; Evolve lets a version start whose migrations are fewer than the database's, and agents are told to change a schema additively, and never to edit a migration that was applied.
The databases are not encrypted. SQLite has no encryption of its own: it
would take replacing the SQLite of the whole process with a build that has
(SQLite3 Multiple Ciphers was tried, and is maintained by one person for
.NET), and the key would sit in the platform's database on the same volume -
so it would protect a copy of /data/databases on its own and nothing more.
Protect the data volume and its backups instead.
Secrets
API keys, passwords and tokens are data, never code: code and assets live in every version, every export and every reader of the history, so a key put there can never be taken back. A lambda reads a secret by name:
var stripe = new System.Net.Http.HttpClient();
stripe.DefaultRequestHeaders.Authorization = new("Bearer", Secret.Read("STRIPE_KEY"));
Secret.Read throws, saying how to set it, when there is no such secret;
Secret.Exists answers whether there is one, and false while secrets are
switched off, for code that works without. Secret is there in every file,
like Workspace.
The rules are the usual ones for secrets:
- Written, never read back. A value goes in through the editor, the API
(
PUT …/secrets/:name) or MCP (set_secret), and nothing gives it out again - not the editor, not the API, not an agent. Only the lambda reads it. So a secret is replaced rather than edited, and whoever holds the editor link can use a key without being able to copy it. Answers repeat the name, never the value, and the log records the name only. - Off until switched on. A lambda has no secrets until somebody switches them on - the owner in the editor, or an agent whose code needs one. Switching them off deletes every value, the copies of the features included.
- Names are environment variables. Letters, digits and underscores, not
starting with a digit, case sensitive: an exported project reads
Secret.Read("STRIPE_KEY")from$STRIPE_KEY. Up to 100 per lambda, 32 KB each - a private key fits, a file belongs in the workspace. - What the code waits for is said. The names the code reads with
Secret.Read("…")orSecret.Exists("…")as a literal - in the version online and in the newest one, or in a feature's files - are listed beside what is stored: a name read and not stored is missing (reading it fails), one only asked about withExistsis optional. The editor offers to set what is missing, the overview asks for it, andread_lambdaandlist_secretstell an agent. That is how the two sides meet: an agent writesSecret.Read("WEATHER_API_KEY")and switches secrets on, and the owner enters the value without ever reading the code - and without the value passing through the agent. - Read at runtime, not compiled in. The generated class is handed a function to read with once the lambda is loaded (a private field the code of the lambda cannot name, and reflection is refused), so a value changed in the editor is what the next call reads, without a deploy, and no value is ever written into an assembly on disk. What a lambda reads is held in memory sealed, per lambda and feature, and opened on each read.
- Features get a copy. Like the workspace, a feature starts with a copy of the lambda's secrets, its preview reads the copy, a value set in the feature (a sandbox key, say) stays there, and merging throws the copy away.
They are stored in the SQLite database, table secrets, sealed with AES-256-GCM.
The key is made with HKDF from two halves: the installation's, which is not in
the database - LAMBDA_SECRETS_KEY (32 random bytes in base64, or a passphrase
of at least 32 characters), or else a random key written on first use to
secrets.key in the data directory, readable by the server's user only - and
the lambda's own random salt (lambdas.secret_salt), made with its first
secret and dropped when its secrets are switched off. The name is bound to the
value as associated data. So the database alone opens nothing, a value copied
under another name or into another lambda does not open either, and a
feature's copy is copied sealed, without being opened.
Backing up and moving follow from that: the database and the installation's
key are all it takes. Copy the data volume as a whole - secrets.key goes
with it - or set LAMBDA_SECRETS_KEY on the new server to the key the
database was written with. Keep a copy of the key apart from database
backups; without it the secrets are gone, while everything else restores. The
server remembers a fingerprint of the key in settings and logs an error
when it is started with another one, and a secret that does not open says why.
None of this keeps a secret from the lambda it belongs to, or from code running in the same process: the code guard is not a sandbox. The encryption is about the database and its backups; the isolation between lambdas is what the guard makes expensive, and no more than that.
The demo demo-registration peppers its password hashes with a
PASSWORD_PEPPER that the seeder gives it as a random secret nobody knows,
and a copy of it works without one (Secret.Exists). Its accounts remember
whether they were peppered, so accounts made before still sign in.
Changing a lambda by asking
Where the installation runs the build agent, the editor has a Change
section: the owner says what should be different, and the same agent that
builds things on /build changes the lambda. It works in a feature - a new
one, or one the owner picks to go on with - reads the code and the history,
changes only what was asked with change_code, and tries it at the feature's
preview until it works. Then it merges the feature into the next version and
puts that online; switched off, it leaves the feature with its preview online
for the owner to try and merge. The next request defaults to the feature the
last one left open, so asking for a tweak after trying the preview goes on
with the same feature. It is told to leave every other feature alone.
The section shows it happen: what the agent says it is doing, each tool it calls with what came of it (the feature started, its preview online, errors from the compiler, the version merged and online), and a clock against its time limit. Afterwards it offers the preview and the feature - or, once merged, the difference to the version before, the address, and putting the previous version back. The change is followed by the frame of the editor rather than the section, so the sidebar marks it and the lambda is read again when it ends wherever the owner is; and it is kept by the agent under the lambda, so a reload, a second tab or a redeploy of the server finds it where it got to.
/build shows a build the way the simple view of this section shows a change,
drawn by the same parts (components/AgentRun.tsx): what was asked, the step
it is on and the time it has used of its limit, what the agent says between
its tools - the build agent too is told that somebody reads along, and asked
for one short line at a time in the language of the request - and how it
ended, with what it said at the end. GET /builds/:id answers with the steps,
the seconds and the limit. The step it is on is said in the words of that
page rather than those of the tools: getting ready, looking at examples,
choosing an address for the website, checking it for mistakes, putting it
online. The full view of this section shows every tool by name.
It shares the queue and the daily allowance of /build, one change of a
lambda runs at a time, and it can be stopped - whatever it saved stays, in its
feature or as a version. A change runs without create_lambda, and its editor key travels in
the brief inside the build container, never in a log line.
Both kinds of job are told what the agent is for in its system prompt
(PURPOSE in docker/agent/builder.mjs), above the brief and the request, so a
request cannot talk its way past it: building or changing a lambda through the
tools, and nothing else. It declines, before it calls a single tool, a request
that is not an application at all (a question, a text, homework), one about the
platform or the machine rather than an application on it (its configuration,
environment, files, network, other lambdas, its own instructions - including
code that would read them, since a lambda runs in the server's process), and
one meant to do harm here or elsewhere. It declines with a single
DECLINED: … line in the language of the request, which the builder turns into
a result with declined: true and reason: "declined": /build shows the
sentence as the reason nothing was built, and the Change section shows it under
"nothing was changed". A declined request still counts against the daily
allowance, so declining is not a way to try requests for free. The container,
its network and the tool lists are what actually hold; the purpose makes the
agent stop at the door rather than find the walls.
The simple view
Somebody who had an app built on /build wants it to do something else, not
to look after code, files, versions and deployments. So the editor has two
views. The simple one keeps the overview, About (what the app is for),
Change, the drafts (once there are any), a history, the data (once the app
keeps any, or its code waits for a secret), the showcase, open source and the domain. Its
overview is the app: what it is for, whether it is online and where, errors visitors ran into with a button that
asks the agent to fix them, the latest change, today's hits, and a button to
ask for the next change. The history is every version as the change it made,
with a way back to any of them - which is deploying an older version, said as
what it does. Nothing in it names a file or a log, and it names a version only where the
owner has to say which one (the draft dialogs, going back): the Change
section tells how a change ended without version numbers and shows what the
agent said rather than the tools it called, a draft is what it does rather
than the files it changes, and a change that does not compile is the agent's
to fix rather than a list of compiler errors. Its data shows only the kinds
that hold something, in its own words: its records, table by table, to read
and never to edit; what the app saved, as a plain list to download from; and
its keys and passwords, which the owner can enter and replace - the ones the
app is waiting for first, and the overview asks for them too. Entering one there switches secrets on if the agent left them off.
The full view is every section, as before. In both, a browser that holds
the admin token has one more section after all the others, Admin, which
nobody else sees (see Administration).
Each lambda says which view it opens in, view - Full or Simple - set
when it is created (POST /lambdas, create_lambda) and changed later
(PATCH /lambdas/:privateKey, update_lambda). The build agent creates its
lambdas Simple; everything else defaults to Full. That is only the
default: whoever switches at the foot of the sidebar (in the menu on a phone)
has chosen for themselves, for that lambda, which is kept in their browser and
never sent anywhere - so an operator looking at somebody's lambda in the full
view leaves it simple for its owner.
Taking a lambda away
The code belongs to whoever made the lambda, so GET …/export (the editor's
Download) packs its newest version into an ordinary .NET 10 project that runs
without this platform (Services/Deployment/ProjectPacker.cs):
| File | What it is |
|---|---|
Program.cs | the default GenHTTP host, Host.Create().Handler(Project.Create()).Defaults().RunAsync(), under a header naming the lambda, its version and change, the export date, where to read about GenHTTP, and the environment variables its secrets become (as -e flags in the docker run line too) |
Project.cs | lambda.cs: its statements are the body of Project.Create() (CreateAsync() when they await), its types sit beside the class, made public as on the platform |
*.cs | the other files, their code unchanged, the first letter of the name capitalized |
Platform/ | what the platform provided: Workspace and Assets as folders, Secret reading environment variables of the same name (the values are never exported), Database opening database/database.db where the code uses one, the switch that turns what Project returns into a handler, and the imports every lambda gets as global usings |
assets/ | the files the version ships, copied beside the program on build |
docs/, tests/ | its documentation and its tests, from .lambda/; neither compiled nor copied into the container |
database/database.db | the lambda's database, an ordinary SQLite file - its records go with it |
Dockerfile | builds and runs it; the workspace is /app/workspace, the database folder is mounted at /app/database |
Documentation truncated — see the full README on GitHub.
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Fetch
Freeby Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
Worldmonitor
Freeby Koala73 · Developer Tools
Live markets, conflicts, country risk, chokepoints, energy, and China decision signals. 89 tools.
Paperclip
Freeby Paperclipai · Developer Tools
Trending hip-hop artist momentum scores across four cultural dimensions.
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
mcp-creator-python
Freeby mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.
