@showly/mcp-server 0.4.3 → 0.4.5

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/dist/cli.d.ts CHANGED
@@ -130,6 +130,17 @@ export declare function buildLoginPrompt(input: {
130
130
  userCode: string;
131
131
  expiresAt: Date;
132
132
  }): string;
133
+ /**
134
+ * Extra control-plane instructions for agents that launched the device flow.
135
+ *
136
+ * The human-facing approval block is intentionally host-neutral and is often
137
+ * relayed verbatim. Remote agent hosts need one additional instruction that is
138
+ * meant for the executor, not the human: keep ownership of the foreground
139
+ * process and poll it until it exits. Without this, an agent can accurately
140
+ * relay the code, end its turn, and strand the post-approval work even though
141
+ * the CLI itself later receives and stores the credential successfully.
142
+ */
143
+ export declare function buildRemoteAgentWaitInstruction(target: LoginTarget): string | null;
133
144
  /** Claude Code reads a `headers` map on an http MCP server entry. */
134
145
  export declare function buildClaudeCodeAuthSnippet(opts: {
135
146
  url: string;
@@ -142,6 +153,17 @@ export declare function buildCodexAuthSnippet(opts: {
142
153
  /** Secrets used by the two remote-agent targets the Hero prompt exercises. */
143
154
  export declare const OPENCLAW_TOKEN_ENV_VAR = "SHOWLY_MCP_TOKEN";
144
155
  export declare const HERMES_TOKEN_ENV_VAR = "MCP_SHOWLY_API_KEY";
156
+ /**
157
+ * Give every OpenClaw credential its own environment variable name.
158
+ *
159
+ * A managed Gateway can keep the old process environment alive while its
160
+ * user config is reloaded. With one fixed SHOWLY_MCP_TOKEN name, that stale
161
+ * process value wins over the freshly-written env.vars value and a successful
162
+ * re-login immediately fails with token_revoked until the whole instance is
163
+ * restarted. The token fingerprint is not a credential, but makes the new
164
+ * config reference a name the old process cannot already contain.
165
+ */
166
+ export declare function openClawTokenEnvVar(token: string): string;
145
167
  /**
146
168
  * Merge Showly into OpenClaw's user config without disturbing other Gateway
147
169
  * settings. The token lives in the Gateway-owned env.vars store; the MCP entry
package/dist/cli.js CHANGED
@@ -34,6 +34,7 @@ import { readFileSync, readdirSync, mkdirSync, rmSync, writeFileSync, existsSync
34
34
  import { dirname, join } from "node:path";
35
35
  import { homedir } from "node:os";
36
36
  import { pathToFileURL } from "node:url";
37
+ import { createHash } from "node:crypto";
37
38
  import { parseDocument } from "yaml";
38
39
  import { loadManifest } from "./index.js";
39
40
  import { SHOWLY_HOSTING_SKILL_DIRECTORY, SHOWLY_HOSTING_SKILL_NAME, SHOWLY_LEGACY_SKILL_NAME, } from "./showly-hosting-skill.js";
@@ -99,7 +100,7 @@ function usage() {
99
100
  "host, pass --agent <cursor|openclaw|hermes|cline> so My Agents can name it.",
100
101
  "",
101
102
  "Environment overrides:",
102
- " SHOWLY_MCP_URL full URL to your MCP endpoint (default https://mcp.showly.ai)",
103
+ " SHOWLY_MCP_URL full URL to your MCP endpoint (default https://mcp.showly.ai/mcp)",
103
104
  " SHOWLY_API_URL full URL to your API (default https://api.showly.ai)",
104
105
  "",
105
106
  "--with-skill installs a reusable showly-hosting skill for Claude Code or Codex.",
@@ -453,6 +454,26 @@ export function buildLoginPrompt(input) {
453
454
  "connected until you approve, and the command can be run again.",
454
455
  ].join("\n");
455
456
  }
457
+ /**
458
+ * Extra control-plane instructions for agents that launched the device flow.
459
+ *
460
+ * The human-facing approval block is intentionally host-neutral and is often
461
+ * relayed verbatim. Remote agent hosts need one additional instruction that is
462
+ * meant for the executor, not the human: keep ownership of the foreground
463
+ * process and poll it until it exits. Without this, an agent can accurately
464
+ * relay the code, end its turn, and strand the post-approval work even though
465
+ * the CLI itself later receives and stores the credential successfully.
466
+ */
467
+ export function buildRemoteAgentWaitInstruction(target) {
468
+ if (target !== "openclaw" && target !== "hermes")
469
+ return null;
470
+ return [
471
+ "[AGENT EXECUTOR - REQUIRED]",
472
+ "The approval block above is an intermediate update, never the final response.",
473
+ "Keep this exact foreground process/session handle alive and repeatedly use your host's wait/poll action until the command exits. No new output means it is still waiting.",
474
+ "Do not ask the human to message you after approval, and do not start another login while this process is alive.",
475
+ ].join("\n");
476
+ }
456
477
  /** Claude Code reads a `headers` map on an http MCP server entry. */
457
478
  export function buildClaudeCodeAuthSnippet(opts) {
458
479
  return {
@@ -490,6 +511,24 @@ export function buildCodexAuthSnippet(opts) {
490
511
  /** Secrets used by the two remote-agent targets the Hero prompt exercises. */
491
512
  export const OPENCLAW_TOKEN_ENV_VAR = "SHOWLY_MCP_TOKEN";
492
513
  export const HERMES_TOKEN_ENV_VAR = "MCP_SHOWLY_API_KEY";
514
+ /**
515
+ * Give every OpenClaw credential its own environment variable name.
516
+ *
517
+ * A managed Gateway can keep the old process environment alive while its
518
+ * user config is reloaded. With one fixed SHOWLY_MCP_TOKEN name, that stale
519
+ * process value wins over the freshly-written env.vars value and a successful
520
+ * re-login immediately fails with token_revoked until the whole instance is
521
+ * restarted. The token fingerprint is not a credential, but makes the new
522
+ * config reference a name the old process cannot already contain.
523
+ */
524
+ export function openClawTokenEnvVar(token) {
525
+ const fingerprint = createHash("sha256")
526
+ .update(token)
527
+ .digest("hex")
528
+ .slice(0, 12)
529
+ .toUpperCase();
530
+ return `${OPENCLAW_TOKEN_ENV_VAR}_${fingerprint}`;
531
+ }
493
532
  function asRecord(value) {
494
533
  return typeof value === "object" && value !== null && !Array.isArray(value)
495
534
  ? value
@@ -509,11 +548,14 @@ export function buildOpenClawAuthConfig(opts) {
509
548
  const currentShowly = asRecord(servers.showly);
510
549
  const currentHeaders = asRecord(currentShowly.headers);
511
550
  const { auth: _nativeOauth, ...showlyWithoutOauth } = currentShowly;
551
+ const tokenEnvVar = openClawTokenEnvVar(opts.token);
552
+ const varsWithoutOldShowlyTokens = Object.fromEntries(Object.entries(vars).filter(([name]) => name !== OPENCLAW_TOKEN_ENV_VAR &&
553
+ !name.startsWith(`${OPENCLAW_TOKEN_ENV_VAR}_`)));
512
554
  return {
513
555
  ...opts.existing,
514
556
  env: {
515
557
  ...env,
516
- vars: { ...vars, [OPENCLAW_TOKEN_ENV_VAR]: opts.token },
558
+ vars: { ...varsWithoutOldShowlyTokens, [tokenEnvVar]: opts.token },
517
559
  },
518
560
  mcp: {
519
561
  ...mcp,
@@ -525,7 +567,7 @@ export function buildOpenClawAuthConfig(opts) {
525
567
  transport: "streamable-http",
526
568
  headers: {
527
569
  ...currentHeaders,
528
- Authorization: `Bearer \${${OPENCLAW_TOKEN_ENV_VAR}}`,
570
+ Authorization: `Bearer \${${tokenEnvVar}}`,
529
571
  },
530
572
  },
531
573
  },
@@ -892,6 +934,9 @@ export async function performLogin(opts, deps = {}) {
892
934
  userCode: started.user_code,
893
935
  expiresAt,
894
936
  }));
937
+ const remoteAgentWaitInstruction = buildRemoteAgentWaitInstruction(opts.target);
938
+ if (remoteAgentWaitInstruction)
939
+ log(remoteAgentWaitInstruction);
895
940
  const token = await pollForDeviceToken({
896
941
  apiUrl,
897
942
  deviceCode: started.device_code,
@@ -1102,7 +1147,15 @@ export function buildLoginOutput(result, opts = {}) {
1102
1147
  });
1103
1148
  lines.push({ stream: "out", line: result.snippet });
1104
1149
  }
1105
- for (const name of envVarsReferencedBy(result.snippet)) {
1150
+ // OpenClaw and Hermes have already received the value in their protected
1151
+ // user-level credential store. Printing an export for them is both false
1152
+ // (the variable is set) and needlessly exposes the token in terminal/chat
1153
+ // logs. Codex and stdout are the two handoff targets whose config really
1154
+ // does depend on the human setting the referenced environment variable.
1155
+ const handoffEnvVars = result.target === "codex" || result.target === "stdout"
1156
+ ? envVarsReferencedBy(result.snippet)
1157
+ : [];
1158
+ for (const name of handoffEnvVars) {
1106
1159
  lines.push({
1107
1160
  stream: "err",
1108
1161
  line: `That config reads the token from ${name}, and nothing sets it yet. Put this in the environment your agent starts in:`,
@@ -1113,6 +1166,25 @@ export function buildLoginOutput(result, opts = {}) {
1113
1166
  line: "That line is the credential itself. It is printed here once, and kept out of the config file so the file stays safe to commit.",
1114
1167
  });
1115
1168
  }
1169
+ if (result.target === "openclaw") {
1170
+ lines.push({
1171
+ stream: "err",
1172
+ line: [
1173
+ "[AGENT EXECUTOR - REQUIRED] Stay in this task: run `openclaw mcp reload`, rediscover Showly's native tools, then make real `list_projects` and `list_sites` calls.",
1174
+ "A saved config or successful doctor/probe is not completion. If the host already ended the task while approval was pending, use one fresh task as a fallback; do not repeat login.",
1175
+ ].join("\n"),
1176
+ });
1177
+ }
1178
+ if (result.target === "hermes") {
1179
+ lines.push({
1180
+ stream: "err",
1181
+ line: [
1182
+ "[AGENT EXECUTOR - REQUIRED] Reload the live Hermes MCP inventory with `/reload-mcp`, then verify with native `mcp__showly__list_projects` and `mcp__showly__list_sites` calls.",
1183
+ "On managed MyClaw, a new task can reuse the same worker. If the native tools are still absent after reload, restart the Hermes instance/container once and open a fresh task; do not repeat login.",
1184
+ "`hermes mcp test` or raw JSON-RPC proves the credential and server, not that the running agent loaded the tools.",
1185
+ ].join("\n"),
1186
+ });
1187
+ }
1116
1188
  if (result.expiresAt) {
1117
1189
  lines.push({
1118
1190
  stream: "err",
package/manifest.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://showly.ai/schemas/skill-manifest-v1.json",
3
3
  "name": "showly",
4
4
  "displayName": "Showly",
5
- "version": "0.4.3",
5
+ "version": "0.4.5",
6
6
  "description": "Deploy and manage Showly sites from inside Claude Code / Codex.",
7
7
  "homepage": "https://showly.ai/docs/mcp/overview",
8
8
  "publisher": "Showly",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "endpoints": {
28
28
  "url_env": "SHOWLY_MCP_URL",
29
- "default_url": "https://mcp.showly.ai",
29
+ "default_url": "https://mcp.showly.ai/mcp",
30
30
  "api_url_env": "SHOWLY_API_URL"
31
31
  }
32
32
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@showly/mcp-server",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "Connect Claude Code / Codex to the Showly MCP server — preview and deploy sites from your agent.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -52,11 +52,11 @@
52
52
  },
53
53
  "claude-code-skill": {
54
54
  "name": "showly",
55
- "version": "0.4.3",
55
+ "version": "0.4.5",
56
56
  "description": "Deploy and manage Showly sites from inside Claude Code.",
57
57
  "mcp-server": {
58
58
  "url-env": "SHOWLY_MCP_URL",
59
- "default-url": "https://mcp.showly.ai",
59
+ "default-url": "https://mcp.showly.ai/mcp",
60
60
  "auth": "oauth",
61
61
  "api-url-env": "SHOWLY_API_URL"
62
62
  },
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "codex-plugin": {
67
67
  "name": "showly",
68
- "version": "0.4.3",
68
+ "version": "0.4.5",
69
69
  "type": "mcp-server",
70
70
  "manifest": "manifest.json"
71
71
  },