mockpit 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +232 -2
- package/bin/demoData.js +132 -0
- package/bin/initDesign.js +846 -0
- package/bin/mockpit.js +1830 -0
- package/bin/serveUrl.d.ts +1 -0
- package/bin/serveUrl.js +7 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +230 -0
- package/dist/server/apiViews.d.ts +329 -0
- package/dist/server/apiViews.js +135 -0
- package/dist/server/app.d.ts +53 -0
- package/dist/server/app.js +3112 -0
- package/dist/server/base64.d.ts +1 -0
- package/dist/server/base64.js +16 -0
- package/dist/server/cdn.d.ts +6 -0
- package/dist/server/cdn.js +33 -0
- package/dist/server/codeRunner.d.ts +5 -0
- package/dist/server/codeRunner.js +89 -0
- package/dist/server/codeWorker.d.ts +1 -0
- package/dist/server/codeWorker.js +196 -0
- package/dist/server/designGuide.d.ts +16 -0
- package/dist/server/designGuide.js +311 -0
- package/dist/server/events.d.ts +53 -0
- package/dist/server/events.js +14 -0
- package/dist/server/feedbackBatch.d.ts +43 -0
- package/dist/server/feedbackBatch.js +98 -0
- package/dist/server/icons.d.ts +50 -0
- package/dist/server/icons.js +225 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +64 -0
- package/dist/server/kits.d.ts +36 -0
- package/dist/server/kits.js +279 -0
- package/dist/server/knobs.d.ts +15 -0
- package/dist/server/knobs.js +333 -0
- package/dist/server/mcpHttp.d.ts +53 -0
- package/dist/server/mcpHttp.js +143 -0
- package/dist/server/mcpSpec.d.ts +191 -0
- package/dist/server/mcpSpec.js +402 -0
- package/dist/server/parts.d.ts +31 -0
- package/dist/server/parts.js +338 -0
- package/dist/server/postMarkdown.d.ts +10 -0
- package/dist/server/postMarkdown.js +271 -0
- package/dist/server/postSurfaces.d.ts +40 -0
- package/dist/server/postSurfaces.js +407 -0
- package/dist/server/public.d.ts +5 -0
- package/dist/server/public.js +5 -0
- package/dist/server/push.d.ts +45 -0
- package/dist/server/push.js +234 -0
- package/dist/server/richRender.d.ts +15 -0
- package/dist/server/richRender.js +392 -0
- package/dist/server/run.d.ts +82 -0
- package/dist/server/run.js +290 -0
- package/dist/server/runApi.d.ts +6 -0
- package/dist/server/runApi.js +82 -0
- package/dist/server/slots.d.ts +14 -0
- package/dist/server/slots.js +71 -0
- package/dist/server/sqlStore.d.ts +119 -0
- package/dist/server/sqlStore.js +954 -0
- package/dist/server/sqliteStorage.d.ts +2 -0
- package/dist/server/sqliteStorage.js +90 -0
- package/dist/server/surfacePage.d.ts +48 -0
- package/dist/server/surfacePage.js +1099 -0
- package/dist/server/themes.d.ts +48 -0
- package/dist/server/themes.js +305 -0
- package/dist/server/types.d.ts +599 -0
- package/dist/server/types.js +299 -0
- package/dist/workers/public.d.ts +1 -0
- package/dist/workers/public.js +3 -0
- package/extensions/mockpit.js +703 -0
- package/guide/AGENT_SETUP.md +23 -0
- package/guide/topics/asks.md +87 -0
- package/guide/topics/feedback.md +127 -0
- package/guide/topics/html.md +175 -0
- package/guide/topics/http.md +76 -0
- package/guide/topics/knobs.md +72 -0
- package/guide/topics/scripts.md +129 -0
- package/guide/topics/surfaces.md +100 -0
- package/package.json +165 -4
- package/skills/launch-video/SKILL.md +159 -0
- package/skills/mockpit/SKILL.md +40 -0
- package/viewer/dist/index.html +1922 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import * as z from "zod/v4";
|
|
2
|
+
export declare const MCP_SERVER_INFO: {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const MCP_INSTRUCTIONS: string;
|
|
7
|
+
export declare const mcpSurfaceSchema: z.ZodObject<{
|
|
8
|
+
id: z.ZodOptional<z.ZodString>;
|
|
9
|
+
kind: z.ZodOptional<z.ZodEnum<{
|
|
10
|
+
html: "html";
|
|
11
|
+
diff: "diff";
|
|
12
|
+
image: "image";
|
|
13
|
+
markdown: "markdown";
|
|
14
|
+
terminal: "terminal";
|
|
15
|
+
mermaid: "mermaid";
|
|
16
|
+
json: "json";
|
|
17
|
+
code: "code";
|
|
18
|
+
}>>;
|
|
19
|
+
html: z.ZodOptional<z.ZodString>;
|
|
20
|
+
kits: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
21
|
+
markdown: z.ZodOptional<z.ZodString>;
|
|
22
|
+
mermaid: z.ZodOptional<z.ZodString>;
|
|
23
|
+
patch: z.ZodOptional<z.ZodString>;
|
|
24
|
+
files: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
25
|
+
filename: z.ZodString;
|
|
26
|
+
before: z.ZodString;
|
|
27
|
+
after: z.ZodString;
|
|
28
|
+
language: z.ZodOptional<z.ZodString>;
|
|
29
|
+
}, z.core.$strip>>>;
|
|
30
|
+
layout: z.ZodOptional<z.ZodEnum<{
|
|
31
|
+
unified: "unified";
|
|
32
|
+
split: "split";
|
|
33
|
+
}>>;
|
|
34
|
+
assetId: z.ZodOptional<z.ZodString>;
|
|
35
|
+
alt: z.ZodOptional<z.ZodString>;
|
|
36
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
37
|
+
title: z.ZodOptional<z.ZodString>;
|
|
38
|
+
text: z.ZodOptional<z.ZodString>;
|
|
39
|
+
cols: z.ZodOptional<z.ZodNumber>;
|
|
40
|
+
data: z.ZodOptional<z.ZodUnknown>;
|
|
41
|
+
code: z.ZodOptional<z.ZodString>;
|
|
42
|
+
language: z.ZodOptional<z.ZodString>;
|
|
43
|
+
lineStart: z.ZodOptional<z.ZodNumber>;
|
|
44
|
+
}, z.core.$strip>;
|
|
45
|
+
type Param = {
|
|
46
|
+
t: "string" | "number" | "boolean" | "integer";
|
|
47
|
+
d?: string;
|
|
48
|
+
req?: boolean;
|
|
49
|
+
} | {
|
|
50
|
+
t: "enum";
|
|
51
|
+
values: readonly [string, ...string[]];
|
|
52
|
+
d?: string;
|
|
53
|
+
req?: boolean;
|
|
54
|
+
} | {
|
|
55
|
+
t: "surfaces" | "asks" | "object";
|
|
56
|
+
d?: string;
|
|
57
|
+
req?: boolean;
|
|
58
|
+
};
|
|
59
|
+
interface ToolDef {
|
|
60
|
+
name: string;
|
|
61
|
+
description: string;
|
|
62
|
+
params: Record<string, Param>;
|
|
63
|
+
httpOnly?: readonly string[];
|
|
64
|
+
stdioExtra?: Record<string, Param>;
|
|
65
|
+
}
|
|
66
|
+
export declare const MCP_TOOL_DEFS: ToolDef[];
|
|
67
|
+
export declare const RUN_TOOL_DEF: ToolDef;
|
|
68
|
+
type Json = Record<string, unknown>;
|
|
69
|
+
export declare const HTTP_MCP_TOOLS: ({
|
|
70
|
+
_meta: {
|
|
71
|
+
"anthropic/alwaysLoad": boolean;
|
|
72
|
+
};
|
|
73
|
+
outputSchema?: Json | undefined;
|
|
74
|
+
name: string;
|
|
75
|
+
description: string;
|
|
76
|
+
inputSchema: Json;
|
|
77
|
+
} | {
|
|
78
|
+
_meta?: undefined;
|
|
79
|
+
outputSchema?: Json | undefined;
|
|
80
|
+
name: string;
|
|
81
|
+
description: string;
|
|
82
|
+
inputSchema: Json;
|
|
83
|
+
})[];
|
|
84
|
+
export declare const STDIO_MCP_CATALOG: ({
|
|
85
|
+
_meta: {
|
|
86
|
+
"anthropic/alwaysLoad": boolean;
|
|
87
|
+
};
|
|
88
|
+
outputSchema?: Json | undefined;
|
|
89
|
+
name: string;
|
|
90
|
+
description: string;
|
|
91
|
+
inputSchema: Json;
|
|
92
|
+
} | {
|
|
93
|
+
_meta?: undefined;
|
|
94
|
+
outputSchema?: Json | undefined;
|
|
95
|
+
name: string;
|
|
96
|
+
description: string;
|
|
97
|
+
inputSchema: Json;
|
|
98
|
+
})[];
|
|
99
|
+
export declare const HTTP_RUN_TOOLS: ({
|
|
100
|
+
_meta: {
|
|
101
|
+
"anthropic/alwaysLoad": boolean;
|
|
102
|
+
};
|
|
103
|
+
outputSchema?: Json | undefined;
|
|
104
|
+
name: string;
|
|
105
|
+
description: string;
|
|
106
|
+
inputSchema: Json;
|
|
107
|
+
} | {
|
|
108
|
+
_meta?: undefined;
|
|
109
|
+
outputSchema?: Json | undefined;
|
|
110
|
+
name: string;
|
|
111
|
+
description: string;
|
|
112
|
+
inputSchema: Json;
|
|
113
|
+
})[];
|
|
114
|
+
export declare const STDIO_RUN_CATALOG: ({
|
|
115
|
+
_meta: {
|
|
116
|
+
"anthropic/alwaysLoad": boolean;
|
|
117
|
+
};
|
|
118
|
+
outputSchema?: Json | undefined;
|
|
119
|
+
name: string;
|
|
120
|
+
description: string;
|
|
121
|
+
inputSchema: Json;
|
|
122
|
+
} | {
|
|
123
|
+
_meta?: undefined;
|
|
124
|
+
outputSchema?: Json | undefined;
|
|
125
|
+
name: string;
|
|
126
|
+
description: string;
|
|
127
|
+
inputSchema: Json;
|
|
128
|
+
})[];
|
|
129
|
+
export declare const STDIO_MCP_TOOLS: ({
|
|
130
|
+
_meta: {
|
|
131
|
+
"anthropic/alwaysLoad": boolean;
|
|
132
|
+
};
|
|
133
|
+
outputSchema?: z.ZodObject<Readonly<{
|
|
134
|
+
[k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
135
|
+
}>, z.core.$loose> | undefined;
|
|
136
|
+
name: string;
|
|
137
|
+
description: string;
|
|
138
|
+
inputSchema: Readonly<{
|
|
139
|
+
[k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
140
|
+
}>;
|
|
141
|
+
} | {
|
|
142
|
+
_meta?: undefined;
|
|
143
|
+
outputSchema?: z.ZodObject<Readonly<{
|
|
144
|
+
[k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
145
|
+
}>, z.core.$loose> | undefined;
|
|
146
|
+
name: string;
|
|
147
|
+
description: string;
|
|
148
|
+
inputSchema: Readonly<{
|
|
149
|
+
[k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
150
|
+
}>;
|
|
151
|
+
})[];
|
|
152
|
+
export declare const STDIO_RUN_TOOLS: ({
|
|
153
|
+
_meta: {
|
|
154
|
+
"anthropic/alwaysLoad": boolean;
|
|
155
|
+
};
|
|
156
|
+
outputSchema?: z.ZodObject<Readonly<{
|
|
157
|
+
[k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
158
|
+
}>, z.core.$loose> | undefined;
|
|
159
|
+
name: string;
|
|
160
|
+
description: string;
|
|
161
|
+
inputSchema: Readonly<{
|
|
162
|
+
[k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
163
|
+
}>;
|
|
164
|
+
} | {
|
|
165
|
+
_meta?: undefined;
|
|
166
|
+
outputSchema?: z.ZodObject<Readonly<{
|
|
167
|
+
[k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
168
|
+
}>, z.core.$loose> | undefined;
|
|
169
|
+
name: string;
|
|
170
|
+
description: string;
|
|
171
|
+
inputSchema: Readonly<{
|
|
172
|
+
[k: string]: z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
173
|
+
}>;
|
|
174
|
+
})[];
|
|
175
|
+
export declare const MCP_TOOL_NAMES: string[];
|
|
176
|
+
export declare function toolResult(name: string, value: unknown): {
|
|
177
|
+
structuredContent?: Record<string, unknown> | undefined;
|
|
178
|
+
content: {
|
|
179
|
+
type: "text";
|
|
180
|
+
text: string;
|
|
181
|
+
}[];
|
|
182
|
+
};
|
|
183
|
+
export declare function runToolResult(envelope: unknown): {
|
|
184
|
+
isError?: boolean | undefined;
|
|
185
|
+
structuredContent?: Record<string, unknown> | undefined;
|
|
186
|
+
content: {
|
|
187
|
+
type: "text";
|
|
188
|
+
text: string;
|
|
189
|
+
}[];
|
|
190
|
+
};
|
|
191
|
+
export {};
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import * as z from "zod/v4";
|
|
2
|
+
import { GUIDE_TOPICS } from "./designGuide.js";
|
|
3
|
+
import { KIT_IDS } from "./kits.js";
|
|
4
|
+
import { RUN_DESCRIPTION } from "./runApi.js";
|
|
5
|
+
import { SURFACE_KINDS } from "./types.js";
|
|
6
|
+
export const MCP_SERVER_INFO = { name: "mockpit", version: "0.2.0" };
|
|
7
|
+
// The `kind` enum both MCP transports advertise — derived from the one canonical
|
|
8
|
+
// list (types.ts) so the MCP tier can never fall behind what REST/CLI accept.
|
|
9
|
+
const SURFACE_KIND_ENUM = [...SURFACE_KINDS];
|
|
10
|
+
export const MCP_INSTRUCTIONS = "Mockpit shows design work to the user: project > mock > state > variant > version. publish " +
|
|
11
|
+
"each variant (name states in the user's words; mark parts with data-part); publish again to " +
|
|
12
|
+
"revise. A choice is several variants plus one ask that binds them. One render plus a " +
|
|
13
|
+
"control: declare a knob. Say where to look, then end your turn; nothing waits. When the " +
|
|
14
|
+
"user says they answered, call feedback. Writes return feedback too; it is delivered once. " +
|
|
15
|
+
"Fetch guide before html.";
|
|
16
|
+
const field = {
|
|
17
|
+
mock: "Mock slug or id",
|
|
18
|
+
state: "State label; omit if single-state",
|
|
19
|
+
variant: 'Variant label; default "default"',
|
|
20
|
+
project: "Default: the session's project",
|
|
21
|
+
title: "Mock title",
|
|
22
|
+
session: "Session id from an earlier publish",
|
|
23
|
+
sessionTitle: "Task name; used only when creating the session",
|
|
24
|
+
agent: "Agent name for a new session",
|
|
25
|
+
html: "HTML body fragment",
|
|
26
|
+
knobs: 'Global knobs (tunekit usePane shape) keyed by path: [default,min,max,step], boolean, {type:"select",options}, "#hex"',
|
|
27
|
+
variantKnobs: "Knobs this variant alone has",
|
|
28
|
+
from: "Branch from this version",
|
|
29
|
+
prompt: "What prompted this version; usually omit",
|
|
30
|
+
kits: `Opt-in bundles: ${KIT_IDS.join("|")}`,
|
|
31
|
+
};
|
|
32
|
+
const diffFileSchema = z.object({
|
|
33
|
+
filename: z.string(),
|
|
34
|
+
before: z.string(),
|
|
35
|
+
after: z.string(),
|
|
36
|
+
language: z.string().optional(),
|
|
37
|
+
});
|
|
38
|
+
// Named so each tool carries it once under $defs and codemode harnesses
|
|
39
|
+
// generate one `Surface` type instead of four anonymous copies.
|
|
40
|
+
export const mcpSurfaceSchema = z
|
|
41
|
+
.object({
|
|
42
|
+
id: z
|
|
43
|
+
.string()
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("Existing surface id: alone keeps it, with kind replaces it"),
|
|
46
|
+
kind: z.enum(SURFACE_KIND_ENUM).optional(),
|
|
47
|
+
html: z.string().optional().describe(field.html),
|
|
48
|
+
kits: z.array(z.string()).optional().describe(field.kits),
|
|
49
|
+
markdown: z.string().optional().describe("Prose; raw HTML is escaped"),
|
|
50
|
+
mermaid: z.string().optional().describe("Diagram source; do not set colors"),
|
|
51
|
+
patch: z.string().optional().describe("Preferred over files"),
|
|
52
|
+
files: z.array(diffFileSchema).optional(),
|
|
53
|
+
layout: z.enum(["unified", "split"]).optional(),
|
|
54
|
+
assetId: z.string().optional().describe("Id from upload"),
|
|
55
|
+
alt: z.string().optional(),
|
|
56
|
+
caption: z.string().optional(),
|
|
57
|
+
title: z.string().optional(),
|
|
58
|
+
text: z.string().optional().describe("ANSI styles render"),
|
|
59
|
+
cols: z.number().optional(),
|
|
60
|
+
data: z.unknown().optional(),
|
|
61
|
+
code: z.string().optional(),
|
|
62
|
+
language: z.string().optional(),
|
|
63
|
+
lineStart: z.number().int().min(1).optional().describe("1-based first line"),
|
|
64
|
+
})
|
|
65
|
+
.meta({
|
|
66
|
+
id: "Surface",
|
|
67
|
+
description: "Set kind and its field: html, markdown, mermaid, patch|files (diff), assetId (image), text (terminal), data (json), code",
|
|
68
|
+
});
|
|
69
|
+
const askSchema = z.object({
|
|
70
|
+
id: z.string().optional().describe("Stable id; reusing one replaces that ask"),
|
|
71
|
+
text: z.string().describe("The question, one sentence"),
|
|
72
|
+
scope: z.enum(["mock", "state", "part"]).optional(),
|
|
73
|
+
state: z.string().optional(),
|
|
74
|
+
part: z.string().optional().describe("data-part name, for scope part"),
|
|
75
|
+
multi: z.boolean().optional(),
|
|
76
|
+
options: z.array(z.object({
|
|
77
|
+
id: z.string().optional(),
|
|
78
|
+
label: z.string(),
|
|
79
|
+
variant: z.string().optional().describe("Bind to a published variant"),
|
|
80
|
+
set: z.record(z.string(), z.unknown()).optional().describe("Or to knob values {path: value}"),
|
|
81
|
+
})),
|
|
82
|
+
});
|
|
83
|
+
function zodParam(p) {
|
|
84
|
+
let s;
|
|
85
|
+
switch (p.t) {
|
|
86
|
+
case "enum":
|
|
87
|
+
s = z.enum(p.values);
|
|
88
|
+
break;
|
|
89
|
+
case "surfaces":
|
|
90
|
+
s = z.array(mcpSurfaceSchema);
|
|
91
|
+
break;
|
|
92
|
+
case "asks":
|
|
93
|
+
s = z.array(askSchema);
|
|
94
|
+
break;
|
|
95
|
+
case "object":
|
|
96
|
+
s = z.record(z.string(), z.unknown());
|
|
97
|
+
break;
|
|
98
|
+
case "integer":
|
|
99
|
+
s = z.number().int();
|
|
100
|
+
break;
|
|
101
|
+
default:
|
|
102
|
+
s = p.t === "string" ? z.string() : p.t === "number" ? z.number() : z.boolean();
|
|
103
|
+
}
|
|
104
|
+
if (p.d)
|
|
105
|
+
s = s.describe(p.d);
|
|
106
|
+
return p.req ? s : s.optional();
|
|
107
|
+
}
|
|
108
|
+
const P = {
|
|
109
|
+
mock: { t: "string", d: field.mock, req: true },
|
|
110
|
+
state: { t: "string", d: field.state },
|
|
111
|
+
variant: { t: "string", d: field.variant },
|
|
112
|
+
project: { t: "string", d: field.project },
|
|
113
|
+
session: { t: "string", d: field.session },
|
|
114
|
+
};
|
|
115
|
+
const VARIANT_REF = { mock: P.mock, state: P.state, variant: P.variant, project: P.project };
|
|
116
|
+
export const MCP_TOOL_DEFS = [
|
|
117
|
+
{
|
|
118
|
+
name: "publish",
|
|
119
|
+
description: "Create or version one variant of one state of a mock. One of html, surfaces (the full ordered list) or parts (splices the latest version). Returns parts per state, nudges, feedback.",
|
|
120
|
+
params: {
|
|
121
|
+
...VARIANT_REF,
|
|
122
|
+
title: { t: "string", d: field.title },
|
|
123
|
+
kind: { t: "enum", values: ["component", "page"] },
|
|
124
|
+
html: { t: "string", d: field.html },
|
|
125
|
+
surfaces: {
|
|
126
|
+
t: "surfaces",
|
|
127
|
+
d: "Full ordered list: {id} keeps a surface, a missing id removes it",
|
|
128
|
+
},
|
|
129
|
+
parts: { t: "object", d: '{"name"|"name#key": outer html}; splices marked parts' },
|
|
130
|
+
knobs: { t: "object", d: field.knobs },
|
|
131
|
+
variantKnobs: { t: "object", d: field.variantKnobs },
|
|
132
|
+
from: { t: "integer", d: field.from },
|
|
133
|
+
prompt: { t: "string", d: field.prompt },
|
|
134
|
+
session: P.session,
|
|
135
|
+
sessionTitle: { t: "string", d: field.sessionTitle },
|
|
136
|
+
agent: { t: "string", d: field.agent },
|
|
137
|
+
},
|
|
138
|
+
httpOnly: ["session", "agent"],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: "ask",
|
|
142
|
+
description: "Ask structured questions on a mock. A choice is several variants plus one ask that binds them; options may also set knob values. Reusing an ask id replaces it.",
|
|
143
|
+
params: {
|
|
144
|
+
mock: P.mock,
|
|
145
|
+
project: P.project,
|
|
146
|
+
asks: { t: "asks", d: "Questions with options", req: true },
|
|
147
|
+
session: P.session,
|
|
148
|
+
},
|
|
149
|
+
httpOnly: ["session"],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "read",
|
|
153
|
+
description: "Without mock: every mock (slug, states, variants, open asks) plus pending. With mock: its states, variants, asks with answers, parts, knobs and last tuned values, plus pending. Never consumes feedback.",
|
|
154
|
+
params: {
|
|
155
|
+
mock: { t: "string", d: field.mock },
|
|
156
|
+
project: P.project,
|
|
157
|
+
body: { t: "boolean", d: "Include each variant's surfaces" },
|
|
158
|
+
history: { t: "boolean", d: "Include version rows" },
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: "feedback",
|
|
163
|
+
description: "Returns at once: what the user sent since you last heard (one batch per mock: reply answers, mix, tuned knobs, part comments, comments, accepted/archived), delivered once, plus pending (viewer open, draft progress).",
|
|
164
|
+
params: {
|
|
165
|
+
project: P.project,
|
|
166
|
+
session: { t: "string", d: "Session id returned by publish", req: true },
|
|
167
|
+
},
|
|
168
|
+
httpOnly: ["session"],
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: "say",
|
|
172
|
+
description: "Post a short plain-text message in a mock's thread. Returns feedback.",
|
|
173
|
+
params: {
|
|
174
|
+
...VARIANT_REF,
|
|
175
|
+
message: { t: "string", d: "Plain text", req: true },
|
|
176
|
+
session: P.session,
|
|
177
|
+
},
|
|
178
|
+
httpOnly: ["session"],
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: "export",
|
|
182
|
+
description: "The accepted (or current) html per state, version history, screenshot URL, knobs and the last reply's tuned values.",
|
|
183
|
+
params: VARIANT_REF,
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: "upload",
|
|
187
|
+
description: "Upload bytes and return id and URL. Reference id as an image surface's assetId.",
|
|
188
|
+
params: {
|
|
189
|
+
data: { t: "string", d: "Base64 file bytes" },
|
|
190
|
+
contentType: { t: "string", d: "MIME type" },
|
|
191
|
+
filename: { t: "string", d: "Original download filename" },
|
|
192
|
+
kind: { t: "enum", values: ["image", "file"] },
|
|
193
|
+
session: P.session,
|
|
194
|
+
},
|
|
195
|
+
httpOnly: ["session"],
|
|
196
|
+
stdioExtra: { path: { t: "string", d: "Local file path; use instead of data" } },
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: "guide",
|
|
200
|
+
description: "Fetch the brief: the loop, parts, asks and knobs, feedback, the html contract, and this " +
|
|
201
|
+
"project's palette, kit and icons. Read it before the first publish. Pass topic for one " +
|
|
202
|
+
`reference section (${GUIDE_TOPICS.join(", ")}).`,
|
|
203
|
+
params: {
|
|
204
|
+
project: P.project,
|
|
205
|
+
topic: { t: "enum", values: GUIDE_TOPICS, d: "One reference section instead of the brief" },
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
];
|
|
209
|
+
// The codemode catalog (`/mcp?mode=code`, MOCKPIT_MCP_MODE=code): one tool whose
|
|
210
|
+
// description carries the whole script API.
|
|
211
|
+
export const RUN_TOOL_DEF = {
|
|
212
|
+
name: "run",
|
|
213
|
+
description: RUN_DESCRIPTION,
|
|
214
|
+
params: {
|
|
215
|
+
code: { t: "string", d: "JavaScript: the body of an async function", req: true },
|
|
216
|
+
session: { t: "string", d: "Session id from an earlier run" },
|
|
217
|
+
project: P.project,
|
|
218
|
+
agent: { t: "string", d: field.agent },
|
|
219
|
+
},
|
|
220
|
+
httpOnly: ["session", "agent"],
|
|
221
|
+
stdioExtra: {
|
|
222
|
+
code: { t: "string", d: "JavaScript: the body of an async function" },
|
|
223
|
+
path: { t: "string", d: "Local .js file to run instead of code" },
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
// Output shapes (MCP 2025-06-18 outputSchema) of what the shared flows return
|
|
227
|
+
// (apiViews.ts, feedbackBatch.ts, app.ts). Loose objects: they name the fields
|
|
228
|
+
// an agent's code reads, not every field, and must never reject the rest.
|
|
229
|
+
const o = z.looseObject;
|
|
230
|
+
const str = z.string();
|
|
231
|
+
const num = z.number();
|
|
232
|
+
const nullStr = z.string().nullable();
|
|
233
|
+
const strs = z.array(z.string());
|
|
234
|
+
const bag = z.record(z.string(), z.unknown());
|
|
235
|
+
const variantRef = o({ state: nullStr, variant: str });
|
|
236
|
+
const partsByState = z.array(o({ state: nullStr, parts: z.array(o({ name: str })) }));
|
|
237
|
+
const feedbackSchema = o({
|
|
238
|
+
mock: nullStr,
|
|
239
|
+
reply: o({
|
|
240
|
+
asks: z.array(o({
|
|
241
|
+
ask: str,
|
|
242
|
+
chosen: z.array(o({ label: str, other: z.literal(true).optional() })),
|
|
243
|
+
note: str.optional(),
|
|
244
|
+
})),
|
|
245
|
+
mix: bag,
|
|
246
|
+
tuned: bag,
|
|
247
|
+
comments: z.array(o({ part: nullStr, text: str })),
|
|
248
|
+
})
|
|
249
|
+
.nullable()
|
|
250
|
+
.describe("The user's Send; null if only comments"),
|
|
251
|
+
comments: z.array(o({ seq: num, text: str })),
|
|
252
|
+
accepted: z.array(variantRef),
|
|
253
|
+
archived: z.array(variantRef),
|
|
254
|
+
}).meta({ id: "Feedback" });
|
|
255
|
+
// Typed once, on feedback: repeating it on every write would cost more than
|
|
256
|
+
// the rest of the write's output schema.
|
|
257
|
+
const feedbackRide = z.array(bag).describe("Feedback batches as the feedback tool returns");
|
|
258
|
+
const pendingSchema = o({
|
|
259
|
+
mock: str,
|
|
260
|
+
viewerOpen: z.boolean(),
|
|
261
|
+
draft: o({ answered: num, of: num, comments: num, touchedAt: str }).nullable(),
|
|
262
|
+
}).meta({ id: "Pending" });
|
|
263
|
+
const writeOutput = {
|
|
264
|
+
post: o({ state: nullStr, variant: str, version: num }),
|
|
265
|
+
sessionId: str,
|
|
266
|
+
url: str.describe("Viewer link for the user"),
|
|
267
|
+
parts: partsByState,
|
|
268
|
+
partChanges: o({ vanished: strs, renamed: z.array(o({ from: str, to: str })) }).optional(),
|
|
269
|
+
nudges: strs.optional(),
|
|
270
|
+
suggestedAsk: bag.optional().describe("Send with ask when a nudge names it"),
|
|
271
|
+
feedback: feedbackRide,
|
|
272
|
+
};
|
|
273
|
+
const OUTPUT_SCHEMAS = {
|
|
274
|
+
run: {
|
|
275
|
+
ok: z.boolean(),
|
|
276
|
+
value: z.unknown().optional(),
|
|
277
|
+
prints: strs,
|
|
278
|
+
calls: z
|
|
279
|
+
.array(o({ fn: str, ok: z.boolean(), summary: str }))
|
|
280
|
+
.describe("Every host call, in order"),
|
|
281
|
+
error: o({
|
|
282
|
+
kind: z.enum(["script", "timeout", "limit", "aborted"]),
|
|
283
|
+
message: str,
|
|
284
|
+
line: num.optional(),
|
|
285
|
+
column: num.optional(),
|
|
286
|
+
}).optional(),
|
|
287
|
+
feedback: z.array(feedbackSchema).describe("Every batch the run received, also on failure"),
|
|
288
|
+
session: nullStr,
|
|
289
|
+
},
|
|
290
|
+
publish: writeOutput,
|
|
291
|
+
ask: { asks: z.array(o({ id: str, text: str })), open: num, url: str, feedback: feedbackRide },
|
|
292
|
+
feedback: {
|
|
293
|
+
feedback: z.array(feedbackSchema).describe("One batch per mock; empty when nothing is new"),
|
|
294
|
+
pending: z.array(pendingSchema),
|
|
295
|
+
},
|
|
296
|
+
say: { feedback: feedbackRide },
|
|
297
|
+
read: {
|
|
298
|
+
mocks: z.array(o({ slug: str, states: strs, variants: num, open: num })).optional(),
|
|
299
|
+
states: strs.optional(),
|
|
300
|
+
asks: z.array(o({ id: str, text: str })).optional(),
|
|
301
|
+
variants: z.array(o({ state: nullStr, variant: str, status: str, version: num })).optional(),
|
|
302
|
+
knobs: bag.optional(),
|
|
303
|
+
tuned: bag.optional().describe("Knob values from the latest reply"),
|
|
304
|
+
pending: z.union([z.array(pendingSchema), pendingSchema]),
|
|
305
|
+
},
|
|
306
|
+
export: {
|
|
307
|
+
states: z.array(o({ state: nullStr, variant: str, version: num, html: str, markdown: str })),
|
|
308
|
+
reply: bag.nullable(),
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
// The core loop, loaded up front by harnesses that defer the rest of the
|
|
312
|
+
// catalog behind a tool search.
|
|
313
|
+
const ALWAYS_LOAD = new Set(["publish", "ask", "feedback", "guide"]);
|
|
314
|
+
const toolMeta = (name) => ALWAYS_LOAD.has(name) ? { _meta: { "anthropic/alwaysLoad": true } } : {};
|
|
315
|
+
function shapeFor(tool, transport) {
|
|
316
|
+
const shape = {};
|
|
317
|
+
const params = transport === "stdio" ? { ...tool.params, ...tool.stdioExtra } : tool.params;
|
|
318
|
+
for (const [key, p] of Object.entries(params)) {
|
|
319
|
+
if (transport === "stdio" && tool.httpOnly?.includes(key))
|
|
320
|
+
continue;
|
|
321
|
+
shape[key] =
|
|
322
|
+
transport === "stdio" && key === "html" && tool.name === "publish"
|
|
323
|
+
? z.string().optional().describe(`${field.html}; or a file path`)
|
|
324
|
+
: zodParam(p);
|
|
325
|
+
}
|
|
326
|
+
return shape;
|
|
327
|
+
}
|
|
328
|
+
const SAFE_INT = Number.MAX_SAFE_INTEGER;
|
|
329
|
+
function compact(value) {
|
|
330
|
+
if (Array.isArray(value))
|
|
331
|
+
return value.map(compact);
|
|
332
|
+
if (!value || typeof value !== "object")
|
|
333
|
+
return value;
|
|
334
|
+
const node = {};
|
|
335
|
+
for (const [k, v] of Object.entries(value))
|
|
336
|
+
node[k] = compact(v);
|
|
337
|
+
delete node.$schema;
|
|
338
|
+
if (typeof node.id === "string")
|
|
339
|
+
delete node.id;
|
|
340
|
+
delete node.propertyNames;
|
|
341
|
+
if (node.minimum === -SAFE_INT)
|
|
342
|
+
delete node.minimum;
|
|
343
|
+
if (node.maximum === SAFE_INT)
|
|
344
|
+
delete node.maximum;
|
|
345
|
+
const extra = node.additionalProperties;
|
|
346
|
+
if (extra && typeof extra === "object" && Object.keys(extra).length === 0) {
|
|
347
|
+
delete node.additionalProperties;
|
|
348
|
+
}
|
|
349
|
+
// zod spells nullable as anyOf [T, null]; a type list says the same.
|
|
350
|
+
const any = node.anyOf;
|
|
351
|
+
if (any?.length === 2 && typeof any[0].type === "string" && any[1].type === "null") {
|
|
352
|
+
const { anyOf: _, ...rest } = node;
|
|
353
|
+
return { ...any[0], ...rest, type: [any[0].type, "null"] };
|
|
354
|
+
}
|
|
355
|
+
return node;
|
|
356
|
+
}
|
|
357
|
+
const toJson = (schema, io) => compact(z.toJSONSchema(schema, { target: "draft-2020-12", io }));
|
|
358
|
+
const catalog = (transport, defs = MCP_TOOL_DEFS) => defs.map((tool) => {
|
|
359
|
+
const output = OUTPUT_SCHEMAS[tool.name];
|
|
360
|
+
return {
|
|
361
|
+
name: tool.name,
|
|
362
|
+
description: tool.description,
|
|
363
|
+
inputSchema: toJson(z.object(shapeFor(tool, transport)), "input"),
|
|
364
|
+
...(output ? { outputSchema: toJson(z.looseObject(output), "output") } : {}),
|
|
365
|
+
...toolMeta(tool.name),
|
|
366
|
+
};
|
|
367
|
+
});
|
|
368
|
+
export const HTTP_MCP_TOOLS = catalog("http");
|
|
369
|
+
// What stdio's tools/list returns. The SDK would serialize the zod shapes
|
|
370
|
+
// itself, but as draft-07 with the leaked `id` (see `compact`), so mcp/server.ts
|
|
371
|
+
// lists this and keeps the zod shapes below for call validation only.
|
|
372
|
+
export const STDIO_MCP_CATALOG = catalog("stdio");
|
|
373
|
+
export const HTTP_RUN_TOOLS = catalog("http", [RUN_TOOL_DEF]);
|
|
374
|
+
export const STDIO_RUN_CATALOG = catalog("stdio", [RUN_TOOL_DEF]);
|
|
375
|
+
const stdioTools = (defs) => defs.map((tool) => ({
|
|
376
|
+
name: tool.name,
|
|
377
|
+
description: tool.description,
|
|
378
|
+
inputSchema: shapeFor(tool, "stdio"),
|
|
379
|
+
...(OUTPUT_SCHEMAS[tool.name]
|
|
380
|
+
? { outputSchema: z.looseObject(OUTPUT_SCHEMAS[tool.name]) }
|
|
381
|
+
: {}),
|
|
382
|
+
...toolMeta(tool.name),
|
|
383
|
+
}));
|
|
384
|
+
export const STDIO_MCP_TOOLS = stdioTools(MCP_TOOL_DEFS);
|
|
385
|
+
export const STDIO_RUN_TOOLS = stdioTools([RUN_TOOL_DEF]);
|
|
386
|
+
export const MCP_TOOL_NAMES = MCP_TOOL_DEFS.map((t) => t.name);
|
|
387
|
+
// One result shape for both transports: the JSON text every client reads, plus
|
|
388
|
+
// the same value as structuredContent where the tool declares an outputSchema.
|
|
389
|
+
export function toolResult(name, value) {
|
|
390
|
+
const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
391
|
+
const structured = Object.hasOwn(OUTPUT_SCHEMAS, name) && value !== null && typeof value === "object";
|
|
392
|
+
return {
|
|
393
|
+
content: [{ type: "text", text }],
|
|
394
|
+
...(structured ? { structuredContent: value } : {}),
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
// A run that failed is still a full envelope (calls, feedback); isError tells
|
|
398
|
+
// the client the script did not finish.
|
|
399
|
+
export function runToolResult(envelope) {
|
|
400
|
+
const ok = envelope?.ok === true;
|
|
401
|
+
return { ...toolResult("run", envelope), ...(ok ? {} : { isError: true }) };
|
|
402
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Surface } from "./types.ts";
|
|
2
|
+
export interface PartInfo {
|
|
3
|
+
name: string;
|
|
4
|
+
label?: string;
|
|
5
|
+
keys?: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface PartChanges {
|
|
8
|
+
vanished: string[];
|
|
9
|
+
renamed: {
|
|
10
|
+
from: string;
|
|
11
|
+
to: string;
|
|
12
|
+
}[];
|
|
13
|
+
}
|
|
14
|
+
export declare function partsInHtml(html: string): PartInfo[];
|
|
15
|
+
export declare function partsInSurfaces(surfaces: Surface[]): PartInfo[];
|
|
16
|
+
export declare function mergeParts(lists: PartInfo[][]): PartInfo[];
|
|
17
|
+
export declare function diffParts(before: PartInfo[], after: PartInfo[]): PartChanges;
|
|
18
|
+
export declare function splicePart(html: string, target: string, replacement: string): {
|
|
19
|
+
html: string;
|
|
20
|
+
} | {
|
|
21
|
+
error: string;
|
|
22
|
+
};
|
|
23
|
+
export type PartSplice = {
|
|
24
|
+
ok: true;
|
|
25
|
+
surfaces: Surface[];
|
|
26
|
+
applied: string[];
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
error: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function spliceParts(surfaces: Surface[], edits: Record<string, string>): PartSplice;
|