rnr-mcp-server
Version:
A Model Context Protocol (MCP) server for React Native Reusables components, providing AI assistants with access to component source code, demos, and metadata for React Native development.
298 lines (292 loc) • 13.5 kB
JavaScript
/**
* React Native Reusables MCP Server
*
* A Model Context Protocol server for React Native Reusables components.
* Provides AI assistants with access to component source code, demos, and metadata for React Native development.
*
* Usage:
* npx rnr-mcp-server
* npx rnr-mcp-server --github-api-key YOUR_TOKEN
* npx rnr-mcp-server -g YOUR_TOKEN
*/
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { setupHandlers } from "./handler.js";
import { validateFrameworkSelection, getAxiosImplementation, } from "./utils/framework.js";
import { logError, logInfo, logWarning } from "./utils/logger.js";
/**
* Parse command line arguments
*/
async function parseArgs() {
const args = process.argv.slice(2);
// Help flag
if (args.includes("--help") || args.includes("-h")) {
console.log(`
React Native Reusables MCP Server
Usage:
npx rnr-mcp-server [options]
Options:
--github-api-key, -g <token> GitHub Personal Access Token for API access
--framework, -f <framework> Framework to use: 'react-native' or 'expo' (default: react-native)
--help, -h Show this help message
--version, -v Show version information
Examples:
npx rnr-mcp-server
npx rnr-mcp-server --github-api-key ghp_your_token_here
npx rnr-mcp-server -g ghp_your_token_here
npx rnr-mcp-server --framework expo
npx rnr-mcp-server -f react-native
Environment Variables:
GITHUB_PERSONAL_ACCESS_TOKEN Alternative way to provide GitHub token
FRAMEWORK Framework to use: 'react-native' or 'expo' (default: react-native)
LOG_LEVEL Log level (debug, info, warn, error) - default: info
For more information, visit: https://github.com/danieltgfischer/rnr-mcp-server
`);
process.exit(0);
}
// Version flag
if (args.includes("--version") || args.includes("-v")) {
// Read version from package.json
try {
const fs = await import("fs");
const path = await import("path");
const { fileURLToPath } = await import("url");
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const packagePath = path.join(__dirname, "..", "package.json");
const packageContent = fs.readFileSync(packagePath, "utf8");
const packageJson = JSON.parse(packageContent);
console.log(`rnr-mcp-server v${packageJson.version}`);
}
catch (error) {
console.log("rnr-mcp-server v1.0.0");
}
process.exit(0);
}
// GitHub API key
const githubApiKeyIndex = args.findIndex((arg) => arg === "--github-api-key" || arg === "-g");
let githubApiKey = null;
if (githubApiKeyIndex !== -1 && args[githubApiKeyIndex + 1]) {
githubApiKey = args[githubApiKeyIndex + 1];
}
else if (process.env.GITHUB_PERSONAL_ACCESS_TOKEN) {
githubApiKey = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
}
return { githubApiKey };
}
/**
* Main function to start the MCP server
*/
async function main() {
try {
logInfo("Starting React Native Reusables MCP Server...");
const { githubApiKey } = await parseArgs();
// Validate and log framework selection
validateFrameworkSelection();
// Get the appropriate axios implementation based on framework
const axios = await getAxiosImplementation();
// Configure GitHub API key if provided
if (githubApiKey) {
axios.setGitHubApiKey(githubApiKey);
logInfo("GitHub API configured with token");
}
else {
logWarning("No GitHub API key provided. Rate limited to 60 requests/hour.");
}
// Initialize the MCP server with metadata and capabilities
// Following MCP SDK 1.16.0 best practices
const server = new Server({
name: "rnr-mcp-server",
version: "1.0.0",
}, {
capabilities: {
resources: {
get_components: {
description: "List of available React Native Reusables components that can be used in the project",
uri: "resource:get_components",
contentType: "text/plain",
},
get_install_script_for_component: {
description: "Generate installation script for a specific React Native Reusables component based on package manager",
uriTemplate: "resource-template:get_install_script_for_component?packageManager={packageManager}&component={component}",
contentType: "text/plain",
},
get_installation_guide: {
description: "Get the installation guide for React Native Reusables based on build tool and package manager",
uriTemplate: "resource-template:get_installation_guide?buildTool={buildTool}&packageManager={packageManager}",
contentType: "text/plain",
},
},
prompts: {
component_usage: {
description: "Get usage examples for a specific React Native Reusables component",
arguments: {
componentName: {
type: "string",
description: "Name of the component to get usage for",
},
},
},
component_search: {
description: "Search for React Native Reusables components by name or description",
arguments: {
query: {
type: "string",
description: "Search query",
},
},
},
component_comparison: {
description: "Compare two React Native Reusables components side by side",
arguments: {
component1: {
type: "string",
description: "First component name",
},
component2: {
type: "string",
description: "Second component name",
},
},
},
component_recommendation: {
description: "Get React Native Reusables component recommendations based on use case",
arguments: {
useCase: {
type: "string",
description: "Use case description",
},
},
},
component_tutorial: {
description: "Get a step-by-step tutorial for using a React Native Reusables component",
arguments: {
componentName: {
type: "string",
description: "Name of the component for tutorial",
},
},
},
},
tools: {
get_component: {
description: "Get the source code for a specific React Native Reusables component",
inputSchema: {
type: "object",
properties: {
componentName: {
type: "string",
description: 'Name of the React Native Reusables component (e.g., "accordion", "button")',
},
},
required: ["componentName"],
},
},
get_component_demo: {
description: "Get demo code illustrating how a React Native Reusables component should be used",
inputSchema: {
type: "object",
properties: {
componentName: {
type: "string",
description: 'Name of the React Native Reusables component (e.g., "accordion", "button")',
},
},
required: ["componentName"],
},
},
list_components: {
description: "Get all available React Native Reusables components",
inputSchema: {
type: "object",
properties: {},
},
},
get_component_metadata: {
description: "Get metadata for a specific React Native Reusables component",
inputSchema: {
type: "object",
properties: {
componentName: {
type: "string",
description: 'Name of the React Native Reusables component (e.g., "accordion", "button")',
},
},
required: ["componentName"],
},
},
get_directory_structure: {
description: "Get the directory structure of the React Native Reusables repository",
inputSchema: {
type: "object",
properties: {
path: {
type: "string",
description: "Path within the repository (default: packages/ui/src)",
},
owner: {
type: "string",
description: 'Repository owner (default: "mrzachnugent")',
},
repo: {
type: "string",
description: 'Repository name (default: "react-native-reusables")',
},
branch: {
type: "string",
description: 'Branch name (default: "main")',
},
},
},
},
get_block: {
description: "Get source code for a specific React Native Reusables block (e.g., calendar-01, dashboard-01)",
inputSchema: {
type: "object",
properties: {
blockName: {
type: "string",
description: 'Name of the block (e.g., "calendar-01", "dashboard-01", "login-02")',
},
includeComponents: {
type: "boolean",
description: "Whether to include component files for complex blocks (default: true)",
},
},
required: ["blockName"],
},
},
list_blocks: {
description: "Get all available React Native Reusables blocks with categorization",
inputSchema: {
type: "object",
properties: {
category: {
type: "string",
description: "Filter by category (calendar, dashboard, login, sidebar, products)",
},
},
},
},
},
},
});
// Set up request handlers and register components (tools, resources, etc.)
setupHandlers(server);
// Start server using stdio transport
const transport = new StdioServerTransport();
logInfo("Transport initialized: stdio");
await server.connect(transport);
logInfo("Server started successfully");
}
catch (error) {
logError("Failed to start server", error);
process.exit(1);
}
}
// Start the server
main().catch((error) => {
logError("Unhandled startup error", error);
process.exit(1);
});