@showly/mcp-server 0.4.2 → 0.4.4
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 +39 -3
- package/dist/cli.js +211 -9
- package/manifest.json +2 -2
- package/package.json +7 -4
package/dist/cli.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
export type Target = "claude-code" | "codex" | "stdout";
|
|
3
|
+
export type LoginTarget = Target | "openclaw" | "hermes";
|
|
3
4
|
/**
|
|
4
5
|
* The host that will RECEIVE a token printed to stdout.
|
|
5
6
|
*
|
|
@@ -88,7 +89,7 @@ export declare const LOGIN_CLIENT_ID = "showly-mcp-cli";
|
|
|
88
89
|
* names; lookalikes stay neutral.
|
|
89
90
|
*/
|
|
90
91
|
export declare const LOGIN_AGENT_CLIENT_IDS: Record<LoginAgent, string>;
|
|
91
|
-
export declare function loginClientId(target:
|
|
92
|
+
export declare function loginClientId(target: LoginTarget, agent?: LoginAgent): string;
|
|
92
93
|
/** The env var name emitted into config snippets that must not hold a secret. */
|
|
93
94
|
export declare const TOKEN_ENV_VAR = "SHOWLY_TOKEN";
|
|
94
95
|
export type DeviceStart = {
|
|
@@ -138,6 +139,40 @@ export declare function buildCodexAuthSnippet(opts: {
|
|
|
138
139
|
url: string;
|
|
139
140
|
tokenEnvVar?: string;
|
|
140
141
|
}): string;
|
|
142
|
+
/** Secrets used by the two remote-agent targets the Hero prompt exercises. */
|
|
143
|
+
export declare const OPENCLAW_TOKEN_ENV_VAR = "SHOWLY_MCP_TOKEN";
|
|
144
|
+
export declare const HERMES_TOKEN_ENV_VAR = "MCP_SHOWLY_API_KEY";
|
|
145
|
+
/**
|
|
146
|
+
* Give every OpenClaw credential its own environment variable name.
|
|
147
|
+
*
|
|
148
|
+
* A managed Gateway can keep the old process environment alive while its
|
|
149
|
+
* user config is reloaded. With one fixed SHOWLY_MCP_TOKEN name, that stale
|
|
150
|
+
* process value wins over the freshly-written env.vars value and a successful
|
|
151
|
+
* re-login immediately fails with token_revoked until the whole instance is
|
|
152
|
+
* restarted. The token fingerprint is not a credential, but makes the new
|
|
153
|
+
* config reference a name the old process cannot already contain.
|
|
154
|
+
*/
|
|
155
|
+
export declare function openClawTokenEnvVar(token: string): string;
|
|
156
|
+
/**
|
|
157
|
+
* Merge Showly into OpenClaw's user config without disturbing other Gateway
|
|
158
|
+
* settings. The token lives in the Gateway-owned env.vars store; the MCP entry
|
|
159
|
+
* contains only an environment placeholder. `auth: oauth` is removed because
|
|
160
|
+
* header auth and native OAuth are alternative modes in OpenClaw.
|
|
161
|
+
*/
|
|
162
|
+
export declare function buildOpenClawAuthConfig(opts: {
|
|
163
|
+
existing: Record<string, unknown>;
|
|
164
|
+
url: string;
|
|
165
|
+
token: string;
|
|
166
|
+
}): Record<string, unknown>;
|
|
167
|
+
/** Replace one dotenv assignment, remove duplicates, and preserve other lines. */
|
|
168
|
+
export declare function mergeDotEnvCredential(existing: string, name: string, token: string): string;
|
|
169
|
+
/**
|
|
170
|
+
* Update Hermes YAML through a real YAML document rather than `hermes config
|
|
171
|
+
* set`, whose secret masking corrupts the literal ${MCP_SHOWLY_API_KEY}
|
|
172
|
+
* placeholder. Parse errors are terminal: never replace a user's unreadable
|
|
173
|
+
* config with a guessed one.
|
|
174
|
+
*/
|
|
175
|
+
export declare function buildHermesAuthConfig(existing: string, url: string): string;
|
|
141
176
|
export type LoginDeps = {
|
|
142
177
|
fetchImpl?: typeof fetch;
|
|
143
178
|
/**
|
|
@@ -240,11 +275,12 @@ export declare function pollForDeviceToken(input: {
|
|
|
240
275
|
clientId?: string;
|
|
241
276
|
}, deps?: LoginDeps): Promise<DeviceToken>;
|
|
242
277
|
export type LoginResult = {
|
|
243
|
-
target:
|
|
278
|
+
target: LoginTarget;
|
|
244
279
|
token: string;
|
|
245
280
|
scope: string;
|
|
246
281
|
expiresAt: Date | null;
|
|
247
282
|
path: string | null;
|
|
283
|
+
credentialPath?: string;
|
|
248
284
|
wrote: boolean;
|
|
249
285
|
snippet: string;
|
|
250
286
|
};
|
|
@@ -259,7 +295,7 @@ export type LoginResult = {
|
|
|
259
295
|
* and out of every `ps` listing on the machine.
|
|
260
296
|
*/
|
|
261
297
|
export declare function performLogin(opts: {
|
|
262
|
-
target:
|
|
298
|
+
target: LoginTarget;
|
|
263
299
|
agent?: LoginAgent;
|
|
264
300
|
env?: NodeJS.ProcessEnv;
|
|
265
301
|
}, deps?: LoginDeps): Promise<LoginResult>;
|
package/dist/cli.js
CHANGED
|
@@ -34,6 +34,8 @@ 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";
|
|
38
|
+
import { parseDocument } from "yaml";
|
|
37
39
|
import { loadManifest } from "./index.js";
|
|
38
40
|
import { SHOWLY_HOSTING_SKILL_DIRECTORY, SHOWLY_HOSTING_SKILL_NAME, SHOWLY_LEGACY_SKILL_NAME, } from "./showly-hosting-skill.js";
|
|
39
41
|
/**
|
|
@@ -83,20 +85,22 @@ function usage() {
|
|
|
83
85
|
"",
|
|
84
86
|
"Usage:",
|
|
85
87
|
" showly-mcp install --to <claude-code|codex|stdout> [--with-skill]",
|
|
86
|
-
" showly-mcp login [--to <claude-code|codex|stdout>] [--agent <host>] [--print-token]",
|
|
88
|
+
" showly-mcp login [--to <claude-code|codex|openclaw|hermes|stdout>] [--agent <host>] [--print-token]",
|
|
87
89
|
" showly-mcp manifest",
|
|
88
90
|
" showly-mcp --version",
|
|
89
91
|
"",
|
|
90
92
|
"login authorizes this machine without a browser on it: it prints a short",
|
|
91
93
|
"code, you approve it on any device, and the credential lands in your host",
|
|
92
|
-
"config. --to
|
|
94
|
+
"config. --to openclaw and --to hermes safely merge the credential into",
|
|
95
|
+
"those hosts' user config; --to codex prints the SHOWLY_TOKEN export its",
|
|
96
|
+
"config reads,",
|
|
93
97
|
"so login also prints the export line that sets it. --print-token writes",
|
|
94
98
|
"ONLY the token to stdout (everything else goes to stderr) so CI can",
|
|
95
99
|
"capture it without it touching a file. When that token is for another",
|
|
96
100
|
"host, pass --agent <cursor|openclaw|hermes|cline> so My Agents can name it.",
|
|
97
101
|
"",
|
|
98
102
|
"Environment overrides:",
|
|
99
|
-
" 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)",
|
|
100
104
|
" SHOWLY_API_URL full URL to your API (default https://api.showly.ai)",
|
|
101
105
|
"",
|
|
102
106
|
"--with-skill installs a reusable showly-hosting skill for Claude Code or Codex.",
|
|
@@ -309,6 +313,24 @@ function safeReadJson(path) {
|
|
|
309
313
|
return {};
|
|
310
314
|
}
|
|
311
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Read a host's primary config without the install helper's empty fallback.
|
|
318
|
+
* Login is about to add a live credential, so treating malformed JSON as `{}`
|
|
319
|
+
* would overwrite the user's whole Gateway after they had already approved.
|
|
320
|
+
*/
|
|
321
|
+
function readHostJson(path, label) {
|
|
322
|
+
let parsed;
|
|
323
|
+
try {
|
|
324
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
throw new Error(`Could not update ${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
328
|
+
}
|
|
329
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
330
|
+
throw new Error(`Could not update ${label}: the root must be a JSON object.`);
|
|
331
|
+
}
|
|
332
|
+
return parsed;
|
|
333
|
+
}
|
|
312
334
|
/**
|
|
313
335
|
* Write a file that holds (or will hold) a credential, 0600 on creation.
|
|
314
336
|
*
|
|
@@ -466,6 +488,110 @@ export function buildCodexAuthSnippet(opts) {
|
|
|
466
488
|
"",
|
|
467
489
|
].join("\n");
|
|
468
490
|
}
|
|
491
|
+
/** Secrets used by the two remote-agent targets the Hero prompt exercises. */
|
|
492
|
+
export const OPENCLAW_TOKEN_ENV_VAR = "SHOWLY_MCP_TOKEN";
|
|
493
|
+
export const HERMES_TOKEN_ENV_VAR = "MCP_SHOWLY_API_KEY";
|
|
494
|
+
/**
|
|
495
|
+
* Give every OpenClaw credential its own environment variable name.
|
|
496
|
+
*
|
|
497
|
+
* A managed Gateway can keep the old process environment alive while its
|
|
498
|
+
* user config is reloaded. With one fixed SHOWLY_MCP_TOKEN name, that stale
|
|
499
|
+
* process value wins over the freshly-written env.vars value and a successful
|
|
500
|
+
* re-login immediately fails with token_revoked until the whole instance is
|
|
501
|
+
* restarted. The token fingerprint is not a credential, but makes the new
|
|
502
|
+
* config reference a name the old process cannot already contain.
|
|
503
|
+
*/
|
|
504
|
+
export function openClawTokenEnvVar(token) {
|
|
505
|
+
const fingerprint = createHash("sha256")
|
|
506
|
+
.update(token)
|
|
507
|
+
.digest("hex")
|
|
508
|
+
.slice(0, 12)
|
|
509
|
+
.toUpperCase();
|
|
510
|
+
return `${OPENCLAW_TOKEN_ENV_VAR}_${fingerprint}`;
|
|
511
|
+
}
|
|
512
|
+
function asRecord(value) {
|
|
513
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
514
|
+
? value
|
|
515
|
+
: {};
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Merge Showly into OpenClaw's user config without disturbing other Gateway
|
|
519
|
+
* settings. The token lives in the Gateway-owned env.vars store; the MCP entry
|
|
520
|
+
* contains only an environment placeholder. `auth: oauth` is removed because
|
|
521
|
+
* header auth and native OAuth are alternative modes in OpenClaw.
|
|
522
|
+
*/
|
|
523
|
+
export function buildOpenClawAuthConfig(opts) {
|
|
524
|
+
const env = asRecord(opts.existing.env);
|
|
525
|
+
const vars = asRecord(env.vars);
|
|
526
|
+
const mcp = asRecord(opts.existing.mcp);
|
|
527
|
+
const servers = asRecord(mcp.servers);
|
|
528
|
+
const currentShowly = asRecord(servers.showly);
|
|
529
|
+
const currentHeaders = asRecord(currentShowly.headers);
|
|
530
|
+
const { auth: _nativeOauth, ...showlyWithoutOauth } = currentShowly;
|
|
531
|
+
const tokenEnvVar = openClawTokenEnvVar(opts.token);
|
|
532
|
+
const varsWithoutOldShowlyTokens = Object.fromEntries(Object.entries(vars).filter(([name]) => name !== OPENCLAW_TOKEN_ENV_VAR &&
|
|
533
|
+
!name.startsWith(`${OPENCLAW_TOKEN_ENV_VAR}_`)));
|
|
534
|
+
return {
|
|
535
|
+
...opts.existing,
|
|
536
|
+
env: {
|
|
537
|
+
...env,
|
|
538
|
+
vars: { ...varsWithoutOldShowlyTokens, [tokenEnvVar]: opts.token },
|
|
539
|
+
},
|
|
540
|
+
mcp: {
|
|
541
|
+
...mcp,
|
|
542
|
+
servers: {
|
|
543
|
+
...servers,
|
|
544
|
+
showly: {
|
|
545
|
+
...showlyWithoutOauth,
|
|
546
|
+
url: opts.url,
|
|
547
|
+
transport: "streamable-http",
|
|
548
|
+
headers: {
|
|
549
|
+
...currentHeaders,
|
|
550
|
+
Authorization: `Bearer \${${tokenEnvVar}}`,
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
/** Replace one dotenv assignment, remove duplicates, and preserve other lines. */
|
|
558
|
+
export function mergeDotEnvCredential(existing, name, token) {
|
|
559
|
+
const assignment = `${name}=${token}`;
|
|
560
|
+
const matcher = new RegExp(`^(?:export\\s+)?${name}=`);
|
|
561
|
+
const lines = existing.split(/\r?\n/);
|
|
562
|
+
if (lines.at(-1) === "")
|
|
563
|
+
lines.pop();
|
|
564
|
+
const merged = [];
|
|
565
|
+
let wrote = false;
|
|
566
|
+
for (const line of lines) {
|
|
567
|
+
if (!matcher.test(line)) {
|
|
568
|
+
merged.push(line);
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
if (!wrote)
|
|
572
|
+
merged.push(assignment);
|
|
573
|
+
wrote = true;
|
|
574
|
+
}
|
|
575
|
+
if (!wrote)
|
|
576
|
+
merged.push(assignment);
|
|
577
|
+
return `${merged.join("\n")}\n`;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Update Hermes YAML through a real YAML document rather than `hermes config
|
|
581
|
+
* set`, whose secret masking corrupts the literal ${MCP_SHOWLY_API_KEY}
|
|
582
|
+
* placeholder. Parse errors are terminal: never replace a user's unreadable
|
|
583
|
+
* config with a guessed one.
|
|
584
|
+
*/
|
|
585
|
+
export function buildHermesAuthConfig(existing, url) {
|
|
586
|
+
const doc = parseDocument(existing.trim().length > 0 ? existing : "{}\n");
|
|
587
|
+
if (doc.errors.length > 0) {
|
|
588
|
+
throw new Error(`Could not update ~/.hermes/config.yaml: ${doc.errors[0].message}`);
|
|
589
|
+
}
|
|
590
|
+
doc.setIn(["mcp_servers", "showly", "url"], url);
|
|
591
|
+
doc.deleteIn(["mcp_servers", "showly", "auth"]);
|
|
592
|
+
doc.setIn(["mcp_servers", "showly", "headers", "Authorization"], `Bearer \${${HERMES_TOKEN_ENV_VAR}}`);
|
|
593
|
+
return doc.toString({ lineWidth: 0 });
|
|
594
|
+
}
|
|
469
595
|
/**
|
|
470
596
|
* How long ONE HTTP request may take before it is abandoned.
|
|
471
597
|
*
|
|
@@ -763,6 +889,22 @@ export async function performLogin(opts, deps = {}) {
|
|
|
763
889
|
const env = opts.env ?? process.env;
|
|
764
890
|
const { url, apiUrl } = resolveUrls(env);
|
|
765
891
|
const log = deps.log ?? ((line) => console.error(line));
|
|
892
|
+
// Validate destination files before asking a human to approve anything.
|
|
893
|
+
// Re-read them after approval before merging, so a change made during the
|
|
894
|
+
// device wait is preserved too.
|
|
895
|
+
const openClawPath = opts.target === "openclaw"
|
|
896
|
+
? join(homedir(), ".openclaw", "openclaw.json")
|
|
897
|
+
: null;
|
|
898
|
+
if (openClawPath && existsSync(openClawPath)) {
|
|
899
|
+
readHostJson(openClawPath, "~/.openclaw/openclaw.json");
|
|
900
|
+
}
|
|
901
|
+
const hermesDirectory = opts.target === "hermes" ? join(homedir(), ".hermes") : null;
|
|
902
|
+
const hermesConfigPath = hermesDirectory
|
|
903
|
+
? join(hermesDirectory, "config.yaml")
|
|
904
|
+
: null;
|
|
905
|
+
if (hermesConfigPath && existsSync(hermesConfigPath)) {
|
|
906
|
+
buildHermesAuthConfig(readFileSync(hermesConfigPath, "utf8"), url);
|
|
907
|
+
}
|
|
766
908
|
const clientId = loginClientId(opts.target, opts.agent);
|
|
767
909
|
const started = await startDeviceFlow(apiUrl, deps, clientId);
|
|
768
910
|
const expiresAt = new Date(Date.now() + started.expires_in * 1000);
|
|
@@ -854,6 +996,54 @@ export async function performLogin(opts, deps = {}) {
|
|
|
854
996
|
snippet,
|
|
855
997
|
};
|
|
856
998
|
}
|
|
999
|
+
if (opts.target === "openclaw") {
|
|
1000
|
+
const path = openClawPath;
|
|
1001
|
+
const existing = existsSync(path)
|
|
1002
|
+
? readHostJson(path, "~/.openclaw/openclaw.json")
|
|
1003
|
+
: {};
|
|
1004
|
+
const merged = buildOpenClawAuthConfig({
|
|
1005
|
+
existing,
|
|
1006
|
+
url,
|
|
1007
|
+
token: token.access_token,
|
|
1008
|
+
});
|
|
1009
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
1010
|
+
writeCredentialFile(path, JSON.stringify(merged, null, 2) + "\n");
|
|
1011
|
+
return {
|
|
1012
|
+
target: opts.target,
|
|
1013
|
+
token: token.access_token,
|
|
1014
|
+
scope: token.scope,
|
|
1015
|
+
expiresAt: tokenExpiresAt,
|
|
1016
|
+
path,
|
|
1017
|
+
wrote: true,
|
|
1018
|
+
snippet: JSON.stringify(merged, null, 2),
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
if (opts.target === "hermes") {
|
|
1022
|
+
const directory = hermesDirectory;
|
|
1023
|
+
const path = hermesConfigPath;
|
|
1024
|
+
const credentialPath = join(directory, ".env");
|
|
1025
|
+
const existingConfig = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
1026
|
+
const existingEnv = existsSync(credentialPath)
|
|
1027
|
+
? readFileSync(credentialPath, "utf8")
|
|
1028
|
+
: "";
|
|
1029
|
+
// Build both outputs before touching either file. In particular, malformed
|
|
1030
|
+
// YAML must not leave a fresh secret behind beside an unchanged config.
|
|
1031
|
+
const config = buildHermesAuthConfig(existingConfig, url);
|
|
1032
|
+
const dotenv = mergeDotEnvCredential(existingEnv, HERMES_TOKEN_ENV_VAR, token.access_token);
|
|
1033
|
+
mkdirSync(directory, { recursive: true });
|
|
1034
|
+
writeCredentialFile(credentialPath, dotenv);
|
|
1035
|
+
writeCredentialFile(path, config);
|
|
1036
|
+
return {
|
|
1037
|
+
target: opts.target,
|
|
1038
|
+
token: token.access_token,
|
|
1039
|
+
scope: token.scope,
|
|
1040
|
+
expiresAt: tokenExpiresAt,
|
|
1041
|
+
path,
|
|
1042
|
+
credentialPath,
|
|
1043
|
+
wrote: true,
|
|
1044
|
+
snippet: config,
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
857
1047
|
return {
|
|
858
1048
|
target: opts.target,
|
|
859
1049
|
token: token.access_token,
|
|
@@ -916,7 +1106,10 @@ export function buildLoginOutput(result, opts = {}) {
|
|
|
916
1106
|
return [{ stream: "out", line: result.token }];
|
|
917
1107
|
const lines = [];
|
|
918
1108
|
if (result.wrote) {
|
|
919
|
-
|
|
1109
|
+
const written = [result.path, result.credentialPath]
|
|
1110
|
+
.filter((path) => Boolean(path))
|
|
1111
|
+
.join(" and ");
|
|
1112
|
+
lines.push({ stream: "err", line: `Connected. Wrote ${written}` });
|
|
920
1113
|
}
|
|
921
1114
|
else if (result.path) {
|
|
922
1115
|
lines.push({
|
|
@@ -931,7 +1124,15 @@ export function buildLoginOutput(result, opts = {}) {
|
|
|
931
1124
|
});
|
|
932
1125
|
lines.push({ stream: "out", line: result.snippet });
|
|
933
1126
|
}
|
|
934
|
-
|
|
1127
|
+
// OpenClaw and Hermes have already received the value in their protected
|
|
1128
|
+
// user-level credential store. Printing an export for them is both false
|
|
1129
|
+
// (the variable is set) and needlessly exposes the token in terminal/chat
|
|
1130
|
+
// logs. Codex and stdout are the two handoff targets whose config really
|
|
1131
|
+
// does depend on the human setting the referenced environment variable.
|
|
1132
|
+
const handoffEnvVars = result.target === "codex" || result.target === "stdout"
|
|
1133
|
+
? envVarsReferencedBy(result.snippet)
|
|
1134
|
+
: [];
|
|
1135
|
+
for (const name of handoffEnvVars) {
|
|
935
1136
|
lines.push({
|
|
936
1137
|
stream: "err",
|
|
937
1138
|
line: `That config reads the token from ${name}, and nothing sets it yet. Put this in the environment your agent starts in:`,
|
|
@@ -1029,12 +1230,13 @@ export function parseCommandArgs(command, rest) {
|
|
|
1029
1230
|
}
|
|
1030
1231
|
return { ok: true, help, flags, values };
|
|
1031
1232
|
}
|
|
1032
|
-
function
|
|
1233
|
+
function parseLoginTarget(parsed, fallback) {
|
|
1033
1234
|
const value = parsed.values.get("--to");
|
|
1034
1235
|
if (value === undefined)
|
|
1035
1236
|
return fallback;
|
|
1036
|
-
if (!["claude-code", "codex", "stdout"].includes(value))
|
|
1237
|
+
if (!["claude-code", "codex", "openclaw", "hermes", "stdout"].includes(value)) {
|
|
1037
1238
|
return null;
|
|
1239
|
+
}
|
|
1038
1240
|
return value;
|
|
1039
1241
|
}
|
|
1040
1242
|
function parseLoginAgent(parsed) {
|
|
@@ -1098,9 +1300,9 @@ export async function runCli(argv, io = consoleIo, env = process.env) {
|
|
|
1098
1300
|
if (command === "login") {
|
|
1099
1301
|
// --to defaults to stdout: printing a snippet can never corrupt a config
|
|
1100
1302
|
// file the user did not ask us to touch.
|
|
1101
|
-
const target =
|
|
1303
|
+
const target = parseLoginTarget(parsed, "stdout");
|
|
1102
1304
|
if (!target) {
|
|
1103
|
-
io.err("login: --to must be claude-code, codex or stdout");
|
|
1305
|
+
io.err("login: --to must be claude-code, codex, openclaw, hermes or stdout");
|
|
1104
1306
|
return 2;
|
|
1105
1307
|
}
|
|
1106
1308
|
const agent = parseLoginAgent(parsed);
|
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.
|
|
5
|
+
"version": "0.4.4",
|
|
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
|
+
"version": "0.4.4",
|
|
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.
|
|
55
|
+
"version": "0.4.4",
|
|
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,10 +65,13 @@
|
|
|
65
65
|
},
|
|
66
66
|
"codex-plugin": {
|
|
67
67
|
"name": "showly",
|
|
68
|
-
"version": "0.4.
|
|
68
|
+
"version": "0.4.4",
|
|
69
69
|
"type": "mcp-server",
|
|
70
70
|
"manifest": "manifest.json"
|
|
71
71
|
},
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"yaml": "^2.9.0"
|
|
74
|
+
},
|
|
72
75
|
"devDependencies": {
|
|
73
76
|
"@showly/eslint-config": "workspace:^",
|
|
74
77
|
"@types/node": "^26.2.0",
|