🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@zaai-dev/mcp

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@zaai-dev/mcp - npm Package Compare versions

Comparing version
0.1.0
to
0.2.0
+408
-2
dist/bin/stdio.js

@@ -13,3 +13,3 @@ #!/usr/bin/env node

// src/version.ts
var PKG_VERSION = true ? "0.1.0" : "0.0.0-dev";
var PKG_VERSION = true ? "0.2.0" : "0.0.0-dev";

@@ -223,2 +223,208 @@ // src/tools/health.ts

// src/tools/get-brand-brief.ts
import { z as z7 } from "zod";
var getBrandBriefInputSchema = z7.object({
project_id: z7.string().uuid().describe(
"Zaai Dev project UUID. Find via the workspace at zaaistudio.com/dev/projects."
)
});
var getBrandBriefOutputSchema = z7.object({
project_id: z7.string(),
project_name: z7.string(),
version: z7.string().describe("Brief version number, or '0' if no brief has been generated yet."),
status: z7.enum(["draft", "published", "archived"]),
positioning: z7.string(),
values: z7.array(z7.string()),
voice: z7.object({
one_liner: z7.string(),
do_say: z7.array(z7.string()),
dont_say: z7.array(z7.string())
}),
audience: z7.object({
primary: z7.string(),
secondary: z7.string().nullable(),
needs: z7.array(z7.string())
}),
design_intent: z7.object({
descriptors: z7.array(z7.string()),
anti_descriptors: z7.array(z7.string())
}),
decisions: z7.array(
z7.object({
id: z7.string(),
title: z7.string(),
decided_at: z7.string(),
rationale: z7.string()
})
),
updated_at: z7.string()
}).passthrough();
async function getBrandBrief(store, args) {
return store.getBrandBrief(args.project_id);
}
// src/tools/get-voice.ts
import { z as z8 } from "zod";
var getVoiceInputSchema = z8.object({
project_id: z8.string().uuid().describe("Zaai Dev project UUID.")
});
var getVoiceOutputSchema = z8.object({
project_id: z8.string(),
one_liner: z8.string().describe("One-sentence summary of how the brand sounds."),
tone_descriptors: z8.array(z8.string()).describe("Adjectives the brand voice aims for."),
do_say: z8.array(z8.string()).describe("Phrasings / patterns the brand actively uses."),
dont_say: z8.array(z8.string()).describe("Phrasings / patterns the brand explicitly avoids."),
example_phrases: z8.array(z8.string()).describe("Concrete phrases that exemplify the voice.")
}).passthrough();
async function getVoice(store, args) {
return store.getVoice(args.project_id);
}
// src/tools/get-audience.ts
import { z as z9 } from "zod";
var getAudienceInputSchema = z9.object({
project_id: z9.string().uuid().describe("Zaai Dev project UUID.")
});
var getAudienceOutputSchema = z9.object({
project_id: z9.string(),
primary: z9.string().describe("Primary audience segment."),
secondary: z9.string().nullable().describe("Secondary audience segment, or null."),
needs: z9.array(z9.string()).describe("Jobs-to-be-done / pain points the audience has."),
channels: z9.array(z9.string()).describe("Where this audience consumes content.")
}).passthrough();
async function getAudience(store, args) {
return store.getAudience(args.project_id);
}
// src/tools/get-design-intent.ts
import { z as z10 } from "zod";
var getDesignIntentInputSchema = z10.object({
project_id: z10.string().uuid().describe("Zaai Dev project UUID.")
});
var getDesignIntentOutputSchema = z10.object({
project_id: z10.string(),
descriptors: z10.array(z10.string()).describe(
"Visual adjectives the brand aims for (e.g. 'editorial', 'spacious', 'monochrome')."
),
anti_descriptors: z10.array(z10.string()).describe("Visual adjectives the brand explicitly avoids."),
inspiration_summary: z10.string().describe("Free-form summary of the inspiration the brief points at.")
}).passthrough();
async function getDesignIntent(store, args) {
return store.getDesignIntent(args.project_id);
}
// src/tools/get-brand-tokens.ts
import { z as z11 } from "zod";
var getBrandTokensInputSchema = z11.object({
project_id: z11.string().uuid().describe("Zaai Dev project UUID.")
});
var getBrandTokensOutputSchema = z11.object({
project_id: z11.string(),
colors: z11.array(
z11.object({
name: z11.string(),
value: z11.string().describe("Hex / rgba / hsl value as authored."),
role: z11.string().optional().describe("Role like 'primary' / 'accent' / 'surface'.")
})
),
fonts: z11.array(
z11.object({
role: z11.string().describe("'display', 'body', 'mono', etc."),
family: z11.string(),
weights: z11.array(z11.number().int())
})
),
radius: z11.array(z11.object({ name: z11.string(), value: z11.string() })),
shadows: z11.array(z11.object({ name: z11.string(), value: z11.string() }))
}).passthrough();
async function getBrandTokens(store, args) {
return store.getBrandTokens(args.project_id);
}
// src/tools/get-decisions.ts
import { z as z12 } from "zod";
var getDecisionsInputSchema = z12.object({
project_id: z12.string().uuid().describe("Zaai Dev project UUID."),
limit: z12.number().int().min(1).max(200).optional().describe("Max decisions to return. Default 50, max 200, newest first.")
});
var getDecisionsOutputSchema = z12.object({
project_id: z12.string(),
items: z12.array(
z12.object({
id: z12.string(),
title: z12.string(),
rationale: z12.string(),
attribution: z12.string().describe(
"Who decided. 'mcp:<token_id>' for AI-logged decisions; user email / id for workspace-authored ones."
),
brief_field: z12.string().nullable().describe("Which brief slice this decision concerns, if any (e.g. 'voice.tone')."),
reference_capture_id: z12.string().nullable(),
created_at: z12.string()
}).passthrough()
)
}).passthrough();
async function getDecisions(store, args) {
return store.getDecisions(args.project_id, args.limit);
}
// src/tools/get-references.ts
import { z as z13 } from "zod";
var getReferencesInputSchema = z13.object({
project_id: z13.string().uuid().describe("Zaai Dev project UUID."),
limit: z13.number().int().min(1).max(100).optional().describe("Max references to return. Default 20, max 100, newest first."),
cursor: z13.string().optional().describe("Opaque pagination cursor from the previous response's next_cursor.")
});
var referenceItem = z13.object({
id: z13.string(),
type: z13.enum(["page", "element", "composite"]),
source_url: z13.string(),
source_title: z13.string(),
note: z13.string().nullable(),
captured_at: z13.string(),
thumbnail_url: z13.string().nullable(),
screenshot_url: z13.string().nullable()
}).passthrough();
var getReferencesOutputSchema = z13.object({
project_id: z13.string(),
items: z13.array(referenceItem),
next_cursor: z13.string().nullable()
}).passthrough();
async function getReferences(store, args) {
return store.getReferences(args.project_id, {
limit: args.limit,
cursor: args.cursor
});
}
// src/tools/search-references.ts
import { z as z14 } from "zod";
var searchReferencesInputSchema = z14.object({
project_id: z14.string().uuid().describe("Zaai Dev project UUID."),
q: z14.string().min(1).max(200).describe("Keyword query \u2014 matched against title / url / note (case-insensitive substring)."),
limit: z14.number().int().min(1).max(50).optional().describe("Max results to return. Default 10, max 50.")
});
var searchReferenceItem = z14.object({
id: z14.string(),
type: z14.enum(["page", "element", "composite"]),
source_url: z14.string(),
source_title: z14.string(),
note: z14.string().nullable(),
captured_at: z14.string(),
thumbnail_url: z14.string().nullable(),
screenshot_url: z14.string().nullable(),
score: z14.number().describe("Relevance score 0..1. v1 keyword scoring: 1.0 title / 0.7 url / 0.5 note."),
matched_field: z14.enum(["title", "url", "note", "tag"])
}).passthrough();
var searchReferencesOutputSchema = z14.object({
project_id: z14.string(),
query: z14.string(),
items: z14.array(searchReferenceItem)
}).passthrough();
async function searchReferences(store, args) {
return store.searchReferences(args.project_id, {
q: args.q,
limit: args.limit
});
}
// src/store/http-store.ts

