@launchchair/agent 0.5.0 → 0.5.2
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/AGENTS.md +3 -0
- package/README.md +19 -1
- package/launchchair-cli.mjs +1 -1
- package/local-bridge.mjs +1 -1
- package/mcp-bridge-server.mjs +29 -4
- package/mcp-widget.html +1203 -0
- package/mcp-widgets.mjs +74 -0
- package/package.json +3 -1
- package/server.json +2 -2
package/mcp-widgets.mjs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
|
|
4
|
+
const html = readFileSync(new URL("./mcp-widget.html", import.meta.url), "utf8");
|
|
5
|
+
// Content-addressed resources prevent hosts from reusing an older UI bundle.
|
|
6
|
+
export const WIDGET_URI = `ui://launchchair/project-${createHash("sha256").update(html).digest("hex").slice(0, 12)}.html`;
|
|
7
|
+
const mimeType = "text/html;profile=mcp-app";
|
|
8
|
+
const project = { projectId: { type: "string" }, projectName: { type: "string" } };
|
|
9
|
+
|
|
10
|
+
export const MCP_WIDGET_TOOLS = [
|
|
11
|
+
["show_choices", "Show the interactive, tabbed choice widget at a choice step or after selecting a choice. Reads the canonical options and saved selection. Users can save an explicit option and choose a next action.", { ...project, phaseKey: { type: "string" }, bucketKey: { type: "string" } }, ["phaseKey", "bucketKey"]],
|
|
12
|
+
["show_phase_synthesis", "Show a tabbed phase synthesis widget after a phase completes. Reads the existing synthesis and verified bucket progress, with next actions. Incomplete phases are explicitly labeled in progress.", { ...project, phaseKey: { type: "string" } }, ["phaseKey"]],
|
|
13
|
+
["show_validation_scores", "Show tabbed Product or Feature validation scorecards, their inputs, and separate direction options. Use after get_scorecards or when presenting validation results. Includes next actions.", project, []],
|
|
14
|
+
].map(([shortName, description, properties, required]) => ({
|
|
15
|
+
name: `launchchair_${shortName}`,
|
|
16
|
+
description,
|
|
17
|
+
inputSchema: { type: "object", properties, required, additionalProperties: false },
|
|
18
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
19
|
+
_meta: {
|
|
20
|
+
ui: { resourceUri: WIDGET_URI, visibility: ["model", "app"] },
|
|
21
|
+
"openai/outputTemplate": WIDGET_URI,
|
|
22
|
+
"openai/widgetAccessible": true,
|
|
23
|
+
},
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
export const MCP_WIDGET_INSTRUCTIONS = "Use LaunchChair AUX widgets when presenting decisions and phase results: call launchchair_show_choices at user-choice checkpoints and after saving a selection, launchchair_show_phase_synthesis for each newly completed phase, and launchchair_show_validation_scores for validation results. These read tools render tabbed MCP Apps with Next action controls; keep using data tools to execute the workflow. If the host cannot render MCP Apps, summarize the returned structured data and provide the returned URLs. Never claim the widget rendered in a host without evidence.";
|
|
27
|
+
|
|
28
|
+
export function listWidgetResources() {
|
|
29
|
+
return [{ uri: WIDGET_URI, name: "LaunchChair project widgets", description: "LaunchChair choices, phase synthesis and validation scores", mimeType }];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function readWidgetResource(uri) {
|
|
33
|
+
if (uri !== WIDGET_URI) throw new Error("Unknown LaunchChair UI resource.");
|
|
34
|
+
return { contents: [{ uri: WIDGET_URI, mimeType, text: html, _meta: {
|
|
35
|
+
ui: { prefersBorder: false, csp: { connectDomains: [], resourceDomains: ["https://fonts.googleapis.com", "https://fonts.gstatic.com"] } },
|
|
36
|
+
"openai/widgetDescription": "LaunchChair design-system tabs, saved choices, synthesis and validation scores with Next action controls.",
|
|
37
|
+
} }] };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function callWidgetTool(name, args, { api, resolveProjectId, baseUrl }) {
|
|
41
|
+
const projectId = await resolveProjectId(args);
|
|
42
|
+
const root = `/api/agent/v1/projects/${encodeURIComponent(projectId)}`;
|
|
43
|
+
let data;
|
|
44
|
+
let type;
|
|
45
|
+
const renderArguments = { projectId };
|
|
46
|
+
if (name === "launchchair_show_choices") {
|
|
47
|
+
if (!args.phaseKey || !args.bucketKey) throw new Error("phaseKey and bucketKey are required.");
|
|
48
|
+
Object.assign(renderArguments, { phaseKey: args.phaseKey, bucketKey: args.bucketKey });
|
|
49
|
+
data = await api(`${root}/buckets/${encodeURIComponent(args.phaseKey)}/${encodeURIComponent(args.bucketKey)}`);
|
|
50
|
+
if (!data?.choice || !Array.isArray(data.choice.options)) throw new Error("This bucket does not expose generated choices. Update the LaunchChair API before using this widget.");
|
|
51
|
+
type = "choices";
|
|
52
|
+
} else if (name === "launchchair_show_phase_synthesis") {
|
|
53
|
+
if (!args.phaseKey) throw new Error("phaseKey is required.");
|
|
54
|
+
renderArguments.phaseKey = args.phaseKey;
|
|
55
|
+
data = await api(`${root}/exports/phase/${encodeURIComponent(args.phaseKey)}?format=json`);
|
|
56
|
+
if (typeof data?.markdown !== "string" || typeof data?.percentage !== "number" || !data?.spec || typeof data.spec !== "object") throw new Error("Structured synthesis is unavailable. Update the LaunchChair API before using this widget.");
|
|
57
|
+
// The rich view consumes spec; text-only clients get the narrative without a
|
|
58
|
+
// second copy of the complete spec from the Markdown export's appendix.
|
|
59
|
+
data.markdown = data.markdown.split("\n## Complete Phase Data")[0].trim();
|
|
60
|
+
type = "synthesis";
|
|
61
|
+
} else if (name === "launchchair_show_validation_scores") {
|
|
62
|
+
data = await api(`${root}/scorecards`);
|
|
63
|
+
if (!Array.isArray(data?.scorecards)) throw new Error("Validation scorecards are unavailable.");
|
|
64
|
+
data.url = new URL(`/app/phase-summary/market_validation?projectId=${encodeURIComponent(projectId)}`, baseUrl).toString();
|
|
65
|
+
type = "scores";
|
|
66
|
+
} else {
|
|
67
|
+
throw new Error("Unknown LaunchChair widget tool.");
|
|
68
|
+
}
|
|
69
|
+
const widget = { ...data, projectId, type, renderTool: name, renderArguments };
|
|
70
|
+
return {
|
|
71
|
+
structuredContent: { widget },
|
|
72
|
+
content: [{ type: "text", text: JSON.stringify(widget, null, 2) }],
|
|
73
|
+
};
|
|
74
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@launchchair/agent",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"mcpName": "io.launchchair/agent",
|
|
5
5
|
"description": "LaunchChair Agent CLI, MCP bridge, local bridge, and runners for Codex, Claude Code, ChatGPT, Claude, and Hermes workflows.",
|
|
6
6
|
"type": "module",
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
"local-bridge.mjs",
|
|
17
17
|
"mcp-bridge-server.mjs",
|
|
18
18
|
"mcp-capabilities.mjs",
|
|
19
|
+
"mcp-widgets.mjs",
|
|
20
|
+
"mcp-widget.html",
|
|
19
21
|
"project-workspace.mjs",
|
|
20
22
|
"runner-command.mjs",
|
|
21
23
|
"codex-runner.mjs",
|
package/server.json
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
"name": "io.launchchair/agent",
|
|
4
4
|
"title": "LaunchChair",
|
|
5
5
|
"description": "Plan, validate, and build products and features with LaunchChair.",
|
|
6
|
-
"version": "0.5.
|
|
6
|
+
"version": "0.5.2",
|
|
7
7
|
"websiteUrl": "https://www.launchchair.io/docs/mcp",
|
|
8
8
|
"packages": [
|
|
9
9
|
{
|
|
10
10
|
"registryType": "npm",
|
|
11
11
|
"identifier": "@launchchair/agent",
|
|
12
|
-
"version": "0.5.
|
|
12
|
+
"version": "0.5.2",
|
|
13
13
|
"runtimeHint": "npx",
|
|
14
14
|
"runtimeArguments": [
|
|
15
15
|
{
|