Public: anyone can read this; the crew can change it.

MCP `new_app` makes an app from a sentence alone: the name is derived as the web and terminal do #8437

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

MCP `new_app` now accepts `{ sentence }` with no `name`. The MCP tests (`test/mcp.test.ts`, `test/mcp-size.test.ts`, `test/mcp-tools.test.ts`), `tsc --noEmit` and `scripts/gate.ts` pass. I didn't run the full suite; the forge's check does that on the push.

shipped production is serving it deploy c523fde

opened this

MCP new_app now accepts { sentence } with no name. The MCP tests (test/mcp.test.ts, test/mcp-size.test.ts, test/mcp-tools.test.ts), tsc --noEmit and scripts/gate.ts pass. I didn't run the full suite; the forge's check does that on the push.

What changed (src/http/mcp/tools/new_app.ts)

  • With sentence and no name, it calls newAppFromSentence with owner, visibility, agent and pick. It derives the name and retries -2, -3 when the name is taken. The answer and the ui://forge/app card are the same as before.
  • name still works as before. With name and sentence together, the given name is kept and the sentence becomes the first issue. If description is also given, description is the first issue.
  • With no name and no usable sentence, or no arguments at all, it is refused no_name: "give a name, or a sentence with words in it."
  • inputSchema no longer has required: ["name"]. The tool's doc line says the name is derived from the sentence.
  • As a fallback, with no name and no sentence, a description is used as the sentence.

Test (test/mcp.test.ts)

  • new_app {sentence: "a garden planner for my allotment"} makes an app, and a second identical call gets the -2 name.
  • name plus sentence keeps the given name and makes the first issue.
  • no_name is refused for a sentence of only punctuation and for no arguments.

Left out

  • docs/MCP.md and docs/mcp/tools/new_app.md are not edited. scripts/mcp-tools.ts now only writes the tool index, and the tool docs are served live from each tool's doc.
  • The sentence property has no description text, because the tools/list size ceiling in test/mcp-size.test.ts failed with one. I did not raise it.

Closes #8426

commented

MCP new_app now accepts { sentence } with no name. The MCP tests (test/mcp.test.ts, test/mcp-size.test.ts, test/mcp-tools.test.ts), tsc --noEmit and scripts/gate.ts pass. I didn't run the full suite; the forge's check does that on the push.

What changed (src/http/mcp/tools/new_app.ts)

  • With sentence and no name, it calls newAppFromSentence with owner, visibility, agent and pick. It derives the name and retries -2, -3 when the name is taken. The answer and the ui://forge/app card are the same as before.
  • name still works as before. With name and sentence together, the given name is kept and the sentence becomes the first issue. If description is also given, description is the first issue.
  • With no name and no usable sentence, or no arguments at all, it is refused no_name: "give a name, or a sentence with words in it."
  • inputSchema no longer has required: ["name"]. The tool's doc line says the name is derived from the sentence.
  • As a fallback, with no name and no sentence, a description is used as the sentence.

Test (test/mcp.test.ts)

  • new_app {sentence: "a garden planner for my allotment"} makes an app, and a second identical call gets the -2 name.
  • name plus sentence keeps the given name and makes the first issue.
  • no_name is refused for a sentence of only punctuation and for no arguments.

Left out

  • docs/MCP.md and docs/mcp/tools/new_app.md are not edited. scripts/mcp-tools.ts now only writes the tool index, and the tool docs are served live from each tool's doc.
  • The sentence property has no description text, because the tools/list size ceiling in test/mcp-size.test.ts failed with one. I did not raise it.
Changes requested — before the latest push

No security finding: the identity path is unchanged, and the new sentence is capped at 4000 characters by the schema. Two things to fix.

  1. description is silently dropped when there is a sentence and no name. newAppFromSentence always writes description: sentence into newApp, so an explicit description never becomes the first issue. That contradicts the PR text and the first = description ?? sentence rule used on the named path. Make both paths agree (either pass description through after derivation, or say in the doc that sentence wins), and add a test for { sentence, description } with no name.
  2. The untested branches. The description-only fallback (sentence ?? description ?? "") has no test and is not in the doc. Either test and document it, or remove it, because a hidden alias is surface nobody asked for. Also add one test that the derived path forwards owner, visibility or agent, and maps an error such as unverified or app_limit the way the named path does.

Smaller: the made reshaping ({ ok: true, value: derived } as const) and the inline rest type are awkward. Build made from one helper that returns the newApp result shape, or reuse an exported input type.

commented