@@ -255,2 +461,68 @@ var HttpCaptureStore = class {

// src/store/project-store.ts
var HttpProjectStore = class {
constructor(config) {
this.config = config;
}
config;
async getBrandBrief(projectId) {
return mcpApiFetch(
this.config,
`/api/mcp/projects/${encodeURIComponent(projectId)}/brief`
);
}
async getVoice(projectId) {
return mcpApiFetch(
this.config,
`/api/mcp/projects/${encodeURIComponent(projectId)}/voice`
);
}
async getAudience(projectId) {
return mcpApiFetch(
this.config,
`/api/mcp/projects/${encodeURIComponent(projectId)}/audience`
);
}
async getDesignIntent(projectId) {
return mcpApiFetch(
this.config,
`/api/mcp/projects/${encodeURIComponent(projectId)}/design-intent`
);
}
async getBrandTokens(projectId) {
return mcpApiFetch(
this.config,
`/api/mcp/projects/${encodeURIComponent(projectId)}/brand-tokens`
);
}
async getDecisions(projectId, limit) {
const sp = new URLSearchParams();
if (limit !== void 0) sp.set("limit", String(limit));
const qs = sp.toString();
return mcpApiFetch(
this.config,
`/api/mcp/projects/${encodeURIComponent(projectId)}/decisions${qs ? `?${qs}` : ""}`
);
}
async getReferences(projectId, args) {
const sp = new URLSearchParams();
if (args.limit !== void 0) sp.set("limit", String(args.limit));
if (args.cursor) sp.set("cursor", args.cursor);
const qs = sp.toString();
return mcpApiFetch(
this.config,
`/api/mcp/projects/${encodeURIComponent(projectId)}/references${qs ? `?${qs}` : ""}`
);
}
async searchReferences(projectId, args) {
const sp = new URLSearchParams();
sp.set("q", args.q);
if (args.limit !== void 0) sp.set("limit", String(args.limit));
return mcpApiFetch(
this.config,
`/api/mcp/projects/${encodeURIComponent(projectId)}/references/search?${sp.toString()}`
);
}
};
// src/resources/capture-resource.ts

@@ -321,2 +593,3 @@ import {

const captureStore = new HttpCaptureStore(config);
const projectStore = new HttpProjectStore(config);
server.registerTool(

@@ -488,2 +761,114 @@ "health",

);
server.registerTool(
"get_brand_brief",
{
title: "Get brand brief",
description: "Returns the full published brand brief for a project: positioning, values, voice, audience, design intent, and decisions. Use this when the LLM needs the whole picture before writing copy or designing a component. If the project has no brief yet, the response still validates \u2014 most fields are empty strings or empty arrays.",
inputSchema: getBrandBriefInputSchema.shape,
outputSchema: getBrandBriefOutputSchema.shape
},
makeProjectToolHandler(
projectStore,
getBrandBrief,
(r) => `Brief v${r.version} (${r.status}) for "${r.project_name}". Voice: ${r.voice.one_liner || "\u2014"}.`
)
);
server.registerTool(
"get_voice",
{
title: "Get voice guidelines",
description: "Returns only the voice slice: one-liner, tone descriptors, do-say / don't-say lists, example phrases. Lighter than the full brief when the LLM is only writing copy. Costs 1 credit.",
inputSchema: getVoiceInputSchema.shape,
outputSchema: getVoiceOutputSchema.shape
},
makeProjectToolHandler(
projectStore,
getVoice,
(r) => `Voice for project ${r.project_id.slice(0, 8)}\u2026: "${r.one_liner || "\u2014"}". ${r.tone_descriptors.length} tone descriptor${r.tone_descriptors.length === 1 ? "" : "s"}.`
)
);
server.registerTool(
"get_audience",
{
title: "Get audience profile",
description: "Returns only the audience slice: primary segment, optional secondary, needs / pain points, channels. Use when targeting copy or design at a specific audience. Costs 1 credit.",
inputSchema: getAudienceInputSchema.shape,
outputSchema: getAudienceOutputSchema.shape
},
makeProjectToolHandler(
projectStore,
getAudience,
(r) => `Audience: ${r.primary || "\u2014"}` + (r.secondary ? ` (secondary: ${r.secondary})` : "") + `. ${r.needs.length} stated need${r.needs.length === 1 ? "" : "s"}.`
)
);
server.registerTool(
"get_design_intent",
{
title: "Get design intent",
description: "Returns the visual descriptors and anti-descriptors that constrain design exploration, plus the inspiration_summary. Use this before generating layouts, palettes, or component styles \u2014 it's the guardrail set the brief encodes. Costs 1 credit.",
inputSchema: getDesignIntentInputSchema.shape,
outputSchema: getDesignIntentOutputSchema.shape
},
makeProjectToolHandler(
projectStore,
getDesignIntent,
(r) => `${r.descriptors.length} descriptor${r.descriptors.length === 1 ? "" : "s"}, ${r.anti_descriptors.length} anti-descriptor${r.anti_descriptors.length === 1 ? "" : "s"}.`
)
);
server.registerTool(
"get_brand_tokens",
{
title: "Get brand tokens",
description: "Returns the brand's design tokens: colors (with optional role), fonts (role / family / weights), radius scale, shadow scale. Use this when generating CSS, Tailwind config, or component styles that should match the brand. Costs 1 credit.",
inputSchema: getBrandTokensInputSchema.shape,
outputSchema: getBrandTokensOutputSchema.shape
},
makeProjectToolHandler(
projectStore,
getBrandTokens,
(r) => `${r.colors.length} color${r.colors.length === 1 ? "" : "s"}, ${r.fonts.length} font${r.fonts.length === 1 ? "" : "s"}, ${r.radius.length} radius, ${r.shadows.length} shadow${r.shadows.length === 1 ? "" : "s"}.`
)
);
server.registerTool(
"get_decisions",
{
title: "Get decisions log",
description: "Returns the brand + design decisions log for a project: what was decided, why, who decided, when, and which brief field (if any) it concerns. Use this to avoid re-litigating settled questions. Costs 1 credit.",
inputSchema: getDecisionsInputSchema.shape,
outputSchema: getDecisionsOutputSchema.shape
},
makeProjectToolHandler(
projectStore,
getDecisions,
(r) => `${r.items.length} decision${r.items.length === 1 ? "" : "s"} returned (newest first).`
)
);
server.registerTool(
"get_references",
{
title: "Get project references",
description: "Paginated list of references (captures) for one project. Use this when the LLM needs to know what visual / interaction references the user has saved for a specific project \u2014 different from list_captures which is org-wide. Costs 1 credit per call.",
inputSchema: getReferencesInputSchema.shape,
outputSchema: getReferencesOutputSchema.shape
},
makeProjectToolHandler(
projectStore,
getReferences,
(r) => `${r.items.length} reference${r.items.length === 1 ? "" : "s"} returned` + (r.next_cursor ? " (more available \u2014 pass next_cursor for the next page)." : ".")
)
);
server.registerTool(
"search_references",
{
title: "Search project references",
description: "Keyword search over a project's references (title / url / note in v1; pgvector ranking in v1.5). Returns scored matches with matched_field signals. Use when the LLM has a specific concept in mind ('hero', 'pricing table'). Costs 1 credit.",
inputSchema: searchReferencesInputSchema.shape,
outputSchema: searchReferencesOutputSchema.shape
},
makeProjectToolHandler(
projectStore,
searchReferences,
(r) => r.items.length === 0 ? `No references match "${r.query}".` : `${r.items.length} reference${r.items.length === 1 ? "" : "s"} matching "${r.query}".`
)
);
registerCaptureResource(server, captureStore);

@@ -501,3 +886,11 @@ info("server initialized", {

"get_animation",
"get_media"
"get_media",
"get_brand_brief",
"get_voice",
"get_audience",
"get_design_intent",
"get_brand_tokens",
"get_decisions",
"get_references",
"search_references"
],

@@ -508,2 +901,15 @@ resources: ["zaai-capture://{id}"]

}
function makeProjectToolHandler(store, fn, summary) {
return async (args) => {
try {
const result = await fn(store, args);
return {
structuredContent: result,
content: [{ type: "text", text: summary(result) }]
};
} catch (err) {
return toolErrorResponse(err);
}
};
}
function makeFocusedGetterHandler(store, fn, label) {

@@ -510,0 +916,0 @@ return async (args) => {

+1
-1
{
"name": "@zaai-dev/mcp",
"version": "0.1.0",
"version": "0.2.0",
"description": "Zaai Dev MCP server — exposes your captures (and, in v1.5, your brand brief) to MCP-compatible AI tools.",

@@ -5,0 +5,0 @@ "type": "module",

Sorry, the diff of this file is too big to display