@launchchair/agent 0.5.0 → 0.5.1
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/AGENTS.md
CHANGED
|
@@ -36,6 +36,8 @@ curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/local-bridge.mjs" -o
|
|
|
36
36
|
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/launchchair-cli.mjs" -o "$HOME/launchchair-agent/launchchair-cli.mjs"
|
|
37
37
|
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/mcp-bridge-server.mjs" -o "$HOME/launchchair-agent/mcp-bridge-server.mjs"
|
|
38
38
|
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/mcp-capabilities.mjs" -o "$HOME/launchchair-agent/mcp-capabilities.mjs"
|
|
39
|
+
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/mcp-widgets.mjs" -o "$HOME/launchchair-agent/mcp-widgets.mjs"
|
|
40
|
+
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/mcp-widget.html" -o "$HOME/launchchair-agent/mcp-widget.html"
|
|
39
41
|
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/project-workspace.mjs" -o "$HOME/launchchair-agent/project-workspace.mjs"
|
|
40
42
|
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/runner-command.mjs" -o "$HOME/launchchair-agent/runner-command.mjs"
|
|
41
43
|
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/codex-runner.mjs" -o "$HOME/launchchair-agent/codex-runner.mjs"
|
|
@@ -131,6 +133,7 @@ Prefer `launchchair setup codex` so the CLI writes native config and verifies MC
|
|
|
131
133
|
6. Use `launchchair_run_once` or `executionMode=local_runner` only when the user explicitly requests a separate local runner.
|
|
132
134
|
7. If LaunchChair asks for a user-choice bucket such as wedge, pricing, or feature direction, call `launchchair_select_choice`. Do not use generic SPEC_UPDATE apply for user-choice buckets.
|
|
133
135
|
8. When the user asks for validation scores or wants to compare ideas, call `launchchair_get_scorecards`. Report validation scorecards separately from generated direction-option scores.
|
|
136
|
+
9. In hosts that support MCP Apps, present `launchchair_show_choices` at a choice step and after a selection, `launchchair_show_phase_synthesis` after phase completion, and `launchchair_show_validation_scores` for validation results. Their Next action controls return work to the host agent. If UI is unsupported, summarize the structured result and show the returned URLs.
|
|
134
137
|
|
|
135
138
|
## Consent Behavior
|
|
136
139
|
|
package/README.md
CHANGED
|
@@ -12,12 +12,30 @@ launchchair --version
|
|
|
12
12
|
launchchair doctor
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Version 0.5.
|
|
15
|
+
Version 0.5.1 adds tabbed MCP Apps widgets for saved choices, rich phase synthesis, and Product/Feature validation scorecards to the expanded MCP, browser login, private token management, host-agent execution, idea preservation, and optional validation gates. Existing installations must update the package and restart their MCP connection to load the new tools.
|
|
16
16
|
|
|
17
17
|
After updating, run `launchchair setup codex --skip-login` or `launchchair setup claude --skip-login` to refresh the installed path and verify tool discovery. Then restart/reload that client. Your saved account login and project bindings remain in `~/.launchchair/config.json`. Older tokens still work within their existing scopes; run `launchchair login` again with workspace-owner approval if you need the new `tokens.manage` scope.
|
|
18
18
|
|
|
19
19
|
For local release testing, run `npm pack ./packages/launchchair-agent --pack-destination /tmp` from the repository root and install the resulting tarball into an isolated npm prefix before publishing.
|
|
20
20
|
|
|
21
|
+
## Agent response widgets
|
|
22
|
+
|
|
23
|
+
The bridge supports LaunchChair AUX through MCP Apps. Three read-only tools return tabbed widgets using the app's Chakra theme, shared choice cards, and existing strategic scorecards:
|
|
24
|
+
|
|
25
|
+
- `launchchair_show_choices` (`projectId`, `phaseKey`, `bucketKey`): generated options and the saved choice.
|
|
26
|
+
- `launchchair_show_phase_synthesis` (`projectId`, `phaseKey`): the app's phase-specific synthesis cards, native tabs and charts, with verified completion state.
|
|
27
|
+
- `launchchair_show_validation_scores` (`projectId`): validation scorecards, evidence inputs, and separate direction scores for Product and Feature projects.
|
|
28
|
+
|
|
29
|
+
Every widget includes **Next action** controls. Saving a choice calls the existing `launchchair_select_choice` tool, then rereads the saved selection. Continue/review actions send a message to the host agent; the host executes queued prompts with its tools. The iframe never receives a token or calls the API directly. Existing scopes, entitlements, idea policies, validation gates, and downstream resets still apply.
|
|
30
|
+
|
|
31
|
+
The content pane scrolls at 560px on desktop and 440px on smaller screens using the app's scrollbar styles. The next-action footer stays outside that pane. Selected choices reuse the same choice cards, and validation thresholds live inside the scorecard footer. Synthesis reads the sanitized spec through the existing export permission; competitor editing and workspace notes remain in the authenticated app.
|
|
32
|
+
|
|
33
|
+
Rendering uses `_meta.ui.resourceUri`, `text/html;profile=mcp-app` resources, and the MCP Apps JSON-RPC host bridge. Compatible ChatGPT hosts can also use the feature-detected `window.openai` aliases. Text-only clients receive the full structured result and URLs. Static templates can be discovered while logged out; project reads still require authentication.
|
|
34
|
+
|
|
35
|
+
From the repository root, run `npm run agent:widgets:build` after changing widget components or the theme. This rebuilds the self-contained HTML in both bridge distributions. `npm run agent:widgets:preview -- /absolute/path/examples.html` builds interactive local examples with labeled sample data. Nothing is published by these commands.
|
|
36
|
+
|
|
37
|
+
The choice widget requires the bucket API's additive `choice` fields. Synthesis requests the existing export endpoint with `?format=json`; default Markdown downloads are unchanged. Test against a matching local API before releasing. Release the matching API changes before publishing the bridge package so installed clients can read the new choice and synthesis payloads.
|
|
38
|
+
|
|
21
39
|
## Login
|
|
22
40
|
|
|
23
41
|
Approve the CLI once in your browser. LaunchChair creates a scoped Agent API token after approval and the CLI stores it locally:
|
package/launchchair-cli.mjs
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
unbindProjectWorkspace,
|
|
21
21
|
} from "./project-workspace.mjs";
|
|
22
22
|
|
|
23
|
-
const VERSION = "0.5.
|
|
23
|
+
const VERSION = "0.5.1";
|
|
24
24
|
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
25
25
|
const DEFAULT_BASE_URL = "https://www.launchchair.io";
|
|
26
26
|
const CONFIG_DIR = process.env.LAUNCHCHAIR_CONFIG_DIR?.trim()
|
package/local-bridge.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import { fileURLToPath } from "node:url";
|
|
|
18
18
|
import { resolveProjectWorkspace } from "./project-workspace.mjs";
|
|
19
19
|
import { buildRunnerCommand, parseRunnerCommand } from "./runner-command.mjs";
|
|
20
20
|
|
|
21
|
-
const VERSION = "0.5.
|
|
21
|
+
const VERSION = "0.5.1";
|
|
22
22
|
const CLIENT_TYPES = ["codex", "chatgpt", "claude", "claude_code", "other"];
|
|
23
23
|
const AGENT_SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
24
24
|
const CONFIG_DIR = process.env.LAUNCHCHAIR_CONFIG_DIR?.trim()
|
package/mcp-bridge-server.mjs
CHANGED
|
@@ -20,9 +20,11 @@ import { fileURLToPath } from "node:url";
|
|
|
20
20
|
import { resolveProjectWorkspace } from "./project-workspace.mjs";
|
|
21
21
|
import { buildRunnerCommand, parseRunnerCommand } from "./runner-command.mjs";
|
|
22
22
|
import { createMcpCapabilities, EXTRA_MCP_TOOLS, MCP_WORKFLOW_INSTRUCTIONS, VALIDATION_GATE_SCHEMA } from "./mcp-capabilities.mjs";
|
|
23
|
+
import { MCP_WIDGET_TOOLS, MCP_WIDGET_INSTRUCTIONS, listWidgetResources, readWidgetResource, callWidgetTool } from "./mcp-widgets.mjs";
|
|
23
24
|
|
|
24
|
-
const VERSION = "0.5.
|
|
25
|
+
const VERSION = "0.5.1";
|
|
25
26
|
const SERVER_NAME = "launchchair-agent-mcp";
|
|
27
|
+
const SUPPORTED_PROTOCOL_VERSIONS = new Set(["2025-11-25", "2025-06-18", "2025-03-26", "2024-11-05"]);
|
|
26
28
|
const AGENT_SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
27
29
|
const CONFIG_DIR = process.env.LAUNCHCHAIR_CONFIG_DIR?.trim()
|
|
28
30
|
? path.resolve(process.env.LAUNCHCHAIR_CONFIG_DIR.trim())
|
|
@@ -181,7 +183,7 @@ function loadStatus() {
|
|
|
181
183
|
|
|
182
184
|
function serverInstructions(status) {
|
|
183
185
|
const featureInstructions = String(status?.capabilities?.mcpServerInstructions ?? "").trim();
|
|
184
|
-
return `${BASE_SERVER_INSTRUCTIONS} ${featureInstructions} ${MCP_WORKFLOW_INSTRUCTIONS}`;
|
|
186
|
+
return `${BASE_SERVER_INSTRUCTIONS} ${featureInstructions} ${MCP_WORKFLOW_INSTRUCTIONS} ${MCP_WIDGET_INSTRUCTIONS}`;
|
|
185
187
|
}
|
|
186
188
|
|
|
187
189
|
function targetForRun(run) {
|
|
@@ -989,6 +991,7 @@ const READ_ONLY_TOOLS = new Set(["launchchair_status", "launchchair_list_project
|
|
|
989
991
|
function annotateTool(tool) {
|
|
990
992
|
return {
|
|
991
993
|
...tool,
|
|
994
|
+
...(tool.name === "launchchair_select_choice" ? { _meta: { ...tool._meta, ui: { visibility: ["model", "app"] }, "openai/widgetAccessible": true } } : {}),
|
|
992
995
|
annotations: {
|
|
993
996
|
...(tool.annotations ?? {}),
|
|
994
997
|
readOnlyHint: tool.annotations?.readOnlyHint ?? READ_ONLY_TOOLS.has(tool.name),
|
|
@@ -1034,6 +1037,7 @@ function writeError(id, code, message, data = undefined) {
|
|
|
1034
1037
|
|
|
1035
1038
|
function textResult(payload) {
|
|
1036
1039
|
return {
|
|
1040
|
+
...(payload && typeof payload === "object" && !Array.isArray(payload) ? { structuredContent: payload } : {}),
|
|
1037
1041
|
content: [
|
|
1038
1042
|
{
|
|
1039
1043
|
type: "text",
|
|
@@ -1054,9 +1058,10 @@ async function handleRpc(request) {
|
|
|
1054
1058
|
if (method === "initialize") {
|
|
1055
1059
|
const status = await loadStatus();
|
|
1056
1060
|
writeResult(id, {
|
|
1057
|
-
protocolVersion: "
|
|
1061
|
+
protocolVersion: SUPPORTED_PROTOCOL_VERSIONS.has(request?.params?.protocolVersion) ? request.params.protocolVersion : "2025-11-25",
|
|
1058
1062
|
capabilities: {
|
|
1059
1063
|
tools: { listChanged: true },
|
|
1064
|
+
resources: {},
|
|
1060
1065
|
},
|
|
1061
1066
|
serverInfo: {
|
|
1062
1067
|
name: SERVER_NAME,
|
|
@@ -1072,12 +1077,28 @@ async function handleRpc(request) {
|
|
|
1072
1077
|
return;
|
|
1073
1078
|
}
|
|
1074
1079
|
|
|
1080
|
+
if (method === "resources/list") {
|
|
1081
|
+
writeResult(id, { resources: listWidgetResources() });
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
if (method === "resources/read") {
|
|
1086
|
+
try { writeResult(id, readWidgetResource(request?.params?.uri)); }
|
|
1087
|
+
catch (error) { writeError(id, -32002, error.message); }
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
if (method === "ping") {
|
|
1092
|
+
writeResult(id, {});
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1075
1096
|
if (method === "tools/list") {
|
|
1076
1097
|
const status = await loadStatus();
|
|
1077
1098
|
const overrides = Array.isArray(status?.capabilities?.mcpToolOverrides)
|
|
1078
1099
|
? status.capabilities.mcpToolOverrides
|
|
1079
1100
|
: [];
|
|
1080
|
-
const tools = mergeToolOverrides([...MCP_TOOLS, ...EXTRA_MCP_TOOLS], overrides);
|
|
1101
|
+
const tools = [...mergeToolOverrides([...MCP_TOOLS, ...EXTRA_MCP_TOOLS], overrides), ...MCP_WIDGET_TOOLS];
|
|
1081
1102
|
writeResult(id, { tools });
|
|
1082
1103
|
return;
|
|
1083
1104
|
}
|
|
@@ -1087,6 +1108,10 @@ async function handleRpc(request) {
|
|
|
1087
1108
|
const toolName = String(params.name ?? "");
|
|
1088
1109
|
const args = typeof params.arguments === "object" && params.arguments ? params.arguments : {};
|
|
1089
1110
|
try {
|
|
1111
|
+
if (MCP_WIDGET_TOOLS.some((tool) => tool.name === toolName)) {
|
|
1112
|
+
writeResult(id, await callWidgetTool(toolName, args, { api, resolveProjectId, baseUrl: config.baseUrl }));
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1090
1115
|
if (EXTRA_MCP_TOOLS.some((tool) => tool.name === toolName)) {
|
|
1091
1116
|
writeResult(id, textResult(await capabilities.dispatch(toolName, args)));
|
|
1092
1117
|
return;
|