Skip to main content
Glama
index.js26.3 kB
#!/usr/bin/env node /** * MCP server for Better Auth references * This server provides tools to: * - List all available Better Auth plugins and features * - Get detailed information about specific plugins/features * - Get usage examples and configuration * - Search for Better Auth functionality by keyword * - Get integration guides for specific frameworks */ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js"; import axios from "axios"; import * as cheerio from "cheerio"; /** * BetterAuthServer class that handles all the Better Auth reference functionality */ class BetterAuthServer { server; axiosInstance; featureCache = new Map(); featuresListCache = null; integrationCache = new Map(); BETTER_AUTH_DOCS_URL = "https://www.better-auth.com"; BETTER_AUTH_GITHUB_URL = "https://github.com/better-auth/better-auth"; // Better Auth categories and features mapping FEATURE_CATEGORIES = { authentication: [ "email-password", "social-sign-on", "apple", "discord", "facebook", "github", "google", "hugging-face", "kick", "microsoft", "slack", "notion", "tiktok", "twitch", "twitter", "dropbox", "linear", "linkedin", "gitlab", "reddit", "roblox", "spotify", "vk", "zoom" ], databases: [ "mysql", "sqlite", "postgresql", "ms-sql", "drizzle", "prisma", "mongodb" ], integrations: [ "astro", "remix", "next", "nuxt", "sveltekit", "solidstart", "tanstack-start", "hono", "fastify", "express", "elysia", "nitro", "nestjs", "expo" ], plugins: [ "two-factor", "username", "anonymous", "phone-number", "magic-link", "email-otp", "passkey", "generic-oauth", "one-tap", "sign-in-with-ethereum", "admin", "api-key", "mcp", "organization", "enterprise", "oidc-provider", "sso", "bearer", "captcha", "have-i-been-pwned", "multi-session", "oauth-proxy", "one-time-token", "open-api", "jwt", "stripe", "polar", "autumn-billing", "dodo-payments", "dub" ] }; constructor() { this.server = new Server({ name: "better-auth-server", version: "0.1.0", }, { capabilities: { tools: {}, }, }); this.axiosInstance = axios.create({ timeout: 15000, headers: { "User-Agent": "Mozilla/5.0 (compatible; BetterAuthMcpServer/0.1.0)", }, }); this.setupToolHandlers(); this.server.onerror = (error) => console.error("[MCP Error]", error); process.on("SIGINT", async () => { await this.server.close(); process.exit(0); }); } /** * Set up the tool handlers for the server */ setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: "list_better_auth_features", description: "Get a list of all available Better Auth plugins and features organized by category", inputSchema: { type: "object", properties: { category: { type: "string", description: "Optional category filter: authentication, databases, integrations, or plugins", enum: ["authentication", "databases", "integrations", "plugins"], }, }, required: [], }, }, { name: "get_feature_details", description: "Get detailed information about a specific Better Auth plugin or feature", inputSchema: { type: "object", properties: { featureName: { type: "string", description: "Name of the Better Auth feature (e.g., \"two-factor\", \"github\", \"drizzle\")", }, }, required: ["featureName"], }, }, { name: "get_integration_guide", description: "Get integration guide for a specific framework or platform", inputSchema: { type: "object", properties: { framework: { type: "string", description: "Framework name (e.g., \"next\", \"remix\", \"astro\", \"expo\")", }, }, required: ["framework"], }, }, { name: "get_auth_examples", description: "Get usage examples and code snippets for Better Auth features", inputSchema: { type: "object", properties: { featureName: { type: "string", description: "Feature to get examples for (e.g., \"email-password\", \"github\", \"two-factor\")", }, framework: { type: "string", description: "Optional framework filter for examples", }, }, required: ["featureName"], }, }, { name: "search_better_auth", description: "Search Better Auth documentation by keyword", inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query to find relevant Better Auth features or documentation", }, }, required: ["query"], }, }, { name: "get_llms_context", description: "Get Better Auth LLMs.txt content for comprehensive context", inputSchema: { type: "object", properties: {}, required: [], }, }, ], })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { switch (request.params.name) { case "list_better_auth_features": return await this.handleListFeatures(request.params.arguments); case "get_feature_details": return await this.handleGetFeatureDetails(request.params.arguments); case "get_integration_guide": return await this.handleGetIntegrationGuide(request.params.arguments); case "get_auth_examples": return await this.handleGetAuthExamples(request.params.arguments); case "search_better_auth": return await this.handleSearchBetterAuth(request.params.arguments); case "get_llms_context": return await this.handleGetLlmsContext(); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`); } }); } /** * Handle the list_better_auth_features tool request */ async handleListFeatures(args) { try { if (!this.featuresListCache) { await this.loadFeaturesFromDocs(); } let features = this.featuresListCache || []; // Filter by category if specified if (args?.category) { features = features.filter(feature => feature.category === args.category); } return this.createSuccessResponse({ total: features.length, features: features, categories: Object.keys(this.FEATURE_CATEGORIES), }); } catch (error) { this.handleAxiosError(error, "Failed to fetch Better Auth features"); } } /** * Load features from Better Auth documentation */ async loadFeaturesFromDocs() { const features = []; for (const [category, featureNames] of Object.entries(this.FEATURE_CATEGORIES)) { for (const featureName of featureNames) { try { const featureInfo = await this.fetchFeatureBasicInfo(featureName, category); features.push(featureInfo); } catch (error) { // Continue with other features if one fails console.error(`Failed to load feature ${featureName}:`, error); } } } this.featuresListCache = features; } /** * Fetch basic information about a feature */ async fetchFeatureBasicInfo(featureName, category) { let url = `${this.BETTER_AUTH_DOCS_URL}/docs`; // Construct URL based on category switch (category) { case "authentication": if (featureName === "email-password") { url += "/authentication/email-password"; } else if (["apple", "discord", "facebook", "github", "google", "hugging-face", "kick", "microsoft", "slack", "notion", "tiktok", "twitch", "twitter", "dropbox", "linear", "linkedin", "gitlab", "reddit", "roblox", "spotify", "vk", "zoom"].includes(featureName)) { url += `/authentication/social/${featureName}`; } else { url += "/authentication/social-sign-on"; } break; case "databases": url += `/databases/${featureName}`; break; case "integrations": url += `/integrations/${featureName}`; break; case "plugins": url += `/plugins/${featureName}`; break; } return { name: featureName, category, description: `Better Auth ${category} feature: ${featureName}`, url, isNew: ["notion", "tiktok", "sign-in-with-ethereum", "autumn-billing", "dodo-payments"].includes(featureName), }; } /** * Handle the get_feature_details tool request */ async handleGetFeatureDetails(args) { const featureName = this.validateFeatureName(args); try { // Check cache first if (this.featureCache.has(featureName)) { return this.createSuccessResponse(this.featureCache.get(featureName)); } // Fetch detailed feature information const featureInfo = await this.fetchFeatureDetails(featureName); // Save to cache this.featureCache.set(featureName, featureInfo); return this.createSuccessResponse(featureInfo); } catch (error) { this.handleAxiosError(error, `Feature "${featureName}"`); } } /** * Fetch detailed feature information */ async fetchFeatureDetails(featureName) { // Find the feature in our categories const category = this.findFeatureCategory(featureName); if (!category) { throw new McpError(ErrorCode.InvalidParams, `Unknown feature: ${featureName}`); } const basicInfo = await this.fetchFeatureBasicInfo(featureName, category); try { const response = await this.axiosInstance.get(basicInfo.url); const $ = cheerio.load(response.data); // Extract detailed information const description = this.extractDescription($); const installation = this.extractInstallation($); const usage = this.extractUsage($); const config = this.extractConfiguration($); const examples = this.extractExamples($); return { ...basicInfo, description: description || basicInfo.description, installation, usage, config, examples, }; } catch (error) { // Return basic info if detailed fetch fails return basicInfo; } } /** * Find which category a feature belongs to */ findFeatureCategory(featureName) { for (const [category, features] of Object.entries(this.FEATURE_CATEGORIES)) { if (features.includes(featureName)) { return category; } } return null; } /** * Handle the get_integration_guide tool request */ async handleGetIntegrationGuide(args) { const framework = this.validateFramework(args); try { // Check cache first if (this.integrationCache.has(framework)) { return this.createSuccessResponse(this.integrationCache.get(framework)); } // Fetch integration guide const guide = await this.fetchIntegrationGuide(framework); // Save to cache this.integrationCache.set(framework, guide); return this.createSuccessResponse(guide); } catch (error) { this.handleAxiosError(error, `Integration guide for "${framework}"`); } } /** * Fetch integration guide for a specific framework */ async fetchIntegrationGuide(framework) { const url = `${this.BETTER_AUTH_DOCS_URL}/docs/integrations/${framework}`; try { const response = await this.axiosInstance.get(url); const $ = cheerio.load(response.data); const title = $("h1").first().text().trim(); const description = this.extractDescription($); const setup = this.extractSetup($); const config = this.extractConfiguration($); const examples = this.extractExamples($); return { name: title || framework, framework, description: description || `Better Auth integration guide for ${framework}`, url, setup, config, examples, }; } catch (error) { throw new McpError(ErrorCode.InvalidParams, `Integration guide for "${framework}" not found`); } } /** * Handle the get_auth_examples tool request */ async handleGetAuthExamples(args) { const featureName = this.validateFeatureName(args); const framework = args?.framework; try { const examples = await this.fetchAuthExamples(featureName, framework); return this.createSuccessResponse({ feature: featureName, framework: framework || "all", examples, }); } catch (error) { this.handleAxiosError(error, `Examples for "${featureName}"`); } } /** * Fetch examples for a specific feature */ async fetchAuthExamples(featureName, framework) { const examples = []; // Get feature details first const feature = await this.fetchFeatureDetails(featureName); if (feature.examples) { examples.push(...feature.examples); } // If framework specified, also get framework-specific examples if (framework) { try { const exampleUrl = `${this.BETTER_AUTH_DOCS_URL}/docs/examples/${framework}`; const response = await this.axiosInstance.get(exampleUrl); const $ = cheerio.load(response.data); const frameworkExamples = this.extractExamples($); examples.push(...frameworkExamples.map(ex => ({ ...ex, framework, }))); } catch (error) { // Continue if framework examples not found } } return examples; } /** * Handle the search_better_auth tool request */ async handleSearchBetterAuth(args) { const query = this.validateSearchQuery(args); try { await this.ensureFeaturesListLoaded(); const results = this.searchFeaturesByQuery(query); return this.createSuccessResponse({ query, results: results.slice(0, 10), // Limit to top 10 results total: results.length, }); } catch (error) { this.handleAxiosError(error, "Search failed"); } } /** * Handle the get_llms_context tool request */ async handleGetLlmsContext() { try { const response = await this.axiosInstance.get(`${this.BETTER_AUTH_DOCS_URL}/llms.txt`); return this.createSuccessResponse({ source: "https://www.better-auth.com/llms.txt", content: response.data, lastUpdated: new Date().toISOString(), }); } catch (error) { this.handleAxiosError(error, "Failed to fetch Better Auth LLMs context"); } } /** * Extract description from the page */ extractDescription($) { let description = ""; // Try different selectors for description const descriptionElement = $("h1").first().next("p"); if (descriptionElement.length > 0) { description = descriptionElement.text().trim(); } // Alternative: look for intro paragraph if (!description) { const introParagraph = $("p").first(); if (introParagraph.length > 0) { description = introParagraph.text().trim(); } } return description; } /** * Extract installation instructions */ extractInstallation($) { let installation = ""; const installSection = $("h2, h3").filter((_, el) => { const text = $(el).text().toLowerCase(); return text.includes("install") || text.includes("setup"); }); if (installSection.length > 0) { const codeBlock = installSection.nextAll("pre").first(); if (codeBlock.length > 0) { installation = codeBlock.text().trim(); } } return installation; } /** * Extract setup instructions */ extractSetup($) { let setup = ""; const sections = ["Setup", "Getting Started", "Installation"]; for (const sectionName of sections) { const section = $("h2, h3").filter((_, el) => $(el).text().trim() === sectionName); if (section.length > 0) { let content = ""; let next = section.next(); while (next.length > 0 && !next.is("h1, h2")) { if (next.is("pre")) { content += next.text().trim() + "\n\n"; } else if (next.is("p")) { content += next.text().trim() + "\n\n"; } next = next.next(); } if (content) { setup = content.trim(); break; } } } return setup; } /** * Extract usage examples */ extractUsage($) { let usage = ""; const usageSection = $("h2, h3").filter((_, el) => { const text = $(el).text().toLowerCase(); return text.includes("usage") || text.includes("basic") || text.includes("example"); }); if (usageSection.length > 0) { const codeBlocks = usageSection.nextAll("pre"); if (codeBlocks.length > 0) { codeBlocks.each((_, el) => { usage += $(el).text().trim() + "\n\n"; }); } } return usage.trim(); } /** * Extract configuration options */ extractConfiguration($) { const config = {}; // Look for configuration sections const configSection = $("h2, h3").filter((_, el) => { const text = $(el).text().toLowerCase(); return text.includes("config") || text.includes("option") || text.includes("setting"); }); if (configSection.length > 0) { // Extract configuration from code blocks const codeBlocks = configSection.nextAll("pre"); codeBlocks.each((_, el) => { const code = $(el).text().trim(); if (code.includes("{") && code.includes("}")) { try { // Try to extract configuration object const configMatch = code.match(/\{[\s\S]*\}/); if (configMatch) { config.example = configMatch[0]; } } catch (error) { // Continue if parsing fails } } }); } return config; } /** * Extract examples from the page */ extractExamples($) { const examples = []; // Find all code blocks with their preceding headings $("pre").each((i, el) => { const code = $(el).text().trim(); if (code) { let title = `Example ${i + 1}`; let description = ""; // Look for preceding heading let prev = $(el).prev(); while (prev.length > 0 && !prev.is("h1, h2, h3, h4")) { prev = prev.prev(); } if (prev.is("h2, h3, h4")) { title = prev.text().trim(); description = `${title} implementation`; } examples.push({ title, code, description, }); } }); return examples; } /** * Search features by query */ searchFeaturesByQuery(query) { if (!this.featuresListCache) { return []; } const searchTerms = query.toLowerCase().split(' '); return this.featuresListCache.filter(feature => { const searchText = `${feature.name} ${feature.description} ${feature.category}`.toLowerCase(); return searchTerms.some(term => searchText.includes(term)); }).sort((a, b) => { // Prioritize exact matches const aExact = a.name.toLowerCase().includes(query.toLowerCase()); const bExact = b.name.toLowerCase().includes(query.toLowerCase()); if (aExact && !bExact) return -1; if (!aExact && bExact) return 1; return 0; }); } /** * Validation helpers */ validateFeatureName(args) { if (!args?.featureName || typeof args.featureName !== "string") { throw new McpError(ErrorCode.InvalidParams, "Feature name is required and must be a string"); } return args.featureName.toLowerCase(); } validateFramework(args) { if (!args?.framework || typeof args.framework !== "string") { throw new McpError(ErrorCode.InvalidParams, "Framework name is required and must be a string"); } return args.framework.toLowerCase(); } validateSearchQuery(args) { if (!args?.query || typeof args.query !== "string") { throw new McpError(ErrorCode.InvalidParams, "Search query is required and must be a string"); } return args.query.toLowerCase(); } /** * Error handling helper */ handleAxiosError(error, context) { if (axios.isAxiosError(error)) { if (error.response?.status === 404) { throw new McpError(ErrorCode.InvalidParams, `${context} not found`); } else { throw new McpError(ErrorCode.InternalError, `${context}: ${error.message}`); } } throw error; } /** * Create standardized success response */ createSuccessResponse(data) { return { content: [ { type: "text", text: JSON.stringify(data, null, 2), }, ], }; } /** * Ensure features list is loaded */ async ensureFeaturesListLoaded() { if (!this.featuresListCache) { await this.loadFeaturesFromDocs(); } if (!this.featuresListCache) { throw new McpError(ErrorCode.InternalError, "Failed to load Better Auth features list"); } } /** * Run the server */ async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error("Better Auth MCP server running on stdio"); } } // Create and run the server const server = new BetterAuthServer(); server.run().catch((error) => { console.error("Server error:", error); process.exit(1); }); //# sourceMappingURL=index.js.map

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dabhivijay2478/better-auth-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server