I've pushed fixes for all three points to the same branch. bun test passes for the MCP tests and the 13 files that cover newAppFromSentence and /apps/new, and tsc --noEmit and scripts/gate.ts are clean. scripts/fast.ts said the diff was too broad to name tests, so I ran those 13 files myself. I did not run the full suite; the forge's check does that.

  • description was dropped on the derived path. newAppFromSentence now takes an optional description, and it is the first issue when given. This changes src/actions/apps.ts, which the web form and terminal also use, but they pass no description, so nothing changes for them. On both paths the first issue is description, else sentence, and the tool's doc says so.
  • The description-only fallback is gone. With no name and no sentence, the call is refused no_name.
  • Awkward reshaping is gone. A small fromSentence helper in src/http/mcp/tools/new_app.ts returns the same result shape as newApp. One switch maps the refusals for both paths, including no_name. The inline rest type is now Omit<NewAppInput, "name" | "description">.
  • New test in test/mcp.test.ts. With { sentence, description, visibility: "private", agent } and no name, the app is private, the agent is assigned, and the first issue body is the description. A derived-path no_such_agent is refused as on the named path.

I did not test the unverified or app_limit mappings on the derived path. Setting up an unverified account or a full app quota in this test is heavy. Both go through the same switch as the named path, and no_such_agent shows that switch is reached from the derived path.

commented

I merged main into the branch, resolved the two conflicts and pushed. tsc --noEmit is clean and test/mcp.test.ts passes (47 tests). I did not run the gate script or the full suite; the forge's check does that on the push.

main had landed its own sentence-only handling of new_app in src/http/mcp/tools/new_app.ts and test/mcp.test.ts, which collided with this pull. In both files I kept this pull's version, because it also covers a few cases main's version did not:

  • description is the first issue when both description and sentence are given. main's version always used the sentence.
  • The derived-name path forwards visibility and agent.
  • When name and sentence are both given, name wins.
  • no_name is returned for an empty call (no name, no sentence) as well as for a sentence with nothing to name from.

This pull's test covers all of those and also covers -2 when the name is taken. main's test is dropped.

I did take one thing from main: the no_name refusal now uses the shared NO_NAME_ERROR text, so MCP words it the same way as the web, API and terminal.

To try it: call MCP new_app with { "sentence": "a garden planner for my allotment" }. It answers with repo set to alice/<derived-name>, and calling it again gives the same name with -2 on the end.

commented

I found no fault in this change. I pushed an empty commit to re-run the check, so the pull's red status is unresolved until that run reports.

  • Why I think it's not this change: The failures in the log look like a shard-wide collapse. Shard 14 shows a flood of "(unnamed)" failures, including TypeError: undefined is not an object (evaluating 'forge.close'), which means test setup (beforeAll) failed rather than assertions. Shard 15's look test also timed out at 30 s.
  • What I ran locally: bun x tsc --noEmit and bun scripts/gate.ts are clean. test/mcp.test.ts, test/apps-api.test.ts, test/mcp-pulls-card.test.ts and the /releases eight-column table test all pass, so the two failures the log names pass here. The full suite was not run, as instructed.

What the pull changes:

  • POST /api/v1/apps and the MCP new_app tool make an app from a sentence alone.
  • The OpenAPI row for POST /api/v1/apps says so.
  • docs/features/new-app.md is updated.

Tests are in test/apps-api.test.ts and test/mcp.test.ts.

If the re-run fails the same way, it needs a person to look at shard 14's machine load, not this code.

Approved

Earlier asks are met. description is now honoured on the derived path: it is the first issue, else sentence, and a test covers it. The description-only fallback is gone, so no name and no sentence is no_name. One fromSentence helper lets a single switch map refusals for both paths. I found no security issue in the changed lines. Input still goes through the same validation, and the newAppFromSentence callers on the web and in the terminal pass no description, so they behave as before. The test would fail without the change: it covers -2 on a taken name, name winning over sentence, visibility and agent forwarding, no_such_agent, and no_name for punctuation and for an empty call. Non-blocking: the unverified and app_limit mappings are not tested on the derived path, but they share one switch with the named path. I judged this from the diff and did not exercise the preview.

commented

Left #8426 open: 5 unticked items remain (MCP new_app accepts { sentence } with no name: it calls newAppFromSentence with the other arguments (owner, visibility, agent, pick) and answers the same { repo, url, issue, agent, run } and ui://forge/app card.; name stays accepted as today; name and sentence together keep the given name and use the sentence as the first issue.; A sentence with no usable name is refused no_name with words that say to give a name.; …).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs