UNPKG

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
#!/usr/bin/env node /** * 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); });