@sellhapi/mcp 0.0.0-stage → 0.1.2

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.
@@ -0,0 +1,342 @@
1
+ "use strict";
2
+ /**
3
+ * Studio tools — website lifecycle management.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.registerStudioTools = registerStudioTools;
7
+ const v4_1 = require("zod/v4");
8
+ const client_js_1 = require("../client.js");
9
+ const util_js_1 = require("../util.js");
10
+ const runtime_hooks_js_1 = require("../resources/runtime-hooks.js");
11
+ const build_rules_js_1 = require("../resources/build-rules.js");
12
+ function registerStudioTools(server) {
13
+ // ── List projects ──────────────────────────────────────────────────────────
14
+ server.registerTool("list_projects", {
15
+ title: "List Projects",
16
+ description: "List all Studio projects for this vendor. Returns project name, status, capabilities, and preview URL.",
17
+ annotations: { readOnlyHint: true },
18
+ }, async () => (0, util_js_1.safeCall)(async () => {
19
+ const data = await (0, client_js_1.get)("/api/mcp/projects");
20
+ return JSON.stringify(data.projects, null, 2);
21
+ }));
22
+ // ── Get project ────────────────────────────────────────────────────────────
23
+ server.registerTool("get_project", {
24
+ title: "Get Project",
25
+ description: "Get full project details including current code, design direction, capabilities, and preview URL.",
26
+ inputSchema: { projectId: v4_1.z.string().describe("Studio project ID") },
27
+ annotations: { readOnlyHint: true },
28
+ }, async ({ projectId }) => (0, util_js_1.safeCall)(async () => {
29
+ const data = await (0, client_js_1.get)(`/api/mcp/projects/${projectId}`);
30
+ return JSON.stringify(data, null, 2);
31
+ }));
32
+ // ── Create project ─────────────────────────────────────────────────────────
33
+ server.registerTool("create_project", {
34
+ title: "Create Project",
35
+ description: "Create a new Studio project. Specify name, business type, and capabilities (physical_products, digital_products, services, appointments, events, donations, memberships, content, listings, customer_accounts). The response includes required_reading with full build rules and runtime hook signatures — read them before generating any code.",
36
+ inputSchema: {
37
+ name: v4_1.z.string().describe("Project name (e.g. 'My Fashion Store')"),
38
+ businessType: v4_1.z.string().optional().describe("Business type (e.g. 'fashion', 'restaurant', 'bakery')"),
39
+ description: v4_1.z.string().optional().describe("Brief description of the business"),
40
+ capabilities: v4_1.z.array(v4_1.z.string()).optional().describe("Capability IDs to enable"),
41
+ },
42
+ }, async ({ name, businessType, description, capabilities }) => (0, util_js_1.safeCall)(async () => {
43
+ const data = await (0, client_js_1.post)("/api/mcp/projects", { name, businessType, description, capabilities });
44
+ // Inject full reference docs so every client (ChatGPT, Claude, Cursor) has them
45
+ // before any code generation — regardless of whether it supports MCP Resources.
46
+ const result = {
47
+ ...data,
48
+ workflow: {
49
+ instruction: "Follow this sequence in order. Skipping steps produces a broken or unstyled website.",
50
+ steps: [
51
+ {
52
+ step: 1,
53
+ tool: "apply_design_direction",
54
+ required: true,
55
+ description: "Generate and store a full DesignDirection — colors, fonts, pages, business context. Do this NOW before anything else. Read sellhapi://design-direction-schema for the exact shape. Present the direction summary to the user (palette, pages, mood) and get confirmation before calling apply_design_direction. Alternatively use set_design_direction to let SellHapi's Claude generate it (costs credits).",
56
+ },
57
+ {
58
+ step: 2,
59
+ tool: "apply_wireframe",
60
+ required: false,
61
+ description: "Optional but recommended. Generate mid-fidelity HTML wireframes for each page, present them section-by-section to the user, collect feedback, then store the approved wireframes. Read sellhapi://wireframe-schema first.",
62
+ },
63
+ {
64
+ step: 3,
65
+ tool: "push_code",
66
+ required: true,
67
+ description: "Generate and push the full React storefront code. Read build_rules and runtime_hooks in full before writing any code — violating build_rules causes this step to fail with a BuildGuard error.",
68
+ },
69
+ {
70
+ step: 4,
71
+ tool: "deploy_website",
72
+ required: true,
73
+ description: "Deploy to the live storefront ONLY after the user has reviewed the preview from push_code and confirmed it looks correct.",
74
+ },
75
+ ],
76
+ },
77
+ required_reading: {
78
+ instruction: "Read build_rules and runtime_hooks in full before generating any storefront code. Violating build_rules causes push_code to fail with a BuildGuard error.",
79
+ build_rules: build_rules_js_1.BUILD_RULES,
80
+ runtime_hooks: runtime_hooks_js_1.RUNTIME_HOOKS,
81
+ },
82
+ };
83
+ return JSON.stringify(result, null, 2);
84
+ }));
85
+ // ── List revisions ─────────────────────────────────────────────────────────
86
+ server.registerTool("list_revisions", {
87
+ title: "List Revisions",
88
+ description: "List all revision history for a project. Returns version numbers, messages, and timestamps — no file payloads. Use get_revision to inspect a specific version's code.",
89
+ inputSchema: { projectId: v4_1.z.string().describe("Studio project ID") },
90
+ annotations: { readOnlyHint: true },
91
+ }, async ({ projectId }) => (0, util_js_1.safeCall)(async () => {
92
+ const data = await (0, client_js_1.get)(`/api/mcp/projects/${projectId}/revisions`);
93
+ return JSON.stringify(data.revisions, null, 2);
94
+ }));
95
+ // ── Get revision ────────────────────────────────────────────────────────────
96
+ server.registerTool("get_revision", {
97
+ title: "Get Revision",
98
+ description: "Get a specific revision including its full code files. Use list_revisions first to find version numbers. Files can be large — only fetch when you need to inspect or restore a specific version.",
99
+ inputSchema: {
100
+ projectId: v4_1.z.string().describe("Studio project ID"),
101
+ version: v4_1.z.number().int().min(1).describe("Revision version number (from list_revisions)"),
102
+ },
103
+ annotations: { readOnlyHint: true },
104
+ }, async ({ projectId, version }) => (0, util_js_1.safeCall)(async () => {
105
+ const data = await (0, client_js_1.get)(`/api/mcp/projects/${projectId}/revisions/${version}`);
106
+ return JSON.stringify(data.revision, null, 2);
107
+ }));
108
+ // ── Restore revision ────────────────────────────────────────────────────────
109
+ server.registerTool("restore_revision", {
110
+ title: "Restore Revision",
111
+ description: "Restore a project to a specific revision. Atomically restores code, design direction, wireframes, and conversation history from that snapshot, creates an audit entry, then rebuilds and returns a preview URL. History grows forward — the restore is recorded as a new revision.",
112
+ inputSchema: {
113
+ projectId: v4_1.z.string().describe("Studio project ID"),
114
+ version: v4_1.z.number().int().min(1).describe("Revision version number to restore to"),
115
+ },
116
+ }, async ({ projectId, version }) => (0, util_js_1.safeCall)(async () => {
117
+ const data = await (0, client_js_1.post)(`/api/mcp/projects/${projectId}/revisions/${version}/restore`);
118
+ return JSON.stringify(data, null, 2);
119
+ }));
120
+ // ── Push code (zero credits) ───────────────────────────────────────────────
121
+ server.registerTool("push_code", {
122
+ title: "Push Code",
123
+ description: "Push client-generated React code to a project. Runs BuildGuard validation + esbuild bundling and returns a preview URL. ZERO AI credit cost — you generate the code, SellHapi only builds it. CRITICAL RULES: (1) import ONLY from @sellhapi/studio-runtime and react — NO external packages; (2) every top-level JSX section must have data-region, data-label, data-scope attributes; (3) NO inline style={{}} — use Tailwind only; (4) NO localStorage/sessionStorage for state management — EXCEPTION: window.location.href IS required and allowed for payment redirects (all commerce hooks return { paymentLink } — redirect with window.location.href = result.paymentLink); (5) prices via priceKobo / 100 — never hardcode currency. Call get_build_rules or get_runtime_hooks for full reference.",
124
+ inputSchema: {
125
+ projectId: v4_1.z.string().describe("Studio project ID"),
126
+ files: v4_1.z.record(v4_1.z.string(), v4_1.z.string()).describe("Map of filename → code content. Must include App.tsx as entry point."),
127
+ },
128
+ }, async ({ projectId, files }) => {
129
+ try {
130
+ const data = await (0, client_js_1.post)(`/api/mcp/projects/${projectId}/push-code`, { files });
131
+ // Surface capability warnings prominently so the AI client acts on them
132
+ const warnings = data.capabilityWarnings ?? [];
133
+ const lines = [JSON.stringify(data, null, 2)];
134
+ if (warnings.length > 0) {
135
+ lines.push("", "⚠ BuildGuard warnings — fix before deploying:", ...warnings.map((w, i) => `${i + 1}. [${w.rule}] ${w.message}${w.fix ? `\n Fix: ${w.fix}` : ""}`));
136
+ }
137
+ return {
138
+ content: [{ type: "text", text: lines.join("\n") }],
139
+ };
140
+ }
141
+ catch (err) {
142
+ // Surface BuildGuard violations as structured data for AI retry
143
+ if (err instanceof client_js_1.ApiError && err.body?.violations) {
144
+ const msg = [
145
+ `BuildGuard violation — fix these issues and push again:`,
146
+ "",
147
+ ...err.body.violations.map((v, i) => `${i + 1}. [${v.severity ?? "error"}] ${v.message}${v.file ? ` (${v.file}:${v.line ?? "?"})` : ""}`),
148
+ ].join("\n");
149
+ return { content: [{ type: "text", text: msg }] };
150
+ }
151
+ return {
152
+ content: [{ type: "text", text: `Error: ${err?.message ?? String(err)}` }],
153
+ };
154
+ }
155
+ });
156
+ // ── Patch code (zero credits) ──────────────────────────────────────────────
157
+ server.registerTool("patch_code", {
158
+ title: "Patch Code",
159
+ description: "Send ONLY the files you changed. Merges them into the project's existing code, then runs BuildGuard + esbuild and returns a preview URL. Use this for targeted edits — changing one section, fixing a bug, tweaking styles — when you don't need to resend the entire project. Use push_code when you're generating the site from scratch or replacing all files.",
160
+ inputSchema: {
161
+ projectId: v4_1.z.string().describe("Studio project ID"),
162
+ files: v4_1.z.record(v4_1.z.string(), v4_1.z.string()).describe("Only the files that changed. Keys are filenames (e.g. 'HeroSection.tsx'), values are full file content. Other files in the project are preserved unchanged."),
163
+ },
164
+ }, async ({ projectId, files }) => {
165
+ try {
166
+ const data = await (0, client_js_1.post)(`/api/mcp/projects/${projectId}/patch-code`, { files });
167
+ const warnings = data.capabilityWarnings ?? [];
168
+ const lines = [JSON.stringify(data, null, 2)];
169
+ if (warnings.length > 0) {
170
+ lines.push("", "⚠ BuildGuard warnings — fix before deploying:", ...warnings.map((w, i) => `${i + 1}. [${w.rule}] ${w.message}${w.fix ? `\n Fix: ${w.fix}` : ""}`));
171
+ }
172
+ return {
173
+ content: [{ type: "text", text: lines.join("\n") }],
174
+ };
175
+ }
176
+ catch (err) {
177
+ if (err instanceof client_js_1.ApiError && err.body?.violations) {
178
+ const msg = [
179
+ `BuildGuard violation — fix these issues and patch again:`,
180
+ "",
181
+ ...err.body.violations.map((v, i) => `${i + 1}. [${v.severity ?? "error"}] ${v.message}${v.file ? ` (${v.file}:${v.line ?? "?"})` : ""}`),
182
+ ].join("\n");
183
+ return { content: [{ type: "text", text: msg }] };
184
+ }
185
+ return {
186
+ content: [{ type: "text", text: `Error: ${err?.message ?? String(err)}` }],
187
+ };
188
+ }
189
+ });
190
+ // ── Deploy website ─────────────────────────────────────────────────────────
191
+ server.registerTool("deploy_website", {
192
+ title: "Deploy Website",
193
+ description: "Deploy the project to the live storefront. Only call this AFTER push_code succeeds and the user has confirmed the preview looks good. Compiles, bundles, and publishes to the vendor's custom domain or sellhapi.shop subdomain.",
194
+ inputSchema: {
195
+ projectId: v4_1.z.string().describe("Studio project ID"),
196
+ },
197
+ }, async ({ projectId }) => {
198
+ try {
199
+ const data = await (0, client_js_1.post)(`/api/mcp/projects/${projectId}/publish`);
200
+ return {
201
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
202
+ };
203
+ }
204
+ catch (err) {
205
+ if (err instanceof client_js_1.ApiError && err.body?.violations) {
206
+ const msg = [
207
+ `Build failed during deploy — fix these issues:`,
208
+ "",
209
+ ...err.body.violations.map((v, i) => `${i + 1}. [${v.severity ?? "error"}] ${v.message}${v.file ? ` (${v.file}:${v.line ?? "?"})` : ""}`),
210
+ ].join("\n");
211
+ return { content: [{ type: "text", text: msg }] };
212
+ }
213
+ return {
214
+ content: [{ type: "text", text: `Error: ${err?.message ?? String(err)}` }],
215
+ };
216
+ }
217
+ });
218
+ // ── Get preview ────────────────────────────────────────────────────────────
219
+ server.registerTool("get_preview", {
220
+ title: "Get Preview URL",
221
+ description: "Get the current sandbox preview URL for a project.",
222
+ inputSchema: {
223
+ projectId: v4_1.z.string().describe("Studio project ID"),
224
+ },
225
+ annotations: { readOnlyHint: true },
226
+ }, async ({ projectId }) => (0, util_js_1.safeCall)(async () => {
227
+ const data = await (0, client_js_1.get)(`/api/mcp/projects/${projectId}`);
228
+ return JSON.stringify({ previewUrl: data.previewUrl ?? null }, null, 2);
229
+ }));
230
+ // ── Get vendor context ─────────────────────────────────────────────────────
231
+ server.registerTool("get_vendor_context", {
232
+ title: "Get Vendor Context",
233
+ description: "CALL THIS FIRST before writing any storefront code. Returns in a single call: active capability IDs, store slug, business name and category, product count, collection names, and service names. Eliminates the 4-call context-gathering chain and ensures code is generated for the actual store contents.",
234
+ annotations: { readOnlyHint: true },
235
+ }, async () => (0, util_js_1.safeCall)(async () => {
236
+ const data = await (0, client_js_1.get)("/api/mcp/vendor-context");
237
+ return JSON.stringify(data, null, 2);
238
+ }));
239
+ // ── Suggest capabilities ───────────────────────────────────────────────────
240
+ server.registerTool("suggest_capabilities", {
241
+ title: "Suggest Capabilities",
242
+ description: "Given a business brief, returns a ranked list of capability IDs to activate. Uses keyword matching against the platform capability registry — no LLM cost. Call after get_vendor_context to identify which capabilities are missing. Examples: 'Lagos bakery selling bread and custom cakes' → physical_products + appointments. 'DJ booking agency' → services + appointments.",
243
+ inputSchema: {
244
+ brief: v4_1.z.string().describe("Business description or brief text — what the business does, what it sells, its services"),
245
+ },
246
+ }, async ({ brief }) => (0, util_js_1.safeCall)(async () => {
247
+ const data = await (0, client_js_1.post)("/api/mcp/suggest-capabilities", { brief });
248
+ return JSON.stringify(data, null, 2);
249
+ }));
250
+ // ── Import website content ─────────────────────────────────────────────────
251
+ server.registerTool("import_website_content", {
252
+ title: "Import Website Content",
253
+ description: "Scrape up to 6 pages of an existing website via Firecrawl and store the combined markdown as contentBrief on the project. Use this when the vendor wants to pull copy (headlines, taglines, service descriptions, about text) from their current website into the new one. Returns the scraped content — use it to inform businessContext in apply_design_direction and to pre-fill section copy in the wireframe. Different from analyze_reference: this extracts full page copy, not just OG metadata.",
254
+ inputSchema: {
255
+ projectId: v4_1.z.string().describe("Studio project ID"),
256
+ urls: v4_1.z.array(v4_1.z.string()).min(1).max(6).describe("URLs to scrape — homepage first, then inner pages (e.g. /about, /services). Max 6. Must be publicly accessible https URLs."),
257
+ },
258
+ }, async ({ projectId, urls }) => (0, util_js_1.safeCall)(async () => {
259
+ const data = await (0, client_js_1.post)(`/api/mcp/projects/${projectId}/import-website-content`, { urls });
260
+ return JSON.stringify(data, null, 2);
261
+ }));
262
+ // ── Apply design direction (zero cost — client generates) ─────────────────
263
+ server.registerTool("apply_design_direction", {
264
+ title: "Apply Design Direction",
265
+ description: "Store a DesignDirection JSON object you generated yourself on a project. ZERO Claude API cost — your LLM does the work. Read sellhapi://design-direction-schema first to understand the exact shape. After generating the direction, show the summary (palette, pages, mood) to the user and get confirmation before calling this. Returns the stored designDirection.",
266
+ inputSchema: {
267
+ projectId: v4_1.z.string().describe("Studio project ID"),
268
+ direction: v4_1.z.record(v4_1.z.string(), v4_1.z.unknown()).describe("Complete DesignDirection object. Must include: businessType, capabilityIds, pages (slug+title+sections), visualDirection (colors+fonts+style tokens), businessContext (name+tagline+targetAudience+USPs)."),
269
+ },
270
+ }, async ({ projectId, direction }) => (0, util_js_1.safeCall)(async () => {
271
+ const data = await (0, client_js_1.post)(`/api/mcp/projects/${projectId}/apply-direction`, { direction });
272
+ return JSON.stringify(data, null, 2);
273
+ }));
274
+ // ── Set design direction (SellHapi's Claude generates — costs credits) ─────
275
+ server.registerTool("set_design_direction", {
276
+ title: "Set Design Direction (AI-generated)",
277
+ description: "Ask SellHapi's Claude to generate a design direction from a plain-text brief. NOTE: costs AI credits. Prefer apply_design_direction instead — read sellhapi://design-direction-schema and generate the direction yourself for free. Use this only when the brief is very ambiguous and you want SellHapi's AI to interpret it.",
278
+ inputSchema: {
279
+ projectId: v4_1.z.string().describe("Studio project ID"),
280
+ brief: v4_1.z.string().describe("Design brief — business description, desired mood, target audience, visual references. The richer the better."),
281
+ storeType: v4_1.z.string().optional().describe("Store category hint for layout optimisation: fashion | beauty | food | electronics | general"),
282
+ },
283
+ }, async ({ projectId, brief, storeType }) => (0, util_js_1.safeCall)(async () => {
284
+ const data = await (0, client_js_1.post)(`/api/mcp/projects/${projectId}/design-direction`, { brief, storeType });
285
+ return JSON.stringify(data, null, 2);
286
+ }));
287
+ // ── Apply wireframe (zero cost — client generates, user approves) ──────────
288
+ server.registerTool("apply_wireframe", {
289
+ title: "Apply Wireframe",
290
+ description: "Store approved mid-fidelity HTML wireframes on a project — one per page. ZERO Claude API cost. IMPORTANT: Read sellhapi://wireframe-schema first. Generate wireframe HTML for each page, present each one to the user in plain text (section-by-section summary), collect feedback, revise if needed, then call this ONLY after the user explicitly approves all pages. The wireframe is the user's last checkpoint before code is written.",
291
+ inputSchema: {
292
+ projectId: v4_1.z.string().describe("Studio project ID"),
293
+ wireframes: v4_1.z.record(v4_1.z.string(), v4_1.z.string()).describe("Map of pageSlug → mid-fidelity HTML string. Each slug must match one in designDirection.pages."),
294
+ },
295
+ }, async ({ projectId, wireframes }) => (0, util_js_1.safeCall)(async () => {
296
+ const data = await (0, client_js_1.post)(`/api/mcp/projects/${projectId}/apply-wireframe`, { wireframes });
297
+ return JSON.stringify(data, null, 2);
298
+ }));
299
+ // ── Analyze design image / website / Pinterest pins (costs Claude credits) ─
300
+ server.registerTool("analyze_design_reference", {
301
+ title: "Analyze Design Reference (Visual)",
302
+ description: "Send a design reference image URL, website URL, or Pinterest pins to Claude vision and get back a fully populated DesignDirection. Use when the user provides a visual reference (screenshot, moodboard, inspiration website, Pinterest board). Three exclusive modes: imageUrl (direct image link), websiteUrl (screenshotted + analyzed), pinUrls (up to 4 pin.it or pinterest.com/pin/ URLs synthesized into one direction). NOTE: costs AI credits — prefer apply_design_direction when you can derive the direction yourself from conversation context.",
303
+ inputSchema: {
304
+ projectId: v4_1.z.string().describe("Studio project ID"),
305
+ imageUrl: v4_1.z.string().optional().describe("Direct image URL to analyze (http/https, publicly accessible). Use when user pastes an image link."),
306
+ websiteUrl: v4_1.z.string().optional().describe("Website URL to screenshot and analyze for visual inspiration. Different from import_website_content which extracts copy — this extracts visual design DNA."),
307
+ pinUrls: v4_1.z.array(v4_1.z.string()).min(1).max(4).optional().describe("Up to 4 pin.it or pinterest.com/pin/ URLs. Synthesized into a single unified design direction."),
308
+ brief: v4_1.z.string().optional().describe("Optional vendor context to guide interpretation (business type, target audience, etc.)"),
309
+ mode: v4_1.z.enum(["inspiration", "recreate"]).optional().describe("inspiration (default): adapt visual DNA to the business. recreate: match the reference as closely as possible."),
310
+ },
311
+ }, async ({ projectId, imageUrl, websiteUrl, pinUrls, brief, mode }) => (0, util_js_1.safeCall)(async () => {
312
+ const data = await (0, client_js_1.post)(`/api/mcp/projects/${projectId}/analyze-image`, { imageUrl, websiteUrl, pinUrls, brief, mode });
313
+ return JSON.stringify(data, null, 2);
314
+ }));
315
+ // ── Analyze reference URL ──────────────────────────────────────────────────
316
+ server.registerTool("analyze_reference", {
317
+ title: "Analyze Reference URL",
318
+ description: "Fetch OG metadata, page title, description, and theme-color hint from any URL. Returns a suggestedBrief string you can pass directly to set_design_direction. Use this when the user provides a reference website they want to draw inspiration from.",
319
+ inputSchema: {
320
+ url: v4_1.z.string().describe("URL to analyze — must be https://. The server fetches this directly with a 6-second timeout."),
321
+ },
322
+ }, async ({ url }) => (0, util_js_1.safeCall)(async () => {
323
+ const data = await (0, client_js_1.post)("/api/mcp/analyze-reference", { url });
324
+ return JSON.stringify(data, null, 2);
325
+ }));
326
+ // ── Get build rules (mid-session reference) ────────────────────────────────
327
+ server.registerTool("get_build_rules", {
328
+ title: "Get Build Rules",
329
+ description: "Returns the full BuildGuard validation rules — every constraint push_code enforces. Call this when push_code fails with a BuildGuard violation and you need to understand the rule in full detail, or whenever you need a refresher on what code patterns are allowed.",
330
+ annotations: { readOnlyHint: true },
331
+ }, async () => ({
332
+ content: [{ type: "text", text: build_rules_js_1.BUILD_RULES }],
333
+ }));
334
+ // ── Get runtime hooks (mid-session reference) ──────────────────────────────
335
+ server.registerTool("get_runtime_hooks", {
336
+ title: "Get Runtime Hooks",
337
+ description: "Returns the complete @sellhapi/studio-runtime hook reference — every hook, its signature, return shape, and usage example. Call this when you need to look up how a specific hook works (e.g. useProducts, useBookAppointment, useCart) before writing or fixing code.",
338
+ annotations: { readOnlyHint: true },
339
+ }, async () => ({
340
+ content: [{ type: "text", text: runtime_hooks_js_1.RUNTIME_HOOKS }],
341
+ }));
342
+ }
package/dist/util.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Shared utilities for the MCP server.
3
+ */
4
+ /** MCP tool content result shape. */
5
+ type ToolResult = {
6
+ content: Array<{
7
+ type: "text";
8
+ text: string;
9
+ }>;
10
+ };
11
+ /**
12
+ * Wraps a tool callback with structured error handling.
13
+ * Returns the stringified result on success, or an isError response on failure.
14
+ */
15
+ export declare function safeCall(fn: () => Promise<string>): Promise<ToolResult>;
16
+ export {};
package/dist/util.js ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /**
3
+ * Shared utilities for the MCP server.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.safeCall = safeCall;
7
+ /**
8
+ * Wraps a tool callback with structured error handling.
9
+ * Returns the stringified result on success, or an isError response on failure.
10
+ */
11
+ async function safeCall(fn) {
12
+ try {
13
+ const text = await fn();
14
+ return { content: [{ type: "text", text }] };
15
+ }
16
+ catch (err) {
17
+ return {
18
+ content: [{ type: "text", text: `Error: ${err?.message ?? String(err)}` }],
19
+ };
20
+ }
21
+ }
package/package.json CHANGED
@@ -1,6 +1,38 @@
1
1
  {
2
2
  "name": "@sellhapi/mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.2",
4
+ "description": "SellHapi MCP server — build and manage SellHapi storefronts from AI tools",
5
+ "bin": {
6
+ "sellhapi-mcp": "./dist/index.js"
7
+ },
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js",
14
+ "start:hosted": "node dist/hosted.js"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "sellhapi",
19
+ "ai",
20
+ "storefront",
21
+ "commerce"
22
+ ],
23
+ "license": "MIT",
24
+ "dependencies": {
25
+ "@modelcontextprotocol/sdk": "^1.12.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/express": "^5.0.6",
29
+ "@types/node": "^20.0.0",
30
+ "typescript": "^5.5.0"
31
+ },
32
+ "engines": {
33
+ "node": ">=18"
34
+ },
35
+ "files": [
36
+ "dist"
37
+ ]
38
+ }