#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import {
COMPONENT_REGISTRY,
CATEGORIES,
searchComponents,
getComponentsByCategory,
getComponentBySlug,
} from "./registry.js";
import { fetchComponentDocs, fetchGettingStarted } from "./fetcher.js";
const server = new McpServer({
name: "reacticx-docs",
version: "1.0.0",
});
// Tool: List all components or filter by category
server.tool(
"list_components",
"List all available Reacticx components. Optionally filter by category: shaders, texts, micro-interactions, components, templates.",
{
category: z
.string()
.optional()
.describe(
"Filter by category: shaders, texts, micro-interactions, components, templates"
),
},
async ({ category }) => {
let results =
category && category.trim()
? getComponentsByCategory(category.trim())
: COMPONENT_REGISTRY;
if (results.length === 0) {
return {
content: [
{
type: "text" as const,
text: `No components found for category "${category}". Available categories: ${CATEGORIES.join(", ")}`,
},
],
};
}
const grouped = new Map<string, typeof results>();
for (const comp of results) {
const cat = comp.category;
if (!grouped.has(cat)) grouped.set(cat, []);
grouped.get(cat)!.push(comp);
}
let output = `# Reacticx Components (${results.length} total)\n\n`;
output += `Install any component: \`bunx --bun reacticx add <component-name>\`\n\n`;
for (const [cat, comps] of grouped) {
output += `## ${cat.charAt(0).toUpperCase() + cat.slice(1)} (${comps.length})\n\n`;
for (const comp of comps) {
const deps =
comp.dependencies.length > 0
? ` — deps: ${comp.dependencies.join(", ")}`
: "";
output += `- **${comp.name}** (\`${comp.slug}\`): ${comp.description}${deps}\n`;
}
output += "\n";
}
return {
content: [{ type: "text" as const, text: output }],
};
}
);
// Tool: Get full documentation for a specific component
server.tool(
"get_component_docs",
"Fetch the full documentation for a specific Reacticx component, including props, code examples, usage, and installation instructions. Use the component slug (e.g. 'accordion', 'elastic-slider', 'aurora').",
{
component: z
.string()
.describe(
"Component slug (e.g. 'accordion', 'elastic-slider', 'aurora')"
),
},
async ({ component }) => {
const slug = component.toLowerCase().trim();
const info = getComponentBySlug(slug);
if (!info) {
const suggestions = searchComponents(slug);
const suggestText =
suggestions.length > 0
? `\n\nDid you mean: ${suggestions
.slice(0, 5)
.map((s) => `"${s.slug}"`)
.join(", ")}?`
: "";
return {
content: [
{
type: "text" as const,
text: `Component "${slug}" not found in registry.${suggestText}`,
},
],
};
}
try {
const docs = await fetchComponentDocs(slug);
const header = [
`**Component:** ${info.name}`,
`**Category:** ${info.category}`,
`**Install:** \`bunx --bun reacticx add ${info.slug}\``,
info.dependencies.length > 0
? `**Dependencies:** ${info.dependencies.join(", ")}`
: null,
"",
"---",
"",
]
.filter(Boolean)
.join("\n");
return {
content: [
{ type: "text" as const, text: header + docs },
],
};
} catch (error) {
const message =
error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text" as const,
text: `Error fetching docs for "${slug}": ${message}\n\nBasic info:\n- Name: ${info.name}\n- Category: ${info.category}\n- Dependencies: ${info.dependencies.join(", ") || "none"}\n- Install: \`bunx --bun reacticx add ${info.slug}\`\n- Docs: https://www.reacticx.com/docs/components/${info.slug}`,
},
],
};
}
}
);
// Tool: Search components by keyword
server.tool(
"search_components",
"Search Reacticx components by keyword. Searches component names, descriptions, and categories.",
{
query: z
.string()
.describe(
"Search query (e.g. 'carousel', 'animation', 'slider', 'loader')"
),
},
async ({ query }) => {
const results = searchComponents(query);
if (results.length === 0) {
return {
content: [
{
type: "text" as const,
text: `No components found matching "${query}". Try a different keyword.`,
},
],
};
}
let output = `# Search Results for "${query}" (${results.length} matches)\n\n`;
for (const comp of results) {
const deps =
comp.dependencies.length > 0
? `\n Dependencies: ${comp.dependencies.join(", ")}`
: "";
output += `- **${comp.name}** (\`${comp.slug}\`) [${comp.category}]\n ${comp.description}${deps}\n Install: \`bunx --bun reacticx add ${comp.slug}\`\n\n`;
}
return {
content: [{ type: "text" as const, text: output }],
};
}
);
// Tool: Get getting started / installation guide
server.tool(
"getting_started",
"Get the Reacticx getting started guide with installation and setup instructions.",
{},
async () => {
try {
const docs = await fetchGettingStarted();
return {
content: [{ type: "text" as const, text: docs }],
};
} catch (error) {
const message =
error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text" as const,
text: `Error fetching getting started docs: ${message}\n\nBasic installation:\n\`\`\`bash\n# Add a component\nbunx --bun reacticx add <component-name>\n\n# Common dependencies\nnpm install react-native-reanimated @shopify/react-native-skia react-native-gesture-handler\n\`\`\`\n\nDocs: https://www.reacticx.com/docs`,
},
],
};
}
}
);
// Tool: Get component dependencies
server.tool(
"get_dependencies",
"Get the required npm dependencies for one or more Reacticx components. Useful for planning installations.",
{
components: z
.array(z.string())
.describe(
"Array of component slugs to check dependencies for"
),
},
async ({ components }) => {
const allDeps = new Set<string>();
const notFound: string[] = [];
const found: { name: string; slug: string; deps: string[] }[] = [];
for (const slug of components) {
const info = getComponentBySlug(slug.toLowerCase().trim());
if (info) {
found.push({
name: info.name,
slug: info.slug,
deps: info.dependencies,
});
info.dependencies.forEach((d) => allDeps.add(d));
} else {
notFound.push(slug);
}
}
let output = `# Dependencies for ${found.length} component(s)\n\n`;
if (found.length > 0) {
for (const comp of found) {
output += `- **${comp.name}** (\`${comp.slug}\`): ${comp.deps.length > 0 ? comp.deps.join(", ") : "no extra dependencies"}\n`;
}
output += `\n## Combined Install Command\n\n`;
if (allDeps.size > 0) {
output += `\`\`\`bash\nnpm install ${[...allDeps].join(" ")}\n\`\`\`\n`;
} else {
output += `No additional dependencies required.\n`;
}
output += `\n## Add Components\n\n\`\`\`bash\n${found.map((c) => `bunx --bun reacticx add ${c.slug}`).join("\n")}\n\`\`\`\n`;
}
if (notFound.length > 0) {
output += `\n## Not Found\n\n${notFound.map((s) => `- "${s}"`).join("\n")}\n`;
}
return {
content: [{ type: "text" as const, text: output }],
};
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch((error) => {
console.error("Server error:", error);
process.exit(1);
});