@shippp/mcp 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,38 @@
1
+ # @shippp/mcp
2
+
3
+ Pull production-perfect Chakra UI code from your Shippp design files straight into your IDE agent (Claude Code, Cursor, …).
4
+
5
+ ## Setup
6
+
7
+ 1. Create an API key in your Shippp workspace settings. It's shown once — copy it.
8
+ 2. Add the server to your IDE's MCP config:
9
+
10
+ ```json
11
+ {
12
+ "mcpServers": {
13
+ "shippp": {
14
+ "command": "npx",
15
+ "args": ["-y", "@shippp/mcp"],
16
+ "env": {
17
+ "SHIPPP_API_KEY": "sk_...",
18
+ "SHIPPP_API_URL": "https://app.shippp.ai"
19
+ }
20
+ }
21
+ }
22
+ }
23
+ ```
24
+
25
+ `SHIPPP_API_URL` is optional (defaults to the Shippp production URL).
26
+
27
+ ## Tools
28
+
29
+ | Tool | Returns |
30
+ |---|---|
31
+ | `shippp_list_units` | The file's screens + components (id + name). Call first to discover ids. |
32
+ | `shippp_get_component` | One component's Chakra `.tsx`. |
33
+ | `shippp_get_tokens` | The design tokens as a Chakra theme. |
34
+ | `shippp_get_design_system` | The whole design system as a package. |
35
+ | `shippp_get_project` | The whole file as a runnable Next.js project. |
36
+ | `shippp_diff` | Your current code vs a fresh generation. |
37
+
38
+ Read-only. The server never modifies your design.
package/dist/client.js ADDED
@@ -0,0 +1,43 @@
1
+ export class ShipppApiError extends Error {
2
+ status;
3
+ constructor(status, message) {
4
+ super(message);
5
+ this.status = status;
6
+ this.name = "ShipppApiError";
7
+ }
8
+ }
9
+ export class ShipppClient {
10
+ config;
11
+ constructor(config) {
12
+ this.config = config;
13
+ }
14
+ async request(path, init) {
15
+ let res;
16
+ try {
17
+ res = await fetch(`${this.config.apiUrl}${path}`, {
18
+ ...init,
19
+ headers: {
20
+ Authorization: `Bearer ${this.config.apiKey}`,
21
+ "Content-Type": "application/json",
22
+ ...(init.headers ?? {}),
23
+ },
24
+ });
25
+ }
26
+ catch (e) {
27
+ throw new ShipppApiError(0, `Could not reach Shippp at ${this.config.apiUrl}: ${e.message}`);
28
+ }
29
+ const body = (await res.json().catch(() => ({})));
30
+ if (!res.ok)
31
+ throw new ShipppApiError(res.status, body?.error || `HTTP ${res.status}`);
32
+ return body;
33
+ }
34
+ manifest(fileId) {
35
+ return this.request(`/api/mcp/manifest?fileId=${encodeURIComponent(fileId)}`, { method: "GET" });
36
+ }
37
+ generate(fileId, request, baseline) {
38
+ return this.request("/api/mcp/generate", {
39
+ method: "POST",
40
+ body: JSON.stringify(baseline ? { fileId, request, baseline } : { fileId, request }),
41
+ });
42
+ }
43
+ }
package/dist/config.js ADDED
@@ -0,0 +1,11 @@
1
+ const DEFAULT_API_URL = "https://app.shippp.ai";
2
+ export function loadConfig() {
3
+ const apiKey = process.env.SHIPPP_API_KEY?.trim();
4
+ if (!apiKey) {
5
+ console.error("[shippp-mcp] SHIPPP_API_KEY is not set. Mint a key in your Shippp workspace " +
6
+ "settings and add it to the MCP server's `env` in your IDE config.");
7
+ process.exit(1);
8
+ }
9
+ const apiUrl = (process.env.SHIPPP_API_URL?.trim() || DEFAULT_API_URL).replace(/\/+$/, "");
10
+ return { apiUrl, apiKey };
11
+ }
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { loadConfig } from "./config.js";
5
+ import { ShipppClient } from "./client.js";
6
+ import { registerTools } from "./tools.js";
7
+ const INSTRUCTIONS = `Shippp exposes DETERMINISTIC, production-perfect Chakra UI code from a designer's Shippp file. Everything returned is GENERATED (never guessed) and reproducible — the same source always yields the same code + hash (see each result's receipt).
8
+
9
+ Workflow: call shippp_list_units first to discover screen/master ids, then pull code with shippp_get_component / shippp_get_tokens / shippp_get_design_system / shippp_get_project. Write the returned "=== FILE: <path> ===" blocks to those paths. Do NOT hand-edit generated code to reflect a design change — change the design in Shippp and regenerate. Use shippp_diff to see exactly what a designer changed since your current code.`;
10
+ async function main() {
11
+ const config = loadConfig();
12
+ const server = new McpServer({ name: "shippp", version: "0.1.0" }, { instructions: INSTRUCTIONS });
13
+ registerTools(server, new ShipppClient(config));
14
+ await server.connect(new StdioServerTransport());
15
+ console.error(`[shippp-mcp] connected via stdio → ${config.apiUrl}`);
16
+ }
17
+ main().catch((e) => {
18
+ console.error("[shippp-mcp] fatal:", e);
19
+ process.exit(1);
20
+ });
package/dist/tools.js ADDED
@@ -0,0 +1,110 @@
1
+ import { z } from "zod";
2
+ import { ShipppApiError } from "./client.js";
3
+ const text = (s) => ({ content: [{ type: "text", text: s }] });
4
+ const fail = (s) => ({ content: [{ type: "text", text: s }], isError: true });
5
+ function errText(e) {
6
+ if (e instanceof ShipppApiError) {
7
+ const hint = e.status === 401 ? " — check SHIPPP_API_KEY." : "";
8
+ return `Error ${e.status}: ${e.message}${hint}`;
9
+ }
10
+ return `Error: ${e.message}`;
11
+ }
12
+ function renderFiles(result) {
13
+ const files = result.files.map((f) => `=== FILE: ${f.path} ===\n${f.content}`).join("\n\n");
14
+ return `${files}\n\n--- RECEIPT (reproducible: ${result.receipt.reproducible}, engine: ${result.receipt.config.engine}) ---\n${JSON.stringify(result.receipt, null, 2)}`;
15
+ }
16
+ function renderDiff(result) {
17
+ const changed = result.changed_files.map((f) => `${f.status}\t${f.path}`).join("\n");
18
+ return `changed: ${result.changed}\n\n${result.diff || "(no changes)"}\n\n--- files ---\n${changed}\n\n--- RECEIPT ---\n${JSON.stringify(result.receipt, null, 2)}`;
19
+ }
20
+ const fileId = z.string().min(1).describe("The Shippp file id (from the file's URL, or shippp_list_units).");
21
+ export function registerTools(server, client) {
22
+ server.registerTool("shippp_list_units", {
23
+ description: "List a Shippp file's screens and master components (id + name). Call this FIRST to discover the ids for shippp_get_component.",
24
+ inputSchema: { fileId },
25
+ }, async ({ fileId }) => {
26
+ try {
27
+ const { manifest } = await client.manifest(fileId);
28
+ return text(JSON.stringify(manifest, null, 2));
29
+ }
30
+ catch (e) {
31
+ return fail(errText(e));
32
+ }
33
+ });
34
+ server.registerTool("shippp_get_component", {
35
+ description: "Get ONE component's deterministic Chakra UI code + a reproducibility receipt. `unitType` is 'master' (a design-system component) or 'screen' (a depth-0 artboard). Get ids from shippp_list_units.",
36
+ inputSchema: {
37
+ fileId,
38
+ unitId: z.string().min(1).describe("The master/screen id from shippp_list_units."),
39
+ unitType: z.enum(["master", "screen"]),
40
+ },
41
+ }, async ({ fileId, unitId, unitType }) => {
42
+ try {
43
+ return text(renderFiles((await client.generate(fileId, { unit: "component", unitId, unitType }))));
44
+ }
45
+ catch (e) {
46
+ return fail(errText(e));
47
+ }
48
+ });
49
+ server.registerTool("shippp_get_tokens", {
50
+ description: "Get the design system's tokens as a Chakra theme (createSystem) + receipt.",
51
+ inputSchema: { fileId },
52
+ }, async ({ fileId }) => {
53
+ try {
54
+ return text(renderFiles((await client.generate(fileId, { unit: "tokens" }))));
55
+ }
56
+ catch (e) {
57
+ return fail(errText(e));
58
+ }
59
+ });
60
+ server.registerTool("shippp_get_design_system", {
61
+ description: "Get the whole design system as a publishable package (tokens + theme + recipes + every component + stories + package.json).",
62
+ inputSchema: { fileId },
63
+ }, async ({ fileId }) => {
64
+ try {
65
+ return text(renderFiles((await client.generate(fileId, { unit: "design-system" }))));
66
+ }
67
+ catch (e) {
68
+ return fail(errText(e));
69
+ }
70
+ });
71
+ server.registerTool("shippp_get_project", {
72
+ description: "Get the whole file as a runnable Next.js project (screens as pages + the design system).",
73
+ inputSchema: { fileId },
74
+ }, async ({ fileId }) => {
75
+ try {
76
+ return text(renderFiles((await client.generate(fileId, { unit: "project" }))));
77
+ }
78
+ catch (e) {
79
+ return fail(errText(e));
80
+ }
81
+ });
82
+ server.registerTool("shippp_diff", {
83
+ description: "Diff your CURRENT code against a fresh deterministic generation of a unit — shows exactly what a designer changed. Pass the same unit params as the get tools, plus `baseline` (the files you currently have for that unit).",
84
+ inputSchema: {
85
+ fileId,
86
+ unit: z.enum(["tokens", "design-system", "project", "component"]),
87
+ unitId: z.string().optional().describe("Required when unit='component'."),
88
+ unitType: z.enum(["master", "screen"]).optional().describe("Required when unit='component'."),
89
+ baseline: z
90
+ .array(z.object({ path: z.string(), content: z.string() }))
91
+ .describe("Your current files for this unit (path + content)."),
92
+ },
93
+ }, async ({ fileId, unit, unitId, unitType, baseline }) => {
94
+ try {
95
+ let request;
96
+ if (unit === "component") {
97
+ if (!unitId || !unitType)
98
+ return fail("unit='component' requires unitId + unitType.");
99
+ request = { unit, unitId, unitType };
100
+ }
101
+ else {
102
+ request = { unit };
103
+ }
104
+ return text(renderDiff((await client.generate(fileId, request, baseline))));
105
+ }
106
+ catch (e) {
107
+ return fail(errText(e));
108
+ }
109
+ });
110
+ }
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@shippp/mcp",
3
+ "version": "0.1.0",
4
+ "description": "Shippp MCP server — pull deterministic, production-perfect Chakra UI code from your Shippp design files, straight into your IDE agent.",
5
+ "type": "module",
6
+ "bin": {
7
+ "shippp-mcp": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "start": "node dist/index.js",
16
+ "prepublishOnly": "rm -rf dist && npm run build"
17
+ },
18
+ "engines": {
19
+ "node": ">=18"
20
+ },
21
+ "keywords": [
22
+ "mcp",
23
+ "modelcontextprotocol",
24
+ "shippp",
25
+ "chakra-ui",
26
+ "design-to-code"
27
+ ],
28
+ "license": "UNLICENSED",
29
+ "dependencies": {
30
+ "@modelcontextprotocol/sdk": "^1.12.0",
31
+ "zod": "^3.23.8"
32
+ },
33
+ "devDependencies": {
34
+ "typescript": "^5.6.0"
35
+ }
36
+ }