feat(wizard): editable spec in step 2 — name, description, JSON schema, secrets
The wizard's confirm step is no longer read-only. Users can refine what Claude parsed before committing to a build. Backend: - @bmm/types adds SpecEdit (tools[name,description,inputSchema] + requiredSecrets); CreateServerInput accepts an optional specEdit alongside previewId. - Servers create endpoint: when specEdit is provided, loads cached spec from Redis, index-merges the edits in (keeping LLM-generated implementations untouched), re-validates via GeneratorSpec, re-runs the banned-pattern scan, overwrites the Redis cache so the worker reads the user's version. Refuses with preview_expired/tool_count_mismatch/banned_pattern on safety failures. - New overwriteSpec() helper in preview-cache. Frontend: - Step 2 renders each tool as an editable card: name input, description textarea, JSON schema textarea with parse-on-keystroke validation (inline error if invalid). - Required secrets list is editable: keys via uppercase-snake-case input, +Add / remove buttons, secret values kept in sync when keys are renamed. - Reset-to-AI-suggestion button appears when edits are dirty. - Pre-submit validation: schema must parse, secret keys must match UPPER_SNAKE_CASE, required secret values must be provided. - Warning copy: 'Renaming parameters may require an Iterate after build — the existing impl references the original names.' Verified end-to-end via browser smoke test: edited description + renamed tool landed correctly in mcp_servers.tools_schema and in the live container at :4107. Implementation field preserved from the original cached spec.
This commit is contained in:
@@ -111,6 +111,23 @@ export type BuildEvent = z.infer<typeof BuildEvent>;
|
||||
|
||||
// ---- API request payloads ----
|
||||
|
||||
export const SpecEditTool = z.object({
|
||||
name: z
|
||||
.string()
|
||||
.min(1)
|
||||
.max(64)
|
||||
.regex(/^[a-z][a-z0-9_]*$/, 'snake_case identifier required'),
|
||||
description: z.string().min(1).max(2000),
|
||||
inputSchema: z.record(z.string(), ToolParam),
|
||||
});
|
||||
export type SpecEditTool = z.infer<typeof SpecEditTool>;
|
||||
|
||||
export const SpecEdit = z.object({
|
||||
tools: z.array(SpecEditTool).min(1).max(50),
|
||||
requiredSecrets: z.array(z.string().regex(/^[A-Z][A-Z0-9_]*$/)).max(30).default([]),
|
||||
});
|
||||
export type SpecEdit = z.infer<typeof SpecEdit>;
|
||||
|
||||
export const CreateServerInput = z.object({
|
||||
name: z.string().min(1).max(128),
|
||||
slug: z
|
||||
@@ -121,6 +138,7 @@ export const CreateServerInput = z.object({
|
||||
prompt: z.string().min(10).max(8000),
|
||||
secrets: z.record(z.string(), z.string()).default({}),
|
||||
previewId: z.string().min(1).max(64).optional(),
|
||||
specEdit: SpecEdit.optional(),
|
||||
});
|
||||
export type CreateServerInput = z.infer<typeof CreateServerInput>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user