@showly/mcp-server 0.1.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/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # @showly/mcp-server
2
+
3
+ Connect Claude Code or Codex to the [Showly](https://showly.ai) MCP server, so
4
+ you can preview and deploy a site directly from your terminal AI.
5
+
6
+ ## Quickstart
7
+
8
+ ```sh
9
+ npx @showly/mcp-server install --to claude-code # or --to codex
10
+ ```
11
+
12
+ The installer writes the Showly MCP server block to `~/.claude.json`
13
+ (or `~/.codex/config.toml`). No token to paste: the next time you call a Showly
14
+ tool, your agent discovers Showly's OAuth authorization server from the
15
+ endpoint and opens a browser tab for you to sign in and approve the scopes
16
+ (standard MCP authorization).
17
+
18
+ ## What you get
19
+
20
+ Read-only tools available to any token:
21
+
22
+ - `list_projects`, `list_sites`, `get_site_context`, `list_templates`
23
+ - `get_preview_status`, `get_deployment_logs`, `diagnose_deployment`
24
+
25
+ Write tools that stage and materialise previews:
26
+
27
+ - `create_change_plan` — turn a natural-language request into a plan
28
+ - `apply_site_patch` — stage one or more file edits as a _changeset_
29
+ - `create_preview` — build the changeset and return a real preview URL
30
+ - `retry_deployment` — re-run a failed preview build
31
+ - `run_checks` — read the lint / typecheck / build status of a preview
32
+ - `create_site_from_template` — bootstrap a new site from a template
33
+ - `request_publish` — open a publish approval (completed in the web UI)
34
+
35
+ Production paths (`publish_site`, `rollback_deployment`) are deliberately
36
+ not exposed via MCP at all — they don't appear in `tools/list`. The agent's
37
+ path to production is `request_publish`, whose success response includes a
38
+ `webApprovalUrl` deep link; the user opens that link to complete approval
39
+ and step-up MFA in the Showly web UI.
40
+
41
+ ## Environment
42
+
43
+ | Variable | Default | What it controls |
44
+ | ---------------- | ----------------------- | ---------------------- |
45
+ | `SHOWLY_MCP_URL` | `https://mcp.showly.ai` | MCP transport endpoint |
46
+ | `SHOWLY_API_URL` | `https://api.showly.ai` | OAuth + API base |
47
+
48
+ ## Without an installer
49
+
50
+ If your agent isn't supported by the installer, run:
51
+
52
+ ```sh
53
+ npx @showly/mcp-server install --to stdout
54
+ ```
55
+
56
+ and paste the snippet into the agent's configuration manually. The manifest
57
+ that drives both targets is also accessible programmatically:
58
+
59
+ ```ts
60
+ import { manifest } from "@showly/mcp-server";
61
+ console.log(manifest.tools.map((t) => t.name));
62
+ ```
63
+
64
+ ## Troubleshooting
65
+
66
+ **Codex doesn't list the showly tools.** Older Codex CLI versions only reach
67
+ remote (streamable HTTP) MCP servers when the rmcp client is enabled. Either
68
+ update Codex, or add to `~/.codex/config.toml`:
69
+
70
+ ```toml
71
+ [features]
72
+ experimental_use_rmcp_client = true
73
+ ```
74
+
75
+ (Current Codex versions need no flag — a bare `url` under
76
+ `[mcp_servers.showly]` is the whole config.)
77
+
78
+ ## License
79
+
80
+ Proprietary — © Showly. This package is distributed for use with the Showly
81
+ hosting service; it is not open source. See https://showly.ai/terms.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ type Target = "claude-code" | "codex" | "stdout";
3
+ export declare function buildClaudeCodeSnippet(opts: {
4
+ url: string;
5
+ apiUrl?: string;
6
+ }): Record<string, unknown>;
7
+ export declare function buildCodexSnippet(opts: {
8
+ url: string;
9
+ apiUrl?: string;
10
+ }): string;
11
+ export declare function resolveUrls(env?: NodeJS.ProcessEnv): {
12
+ url: string;
13
+ apiUrl: string;
14
+ };
15
+ export type InstallResult = {
16
+ target: Target;
17
+ path: string | null;
18
+ snippet: string;
19
+ wrote: boolean;
20
+ alreadyConfigured: boolean;
21
+ };
22
+ export declare function performInstall(target: Target, env?: NodeJS.ProcessEnv): InstallResult;
23
+ /**
24
+ * True when this module is the program entrypoint.
25
+ *
26
+ * Must handle the `npm bin` case: an installed package exposes the CLI as
27
+ * `node_modules/.bin/showly-mcp`, a symlink to `dist/cli.js`. Node sets
28
+ * `process.argv[1]` to the symlink path (`…/.bin/showly-mcp`), while
29
+ * `import.meta.url` is the real `…/dist/cli.js`. The old guard compared the
30
+ * two strings (and looked for a `cli.js` suffix on argv[1]) so it silently
31
+ * failed through the symlink — `showly-mcp <cmd>` exited 0 with no output.
32
+ *
33
+ * Resolve argv[1] to its real path before comparing, so the symlink and the
34
+ * target reconcile. Exported for unit testing (the bin path can't be
35
+ * exercised from an in-process test runner otherwise).
36
+ */
37
+ export declare function isMainModule(argv1: string | undefined, metaUrl: string): boolean;
38
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env node
2
+ // packages/showly-mcp-server/src/cli.ts — install helper.
3
+ //
4
+ // Usage:
5
+ // showly-mcp install --to claude-code # writes ~/.claude.json
6
+ // showly-mcp install --to codex # writes ~/.codex/config.toml
7
+ // showly-mcp install --to stdout # prints the snippet for manual paste
8
+ // showly-mcp manifest # prints manifest.json
9
+ // showly-mcp --help
10
+ //
11
+ // The CLI only writes the MCP server config block — it doesn't touch network.
12
+ // The agent discovers OAuth from the server and runs the browser sign-in on
13
+ // first use (standard MCP authorization).
14
+ import { readFileSync, mkdirSync, writeFileSync, existsSync, realpathSync, } from "node:fs";
15
+ import { dirname, join } from "node:path";
16
+ import { homedir } from "node:os";
17
+ import { pathToFileURL } from "node:url";
18
+ import { loadManifest } from "./index.js";
19
+ function usage() {
20
+ return [
21
+ "Showly MCP server installer",
22
+ "",
23
+ "Usage:",
24
+ " showly-mcp install --to <claude-code|codex|stdout>",
25
+ " showly-mcp manifest",
26
+ "",
27
+ "Environment overrides:",
28
+ " SHOWLY_MCP_URL full URL to your MCP endpoint (default https://mcp.showly.ai)",
29
+ " SHOWLY_API_URL full URL to your API (default https://api.showly.ai)",
30
+ ].join("\n");
31
+ }
32
+ // The MCP server config is intentionally MINIMAL: just the transport + URL.
33
+ // OAuth is NOT configured inline — a standards-compliant MCP client (Claude
34
+ // Code / Codex) discovers it from the server: it connects, gets 401 +
35
+ // WWW-Authenticate, fetches the server's protected-resource metadata, then the
36
+ // authorization-server metadata, dynamically registers, and runs
37
+ // authorization_code + PKCE in the browser. There is no client-readable
38
+ // `oauth` field in the host config schema, so emitting one (as a prior version
39
+ // did) was a no-op that misled rather than helped. `apiUrl` is retained in the
40
+ // signature for the stdout help text + the device-flow fallback docs.
41
+ export function buildClaudeCodeSnippet(opts) {
42
+ return {
43
+ mcpServers: {
44
+ showly: {
45
+ type: "http",
46
+ url: opts.url,
47
+ },
48
+ },
49
+ };
50
+ }
51
+ // Codex's HTTP MCP config schema has no `type` key — a bare `url` is what
52
+ // selects the streamable-HTTP transport (stdio servers use `command` instead).
53
+ // Codex's TOML deserialization rejects unknown keys, so emitting one risks
54
+ // breaking the whole config file, not just our entry. Codex versions that
55
+ // predate the rmcp client need `[features] experimental_use_rmcp_client =
56
+ // true` on top — documented in the README rather than written here, so we
57
+ // never inject a duplicate [features] table into a user's config.
58
+ export function buildCodexSnippet(opts) {
59
+ return [
60
+ "# Added by @showly/mcp-server install",
61
+ "",
62
+ "[mcp_servers.showly]",
63
+ `url = "${opts.url}"`,
64
+ "",
65
+ ].join("\n");
66
+ }
67
+ export function resolveUrls(env = process.env) {
68
+ const manifest = loadManifest();
69
+ return {
70
+ url: env[manifest.mcp.endpoints.url_env] ?? manifest.mcp.endpoints.default_url,
71
+ apiUrl: env[manifest.mcp.endpoints.api_url_env] ?? "https://api.showly.ai",
72
+ };
73
+ }
74
+ export function performInstall(target, env = process.env) {
75
+ const { url, apiUrl } = resolveUrls(env);
76
+ if (target === "stdout") {
77
+ const obj = buildClaudeCodeSnippet({ url, apiUrl });
78
+ return {
79
+ target,
80
+ path: null,
81
+ snippet: "# claude-code (~/.claude.json):\n" +
82
+ JSON.stringify(obj, null, 2) +
83
+ "\n\n# codex (~/.codex/config.toml):\n" +
84
+ buildCodexSnippet({ url, apiUrl }),
85
+ wrote: false,
86
+ alreadyConfigured: false,
87
+ };
88
+ }
89
+ if (target === "claude-code") {
90
+ // User-scope MCP servers live in ~/.claude.json (top-level `mcpServers`),
91
+ // NOT ~/.claude/settings.json — Claude Code never reads mcpServers from
92
+ // settings.json, so a prior version of this installer wrote a block that
93
+ // was silently ignored.
94
+ const path = join(homedir(), ".claude.json");
95
+ const existing = existsSync(path)
96
+ ? safeReadJson(path)
97
+ : { mcpServers: {} };
98
+ const before = JSON.stringify(existing);
99
+ const snippet = buildClaudeCodeSnippet({ url, apiUrl });
100
+ const merged = {
101
+ ...existing,
102
+ mcpServers: {
103
+ ...(typeof existing.mcpServers === "object" && existing.mcpServers
104
+ ? existing.mcpServers
105
+ : {}),
106
+ showly: snippet.mcpServers.showly,
107
+ },
108
+ };
109
+ const after = JSON.stringify(merged);
110
+ if (before === after) {
111
+ return {
112
+ target,
113
+ path,
114
+ snippet: JSON.stringify(merged, null, 2),
115
+ wrote: false,
116
+ alreadyConfigured: true,
117
+ };
118
+ }
119
+ mkdirSync(dirname(path), { recursive: true });
120
+ writeFileSync(path, JSON.stringify(merged, null, 2) + "\n", "utf8");
121
+ return {
122
+ target,
123
+ path,
124
+ snippet: JSON.stringify(merged, null, 2),
125
+ wrote: true,
126
+ alreadyConfigured: false,
127
+ };
128
+ }
129
+ // codex
130
+ const path = join(homedir(), ".codex", "config.toml");
131
+ const snippet = buildCodexSnippet({ url, apiUrl });
132
+ const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
133
+ if (existing.includes("[mcp_servers.showly]")) {
134
+ return {
135
+ target,
136
+ path,
137
+ snippet,
138
+ wrote: false,
139
+ alreadyConfigured: true,
140
+ };
141
+ }
142
+ mkdirSync(dirname(path), { recursive: true });
143
+ writeFileSync(path, existing.length > 0 && !existing.endsWith("\n")
144
+ ? `${existing}\n${snippet}`
145
+ : `${existing}${snippet}`, "utf8");
146
+ return {
147
+ target,
148
+ path,
149
+ snippet,
150
+ wrote: true,
151
+ alreadyConfigured: false,
152
+ };
153
+ }
154
+ function safeReadJson(path) {
155
+ try {
156
+ return JSON.parse(readFileSync(path, "utf8"));
157
+ }
158
+ catch {
159
+ return {};
160
+ }
161
+ }
162
+ function main(argv) {
163
+ const [cmd, ...rest] = argv.slice(2);
164
+ if (!cmd || cmd === "--help" || cmd === "-h") {
165
+ console.log(usage());
166
+ return;
167
+ }
168
+ if (cmd === "manifest") {
169
+ console.log(JSON.stringify(loadManifest(), null, 2));
170
+ return;
171
+ }
172
+ if (cmd === "install") {
173
+ const toIdx = rest.indexOf("--to");
174
+ if (toIdx === -1 || !rest[toIdx + 1]) {
175
+ console.error("install: --to <target> is required");
176
+ process.exitCode = 2;
177
+ console.error(usage());
178
+ return;
179
+ }
180
+ const target = rest[toIdx + 1];
181
+ if (!["claude-code", "codex", "stdout"].includes(target)) {
182
+ console.error(`install: unknown target "${target}"`);
183
+ process.exitCode = 2;
184
+ return;
185
+ }
186
+ const result = performInstall(target);
187
+ if (target === "stdout") {
188
+ console.log(result.snippet);
189
+ }
190
+ else if (result.alreadyConfigured) {
191
+ console.log(`Already configured at ${result.path}`);
192
+ }
193
+ else {
194
+ console.log(`Wrote ${result.path}`);
195
+ console.log("");
196
+ console.log("Next: open Claude Code / Codex and run any read tool (e.g. list_sites).");
197
+ console.log("The agent will pop a browser tab for you to authorize the connection.");
198
+ }
199
+ return;
200
+ }
201
+ console.error(`unknown command: ${cmd}`);
202
+ process.exitCode = 2;
203
+ console.error(usage());
204
+ }
205
+ /**
206
+ * True when this module is the program entrypoint.
207
+ *
208
+ * Must handle the `npm bin` case: an installed package exposes the CLI as
209
+ * `node_modules/.bin/showly-mcp`, a symlink to `dist/cli.js`. Node sets
210
+ * `process.argv[1]` to the symlink path (`…/.bin/showly-mcp`), while
211
+ * `import.meta.url` is the real `…/dist/cli.js`. The old guard compared the
212
+ * two strings (and looked for a `cli.js` suffix on argv[1]) so it silently
213
+ * failed through the symlink — `showly-mcp <cmd>` exited 0 with no output.
214
+ *
215
+ * Resolve argv[1] to its real path before comparing, so the symlink and the
216
+ * target reconcile. Exported for unit testing (the bin path can't be
217
+ * exercised from an in-process test runner otherwise).
218
+ */
219
+ export function isMainModule(argv1, metaUrl) {
220
+ if (!argv1)
221
+ return false;
222
+ try {
223
+ return pathToFileURL(realpathSync(argv1)).href === metaUrl;
224
+ }
225
+ catch {
226
+ return false;
227
+ }
228
+ }
229
+ if (isMainModule(process.argv[1], import.meta.url)) {
230
+ main(process.argv);
231
+ }
@@ -0,0 +1,10 @@
1
+ import type { SkillManifest, ToolManifestEntry } from "./manifest-types.js";
2
+ export type { SkillManifest, ToolManifestEntry, ToolKind, } from "./manifest-types.js";
3
+ export declare function loadManifest(): SkillManifest;
4
+ /** Sync read of the manifest; throws if file missing or malformed. */
5
+ export declare const manifest: SkillManifest;
6
+ /** Tool names safe to call directly from an MCP-origin token. */
7
+ export declare const INVOKABLE_TOOLS: readonly string[];
8
+ /** Tool names that are intentionally blocked at the wrapper layer. */
9
+ export declare const MCP_BLOCKED_TOOLS: readonly string[];
10
+ export declare function getToolEntry(name: string): ToolManifestEntry | undefined;
package/dist/index.js ADDED
@@ -0,0 +1,34 @@
1
+ // packages/showly-mcp-server/src/index.ts — programmatic API.
2
+ //
3
+ // Consumers (mostly tests + integration code) can do:
4
+ //
5
+ // import { loadManifest, manifest, INVOKABLE_TOOLS } from "@showly/mcp-server";
6
+ //
7
+ // The Claude Code skill loader and the Codex plugin loader both
8
+ // resolve manifest.json directly from their package.json fields; the
9
+ // JS surface here is for programmatic clients (tests, integrators)
10
+ // that want a typed view.
11
+ import { readFileSync } from "node:fs";
12
+ import { dirname, resolve } from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ const HERE = dirname(fileURLToPath(import.meta.url));
15
+ // We ship manifest.json at the package root, so from dist/index.js
16
+ // it lives one level up.
17
+ const MANIFEST_PATH = resolve(HERE, "..", "manifest.json");
18
+ let cached = null;
19
+ export function loadManifest() {
20
+ if (cached)
21
+ return cached;
22
+ const raw = readFileSync(MANIFEST_PATH, "utf8");
23
+ cached = JSON.parse(raw);
24
+ return cached;
25
+ }
26
+ /** Sync read of the manifest; throws if file missing or malformed. */
27
+ export const manifest = loadManifest();
28
+ /** Tool names safe to call directly from an MCP-origin token. */
29
+ export const INVOKABLE_TOOLS = Object.freeze(manifest.tools.filter((t) => !t.mcp_origin_blocked).map((t) => t.name));
30
+ /** Tool names that are intentionally blocked at the wrapper layer. */
31
+ export const MCP_BLOCKED_TOOLS = Object.freeze(manifest.tools.filter((t) => t.mcp_origin_blocked).map((t) => t.name));
32
+ export function getToolEntry(name) {
33
+ return manifest.tools.find((t) => t.name === name);
34
+ }
@@ -0,0 +1,34 @@
1
+ export type ToolKind = "read" | "write" | "production";
2
+ export type ToolManifestEntry = {
3
+ name: string;
4
+ kind: ToolKind;
5
+ scopes: string[];
6
+ mcp_origin_blocked?: boolean;
7
+ note?: string;
8
+ };
9
+ export type SkillManifest = {
10
+ name: string;
11
+ displayName: string;
12
+ version: string;
13
+ description: string;
14
+ homepage: string;
15
+ publisher: string;
16
+ mcp: {
17
+ transport: "streamable-http" | "stdio";
18
+ auth: {
19
+ kind: "oauth" | "oauth-device-flow" | "api-token";
20
+ /** Scopes the client requests by default at consent time. */
21
+ default_scopes: string[];
22
+ };
23
+ endpoints: {
24
+ /** Env var that overrides the MCP server URL. */
25
+ url_env: string;
26
+ /** Default MCP server URL (the resource a client connects to). */
27
+ default_url: string;
28
+ /** Env var that overrides the API base (device-flow fallback + docs). */
29
+ api_url_env: string;
30
+ };
31
+ };
32
+ tools: ToolManifestEntry[];
33
+ quickstart: string[];
34
+ };
@@ -0,0 +1,6 @@
1
+ // packages/showly-mcp-server/src/manifest-types.ts — shape of manifest.json.
2
+ //
3
+ // The manifest is the contract between the package and the host agent
4
+ // (Claude Code, Codex, future plugins). Bumping a field requires a
5
+ // minor version bump and a SDK release.
6
+ export {};
package/manifest.json ADDED
@@ -0,0 +1,87 @@
1
+ {
2
+ "$schema": "https://showly.ai/schemas/skill-manifest-v1.json",
3
+ "name": "showly",
4
+ "displayName": "Showly",
5
+ "version": "0.1.0",
6
+ "description": "Deploy and manage Showly sites from inside Claude Code / Codex.",
7
+ "homepage": "https://showly.ai/docs/skills",
8
+ "publisher": "Showly",
9
+ "mcp": {
10
+ "transport": "streamable-http",
11
+ "auth": {
12
+ "kind": "oauth",
13
+ "default_scopes": [
14
+ "project:read",
15
+ "site:read",
16
+ "site:write",
17
+ "preview:read",
18
+ "preview:create",
19
+ "checks:run",
20
+ "publish:request",
21
+ "logs:read",
22
+ "template:read",
23
+ "template:create"
24
+ ]
25
+ },
26
+ "endpoints": {
27
+ "url_env": "SHOWLY_MCP_URL",
28
+ "default_url": "https://mcp.showly.ai",
29
+ "api_url_env": "SHOWLY_API_URL"
30
+ }
31
+ },
32
+ "tools": [
33
+ { "name": "list_projects", "kind": "read", "scopes": ["project:read"] },
34
+ { "name": "list_sites", "kind": "read", "scopes": ["site:read"] },
35
+ { "name": "get_site_context", "kind": "read", "scopes": ["site:read"] },
36
+ {
37
+ "name": "get_preview_status",
38
+ "kind": "read",
39
+ "scopes": ["preview:read"]
40
+ },
41
+ { "name": "get_deployment_logs", "kind": "read", "scopes": ["logs:read"] },
42
+ {
43
+ "name": "diagnose_deployment",
44
+ "kind": "read",
45
+ "scopes": ["logs:read"]
46
+ },
47
+ { "name": "list_templates", "kind": "read", "scopes": ["template:read"] },
48
+ { "name": "create_change_plan", "kind": "write", "scopes": ["site:read"] },
49
+ { "name": "apply_site_patch", "kind": "write", "scopes": ["site:write"] },
50
+ { "name": "create_preview", "kind": "write", "scopes": ["preview:create"] },
51
+ {
52
+ "name": "retry_deployment",
53
+ "kind": "write",
54
+ "scopes": ["preview:create"]
55
+ },
56
+ { "name": "run_checks", "kind": "write", "scopes": ["checks:run"] },
57
+ {
58
+ "name": "request_publish",
59
+ "kind": "write",
60
+ "scopes": ["publish:request"]
61
+ },
62
+ {
63
+ "name": "create_site_from_template",
64
+ "kind": "write",
65
+ "scopes": ["template:create", "site:write"]
66
+ },
67
+ {
68
+ "name": "publish_site",
69
+ "kind": "production",
70
+ "scopes": ["production:deploy"],
71
+ "mcp_origin_blocked": true,
72
+ "note": "Production-side deploy. MCP-origin calls are rejected; trigger from the web UI with step-up MFA + approval."
73
+ },
74
+ {
75
+ "name": "rollback_deployment",
76
+ "kind": "production",
77
+ "scopes": ["production:rollback"],
78
+ "mcp_origin_blocked": true,
79
+ "note": "Production-side rollback. Same constraint as publish_site."
80
+ }
81
+ ],
82
+ "quickstart": [
83
+ "Install: npx @showly/mcp-server install --to claude-code",
84
+ "Authorize: run any read tool — your agent opens the consent URL in your browser.",
85
+ "Demo: ask the agent to 'preview a one-line H1 change' on a site you own."
86
+ ]
87
+ }
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@showly/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "Connect Claude Code / Codex to the Showly MCP server — preview and deploy sites from your agent.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "bin": {
10
+ "showly-mcp": "./dist/cli.js"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./manifest": "./manifest.json",
18
+ "./package.json": "./package.json"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "manifest.json",
23
+ "README.md"
24
+ ],
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "scripts": {
32
+ "build": "tsc -p tsconfig.json",
33
+ "build:publish": "tsc -p tsconfig.json --declarationMap false",
34
+ "lint": "eslint .",
35
+ "test": "tsx --test \"src/**/*.test.ts\" \"src/*.test.ts\"",
36
+ "typecheck": "tsc -p tsconfig.json --noEmit",
37
+ "prepublishOnly": "rm -rf dist && pnpm run build:publish"
38
+ },
39
+ "keywords": [
40
+ "claude-code",
41
+ "codex",
42
+ "mcp",
43
+ "mcp-server",
44
+ "showly",
45
+ "hosting",
46
+ "agent"
47
+ ],
48
+ "homepage": "https://showly.ai/docs/mcp",
49
+ "bugs": {
50
+ "url": "https://showly.ai/support"
51
+ },
52
+ "claude-code-skill": {
53
+ "name": "showly",
54
+ "version": "0.1.0",
55
+ "description": "Deploy and manage Showly sites from inside Claude Code.",
56
+ "mcp-server": {
57
+ "url-env": "SHOWLY_MCP_URL",
58
+ "default-url": "https://mcp.showly.ai",
59
+ "auth": "oauth",
60
+ "api-url-env": "SHOWLY_API_URL"
61
+ },
62
+ "tools-prefix": "showly",
63
+ "manifest": "manifest.json"
64
+ },
65
+ "codex-plugin": {
66
+ "name": "showly",
67
+ "version": "0.1.0",
68
+ "type": "mcp-server",
69
+ "manifest": "manifest.json"
70
+ },
71
+ "devDependencies": {
72
+ "@showly/eslint-config": "workspace:^",
73
+ "@types/node": "^25.9.1",
74
+ "eslint": "^9.18.0",
75
+ "tsx": "^4.22.4",
76
+ "typescript": "^6.0.3"
77
+ }
78
+ }