UNPKG

@rocketshipai/mcp-server

Version:

MCP assistant for helping AI coding agents write better Rocketship tests

1,029 lines (1,020 loc) • 62.5 kB
#!/usr/bin/env node "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RocketshipMCPServer = void 0; const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const types_js_1 = require("@modelcontextprotocol/sdk/types.js"); // Static knowledge loader with embedded content class RocketshipKnowledgeLoader { schema = null; examples = new Map(); docs = new Map(); cliData = null; constructor() { console.log(`Initializing Rocketship MCP Server v0.4.1`); this.loadEmbeddedKnowledge(); console.log(`āœ“ Loaded embedded Rocketship knowledge`); console.log(`āœ“ Available examples: ${this.examples.size}`); console.log(`āœ“ Available docs: ${this.docs.size}`); } loadEmbeddedKnowledge() { // Load embedded knowledge from build step - fail fast if not available const { EMBEDDED_SCHEMA, EMBEDDED_EXAMPLES, EMBEDDED_DOCS, EMBEDDED_CLI_DATA, } = require("./embedded-knowledge"); this.schema = EMBEDDED_SCHEMA; this.examples = EMBEDDED_EXAMPLES; this.docs = EMBEDDED_DOCS; this.cliData = EMBEDDED_CLI_DATA; console.log(`šŸ“¦ Loaded real embedded knowledge from build step`); console.log(`šŸ“‹ CLI Version: ${this.cliData?.version?.version || "unknown"}`); } getSchema() { return this.schema; } getExample(name) { return this.examples.get(name); } getAllExamples() { return Array.from(this.examples.keys()); } getDocumentation(path) { return this.docs.get(path); } getAllDocs() { return Array.from(this.docs.keys()); } getCLIData() { return this.cliData; } } // Initialize knowledge loader const knowledgeLoader = new RocketshipKnowledgeLoader(); // REMOVED: Hard-coded tool descriptions replaced with dynamic generation // Generate dynamic tool descriptions based on CLI introspection data function generateToolDescriptions(knowledgeLoader) { const cliData = knowledgeLoader.getCLIData(); const schema = knowledgeLoader.getSchema(); const availablePlugins = schema?.properties?.tests?.items?.properties?.steps?.items?.properties ?.plugin?.enum || []; // Extract dynamic information const filePattern = cliData?.usage?.file_structure?.pattern || ".rocketship/**/*.yaml"; const varExamples = cliData?.usage?.syntax_patterns?.variables || {}; const commonCommands = cliData?.usage?.common_patterns || []; // Build variable syntax examples from extracted data let variableSyntax = ""; if (varExamples.config && varExamples.config.length > 0) { variableSyntax += `šŸ’” Config variables: ${varExamples.config .slice(0, 2) .join(", ")}\n`; } if (varExamples.environment && varExamples.environment.length > 0) { variableSyntax += `šŸ’” Environment variables: ${varExamples.environment .slice(0, 2) .join(", ")}\n`; } if (varExamples.runtime && varExamples.runtime.length > 0) { variableSyntax += `šŸ’” Runtime variables: ${varExamples.runtime .slice(0, 2) .join(", ")}\n`; } // Build CLI command examples from extracted data let cliExamples = ""; const runCommands = commonCommands .filter((c) => c.command.includes("run")) .slice(0, 2); if (runCommands.length > 0) { cliExamples = `šŸ’” Example commands: ${runCommands .map((c) => c.command) .join(", ")}`; } // Build plugin recommendations from available plugins let pluginRecommendations = ""; if (availablePlugins.includes("browser_use") || availablePlugins.includes("playwright")) { pluginRecommendations += "šŸ’” For frontend projects: browser_use and playwright plugins available for user journey testing\n"; } if (availablePlugins.includes("http")) { pluginRecommendations += "šŸ’” For API projects: http plugin available for endpoint testing\n"; } return { get_available_examples: `Lists all available examples from the current codebase with descriptions and metadata. šŸ’” Use this to discover what examples exist before requesting specific ones. šŸ’” Shows available plugins: ${availablePlugins.join(", ")} šŸ’” Returns example names, descriptions, and plugin types for easy discovery.`, get_rocketship_examples: `Provides real examples from the current codebase for specific features or use cases. šŸ’” YOU (the coding agent) create the test files based on these examples. ${pluginRecommendations}šŸ’” File pattern: ${filePattern} ${variableSyntax}`, search_examples: `Search for examples using keywords to find relevant test patterns. šŸ’” Use this when you're not sure which plugin to use or need examples for a specific use case. šŸ’” Searches across example content, file names, and plugin types. šŸ’” Better than guessing - find examples that match your testing scenario.`, suggest_test_structure: `Suggests proper file structure and test organization based on current project configuration. šŸ’” YOU (the coding agent) create the directory structure and files. ${pluginRecommendations}šŸ’” Available plugins: ${availablePlugins.join(", ")}`, get_schema_info: `Provides current schema information for validation and proper syntax. šŸ’” Use this to ensure your YAML follows the correct schema. šŸ’” Available plugins: ${availablePlugins.join(", ")} šŸ’” Schema validation ensures compatibility with current version.`, get_cli_guidance: `Provides current CLI usage patterns and commands from introspection. šŸ’” YOU (the coding agent) will run these commands to execute tests. ${cliExamples} šŸ’” All commands are extracted from current CLI version.`, get_rocketship_cli_installation_instructions: `Get step-by-step instructions for installing the Rocketship CLI on different platforms. šŸ’” Use this when rocketship command is not found or you need to install Rocketship. šŸ’” Provides platform-specific installation commands and troubleshooting. šŸ’” Shows what methods are available vs NOT available (like Homebrew). šŸ’” Includes prerequisites and post-installation verification steps.`, analyze_codebase_for_testing: `Analyzes a codebase to suggest meaningful test scenarios based on available plugins. šŸ’” Focus on customer-facing flows and critical business logic. ${pluginRecommendations}šŸ’” Suggestions are based on available plugins: ${availablePlugins.join(", ")} šŸ’” TIP: Include relevant keywords for better flow suggestions`, }; } class RocketshipMCPServer { server; knowledgeLoader; constructor() { this.knowledgeLoader = knowledgeLoader; this.server = new index_js_1.Server({ name: "rocketship-mcp", version: "0.4.1", }, { capabilities: { tools: {}, }, }); this.setupHandlers(); } setupHandlers() { this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => { // Generate dynamic tool descriptions based on current CLI introspection data const dynamicDescriptions = generateToolDescriptions(this.knowledgeLoader); const schema = this.knowledgeLoader.getSchema(); const availablePlugins = schema?.properties?.tests?.items?.properties?.steps?.items?.properties ?.plugin?.enum || []; return { tools: [ { name: "get_available_examples", description: dynamicDescriptions.get_available_examples, inputSchema: { type: "object", properties: {}, required: [], }, }, { name: "search_examples", description: dynamicDescriptions.search_examples, inputSchema: { type: "object", properties: { keywords: { type: "array", items: { type: "string" }, description: "Keywords to search for in examples (e.g., ['authentication', 'api', 'database'])", }, plugin_type: { type: "string", enum: availablePlugins.concat(["any"]), description: "Optional: Filter by specific plugin type, or 'any' for all plugins", }, }, required: ["keywords"], }, }, { name: "get_rocketship_examples", description: dynamicDescriptions.get_rocketship_examples, inputSchema: { type: "object", properties: { feature_type: { type: "string", enum: availablePlugins, description: "The plugin/feature type to get examples for", }, use_case: { type: "string", description: "Specific use case or scenario you're testing", }, }, required: ["feature_type"], }, }, { name: "suggest_test_structure", description: dynamicDescriptions.suggest_test_structure, inputSchema: { type: "object", properties: { project_type: { type: "string", enum: ["frontend", "backend", "fullstack", "api", "mobile"], description: "Type of project being tested", }, user_flows: { type: "array", items: { type: "string" }, description: "Key user journeys to test (e.g., 'user registration', 'purchase flow'). TIP: Use keywords like 'authentication', 'dashboard', 'search', 'records', 'settings', 'workflow', 'reports', 'notifications' for better suggestions.", }, }, required: ["project_type"], }, }, { name: "get_schema_info", description: dynamicDescriptions.get_schema_info, inputSchema: { type: "object", properties: { section: { type: "string", enum: ["plugins", "assertions", "save", "structure", "full"], description: "Which part of the schema to focus on", }, }, required: ["section"], }, }, { name: "get_cli_guidance", description: dynamicDescriptions.get_cli_guidance, inputSchema: { type: "object", properties: { command: { type: "string", enum: this.getAvailableCLICommands(), description: "CLI command guidance needed", }, }, required: ["command"], }, }, { name: "get_rocketship_cli_installation_instructions", description: dynamicDescriptions.get_rocketship_cli_installation_instructions, inputSchema: { type: "object", properties: { platform: { type: "string", enum: [ "auto", "macos-arm64", "macos-intel", "linux", "windows", ], description: "Target platform for installation (auto-detects if not specified)", }, }, required: [], }, }, { name: "analyze_codebase_for_testing", description: dynamicDescriptions.analyze_codebase_for_testing, inputSchema: { type: "object", properties: { codebase_info: { type: "string", description: "Description of the codebase structure and functionality", }, focus_area: { type: "string", enum: [ "user_journeys", "api_endpoints", "critical_paths", "integration_points", ], description: "What aspect to focus testing on", }, suggested_flows: { type: "array", items: { type: "string" }, description: "Optional: Specific flows you think are most relevant (e.g., 'authentication', 'data-management', 'reporting')", }, }, required: ["codebase_info", "focus_area"], }, }, ], }; }); this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "get_available_examples": return this.handleGetAvailableExamples(args); case "search_examples": return this.handleSearchExamples(args); case "get_rocketship_examples": return this.handleGetExamples(args); case "suggest_test_structure": return this.handleSuggestStructure(args); case "get_schema_info": return this.handleGetSchemaInfo(args); case "get_cli_guidance": return this.handleGetCLIGuidance(args); case "get_rocketship_cli_installation_instructions": return this.handleGetInstallationInstructions(args); case "analyze_codebase_for_testing": return this.handleAnalyzeCodebase(args); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } }); } async handleGetAvailableExamples(args) { const allExamples = this.knowledgeLoader.getAllExamples(); const schema = this.knowledgeLoader.getSchema(); const availablePlugins = schema?.properties?.tests?.items?.properties?.steps?.items?.properties ?.plugin?.enum || []; let response = `# Available Rocketship Examples\n\n`; response += `Found ${allExamples.length} examples in the current codebase:\n\n`; for (const exampleName of allExamples) { const example = this.knowledgeLoader.getExample(exampleName); if (example) { response += `## ${exampleName}\n\n`; // Try to detect plugin types from the example content const detectedPlugins = this.detectPluginsInExample(example.content, availablePlugins); if (detectedPlugins.length > 0) { response += `**Plugins used:** ${detectedPlugins.join(", ")}\n\n`; } // Try to extract description or name from the YAML const description = this.extractDescriptionFromYAML(example.content); if (description) { response += `**Description:** ${description}\n\n`; } // Show first few lines as preview const lines = example.content.split("\n").slice(0, 5); response += `**Preview:**\n\`\`\`yaml\n${lines.join("\n")}\n...\n\`\`\`\n\n`; response += `Use \`get_rocketship_examples(feature_type="${detectedPlugins[0] || "http"}")\` to see full example.\n\n`; response += `---\n\n`; } } response += `## Quick Reference\n\n`; response += `**Available plugins:** ${availablePlugins.join(", ")}\n\n`; response += `**Usage:**\n`; response += `- Use \`search_examples(keywords=["keyword1", "keyword2"])\` to find specific examples\n`; response += `- Use \`get_rocketship_examples(feature_type="plugin_name")\` to get full examples\n`; return { content: [ { type: "text", text: response, }, ], }; } async handleSearchExamples(args) { const { keywords, plugin_type } = args; const allExamples = this.knowledgeLoader.getAllExamples(); const schema = this.knowledgeLoader.getSchema(); const availablePlugins = schema?.properties?.tests?.items?.properties?.steps?.items?.properties ?.plugin?.enum || []; let response = `# Search Results for: ${keywords.join(", ")}\n\n`; const matchingExamples = []; for (const exampleName of allExamples) { const example = this.knowledgeLoader.getExample(exampleName); if (example) { const matches = []; let score = 0; // Search in example name for (const keyword of keywords) { const lowerKeyword = keyword.toLowerCase(); if (exampleName.toLowerCase().includes(lowerKeyword)) { matches.push(`name contains "${keyword}"`); score += 3; } } // Search in example content const lowerContent = example.content.toLowerCase(); for (const keyword of keywords) { const lowerKeyword = keyword.toLowerCase(); if (lowerContent.includes(lowerKeyword)) { matches.push(`content contains "${keyword}"`); score += 1; } } // Filter by plugin type if specified if (plugin_type && plugin_type !== "any") { const detectedPlugins = this.detectPluginsInExample(example.content, availablePlugins); if (!detectedPlugins.includes(plugin_type)) { continue; // Skip this example } } if (matches.length > 0) { matchingExamples.push({ name: exampleName, content: example.content, score, matches, }); } } } // Sort by relevance score matchingExamples.sort((a, b) => b.score - a.score); if (matchingExamples.length === 0) { response += `No examples found matching: ${keywords.join(", ")}\n\n`; response += `**Available examples:** ${allExamples.join(", ")}\n\n`; response += `**Available plugins:** ${availablePlugins.join(", ")}\n\n`; response += `Try broader keywords or use \`get_available_examples()\` to see all examples.\n`; } else { response += `Found ${matchingExamples.length} matching examples:\n\n`; for (const match of matchingExamples.slice(0, 5)) { // Show top 5 matches response += `## ${match.name} (Score: ${match.score})\n\n`; response += `**Matches:** ${match.matches.join(", ")}\n\n`; const detectedPlugins = this.detectPluginsInExample(match.content, availablePlugins); if (detectedPlugins.length > 0) { response += `**Plugins:** ${detectedPlugins.join(", ")}\n\n`; } const description = this.extractDescriptionFromYAML(match.content); if (description) { response += `**Description:** ${description}\n\n`; } // Show relevant lines (lines containing keywords) const relevantLines = this.findRelevantLines(match.content, keywords); if (relevantLines.length > 0) { response += `**Relevant content:**\n\`\`\`yaml\n${relevantLines .slice(0, 10) .join("\n")}\n\`\`\`\n\n`; } response += `Use \`get_rocketship_examples(feature_type="${detectedPlugins[0] || "http"}")\` to see full example.\n\n`; response += `---\n\n`; } if (matchingExamples.length > 5) { response += `... and ${matchingExamples.length - 5} more matches. Refine your search for better results.\n\n`; } } return { content: [ { type: "text", text: response, }, ], }; } detectPluginsInExample(content, availablePlugins) { const plugins = []; const lowerContent = content.toLowerCase(); for (const plugin of availablePlugins) { if (lowerContent.includes(`plugin: ${plugin}`)) { plugins.push(plugin); } } return [...new Set(plugins)]; // Remove duplicates } extractDescriptionFromYAML(content) { const lines = content.split("\n"); for (const line of lines) { const trimmed = line.trim(); if (trimmed.startsWith("description:")) { return trimmed.replace("description:", "").trim().replace(/['"]/g, ""); } if (trimmed.startsWith("name:")) { return trimmed.replace("name:", "").trim().replace(/['"]/g, ""); } } return null; } findRelevantLines(content, keywords) { const lines = content.split("\n"); const relevantLines = []; for (const line of lines) { const lowerLine = line.toLowerCase(); for (const keyword of keywords) { if (lowerLine.includes(keyword.toLowerCase())) { relevantLines.push(line); break; } } } return relevantLines; } async handleGetExamples(args) { const { feature_type, use_case } = args; // Get embedded examples const allExamples = this.knowledgeLoader.getAllExamples(); const relevantExamples = allExamples.filter((name) => name.includes(feature_type) || (feature_type === "browser" && name.includes("browser")) || (feature_type === "http" && (name.includes("http") || name.includes("request"))) || (feature_type === "sql" && name.includes("sql")) || (feature_type === "agent" && name.includes("agent"))); let response = `# Real Rocketship Examples for ${feature_type}\n\n`; if (use_case) { response += `Use case: ${use_case}\n\n`; } // Add suggestions for frontend projects if (feature_type === "browser" || use_case?.toLowerCase().includes("frontend")) { response += `šŸ’” **Frontend Testing Considerations:**\n`; response += `- Browser plugin is great for testing user journeys\n`; response += `- Consider testing user flows in addition to API endpoints\n`; response += `- Focus on the most important customer paths\n\n`; } // Use extracted file structure from CLI introspection const cliData = this.knowledgeLoader.getCLIData(); if (cliData?.usage?.file_structure) { response += `## File Structure\n`; response += `Create this structure (YOU must create these files):\n`; if (cliData.usage.file_structure.examples) { Object.entries(cliData.usage.file_structure.examples).forEach(([key, example]) => { response += `\`\`\`\n${example}\`\`\`\n\n`; }); } else if (cliData.usage.file_structure.pattern) { response += `Pattern: \`${cliData.usage.file_structure.pattern}\`\n\n`; } } // Show real examples response += `## Real Examples from Codebase\n\n`; // Show all relevant examples (we have fewer now) for (const exampleName of relevantExamples) { const example = this.knowledgeLoader.getExample(exampleName); if (example) { response += `### ${exampleName}\n\n`; response += `\`\`\`yaml\n${example.content}\`\`\`\n\n`; } } // If no specific examples found, show a generic one if (relevantExamples.length === 0) { const firstExample = this.knowledgeLoader.getExample(allExamples[0]); if (firstExample) { response += `### Example (${allExamples[0]})\n\n`; response += `\`\`\`yaml\n${firstExample.content}\`\`\`\n\n`; } } // Use extracted syntax patterns from CLI introspection if (cliData?.usage?.syntax_patterns) { response += `## Syntax Rules (from Documentation)\n\n`; if (cliData.usage.syntax_patterns.variables) { response += `**Variable Types:**\n`; const vars = cliData.usage.syntax_patterns.variables; if (vars.config && vars.config.length > 0) { response += `- Config variables: Examples: ${vars.config .slice(0, 3) .map((v) => `\`${v}\``) .join(", ")}\n`; } if (vars.environment && vars.environment.length > 0) { response += `- Environment variables: Examples: ${vars.environment .slice(0, 3) .map((v) => `\`${v}\``) .join(", ")}\n`; } if (vars.runtime && vars.runtime.length > 0) { response += `- Runtime variables: Examples: ${vars.runtime .slice(0, 3) .map((v) => `\`${v}\``) .join(", ")}\n`; } response += `\n`; } if (cliData.usage.syntax_patterns.save_operations && Object.keys(cliData.usage.syntax_patterns.save_operations).length > 0) { response += `**Save Operations:**\n`; Object.entries(cliData.usage.syntax_patterns.save_operations) .slice(0, 2) .forEach(([key, example]) => { response += `\`\`\`yaml\n${example}\`\`\`\n`; }); response += `\n`; } } // Use extracted plugin-specific guidance from CLI introspection if (feature_type === "browser_use" && cliData?.usage?.syntax_patterns?.plugins?.browser_use) { response += `## Browser Use Plugin Guidance (from Documentation)\n\n`; cliData.usage.syntax_patterns.plugins.browser_use.forEach((context, index) => { response += `### Example ${index + 1}\n\`\`\`yaml\n${context}\`\`\`\n\n`; }); } else if (feature_type === "playwright" && cliData?.usage?.syntax_patterns?.plugins?.playwright) { response += `## Playwright Plugin Guidance (from Documentation)\n\n`; cliData.usage.syntax_patterns.plugins.playwright.forEach((context, index) => { response += `### Example ${index + 1}\n\`\`\`yaml\n${context}\`\`\`\n\n`; }); } // Use extracted CLI commands for next steps response += `## Next Steps\n`; response += `1. YOU create the directory structure shown above\n`; response += `2. YOU write YAML test files (e.g. auth_login.yaml, checkout.yaml) based on these examples\n`; // Use actual CLI commands from introspection if (cliData?.usage?.common_patterns) { const runPatterns = cliData.usage.common_patterns.filter((p) => p.command.includes("run") && (p.command.includes("-ad") || p.command.includes("--dir"))); if (runPatterns.length > 0) { response += `3. Run: \`${runPatterns[0].command}\` to execute tests\n`; } } return { content: [ { type: "text", text: response, }, ], }; } async handleSuggestStructure(args) { const { project_type, user_flows } = args; const cliData = this.knowledgeLoader.getCLIData(); let response = `# Rocketship Test Structure for ${project_type} Project\n\n`; response += `šŸ’” **Note: YOU create all directories and files yourself**\n\n`; // Use extracted file structure patterns from CLI introspection if (cliData?.usage?.file_structure) { response += `## Recommended Structure (from Documentation)\n\n`; if (cliData.usage.file_structure.pattern) { response += `**Pattern:** \`${cliData.usage.file_structure.pattern}\`\n\n`; } if (cliData.usage.file_structure.examples) { Object.entries(cliData.usage.file_structure.examples).forEach(([key, example]) => { response += `### ${key}\n\`\`\`\n${example}\`\`\`\n\n`; }); } // Show real examples from actual codebase if (cliData.usage.file_structure.real_examples) { response += `### Real Examples in Codebase\n`; Object.entries(cliData.usage.file_structure.real_examples).forEach(([dir, info]) => { response += `- \`${info.path}\` āœ“\n`; }); response += `\n`; } } // Project-specific guidance with dynamic examples const availablePlugins = this.knowledgeLoader.getSchema()?.properties?.tests?.items?.properties ?.steps?.items?.properties?.plugin?.enum || []; if (project_type === "frontend" || project_type === "fullstack") { response += `## 🌐 Frontend Testing Strategy\n\n`; if (availablePlugins.includes("browser_use") || availablePlugins.includes("playwright")) { response += `**Available: browser_use and playwright plugins for user journey testing**\n\n`; response += `For complete working examples, use:\n`; response += `\`\`\`\nget_rocketship_examples feature_type="browser_use"\n\`\`\`\n\n`; } // Generate user flow structure dynamically if (user_flows && user_flows.length > 0) { response += `### Suggested Structure for Your Flows\n\`\`\`\n`; response += `.rocketship/\n`; for (const flow of user_flows.slice(0, 5)) { const cleanName = flow .toLowerCase() .replace(/\s+/g, "-") .replace(/[^a-z0-9-]/g, ""); response += `ā”œā”€ā”€ ${cleanName}/\n`; response += `│ └── rocketship.yaml\n`; } response += `\`\`\`\n\n`; } } else { response += `## šŸ”Œ API Testing Strategy\n\n`; response += `Focus on user journey endpoints (not just coverage)\n\n`; if (availablePlugins.includes("http")) { response += `**Available: HTTP plugin for API testing**\n\n`; response += `For complete working examples, use:\n`; response += `\`\`\`\nget_rocketship_examples feature_type="http"\n\`\`\`\n\n`; } } // Use extracted variable syntax from CLI introspection if (cliData?.usage?.syntax_patterns?.variables) { response += `### Variable Types (from Documentation)\n\n`; const vars = cliData.usage.syntax_patterns.variables; if (vars.config && vars.config.length > 0) { response += `- **Config variables**: ${vars.config .slice(0, 2) .map((v) => `\`${v}\``) .join(", ")}\n`; } if (vars.environment && vars.environment.length > 0) { response += `- **Environment variables**: ${vars.environment .slice(0, 2) .map((v) => `\`${v}\``) .join(", ")}\n`; } if (vars.runtime && vars.runtime.length > 0) { response += `- **Runtime variables**: ${vars.runtime .slice(0, 2) .map((v) => `\`${v}\``) .join(", ")}\n`; } response += `\n`; } // Use extracted CLI commands from introspection response += `## CLI Commands (from Current CLI)\n\n`; if (cliData?.usage?.common_patterns) { response += `\`\`\`bash\n`; // Find validate patterns const validatePatterns = cliData.usage.common_patterns.filter((p) => p.command.includes("validate")); if (validatePatterns.length > 0) { response += `# ${validatePatterns[0].description}\n`; response += `${validatePatterns[0].command}\n\n`; } // Find run patterns const runPatterns = cliData.usage.common_patterns.filter((p) => p.command.includes("run")); runPatterns.slice(0, 3).forEach((pattern) => { response += `# ${pattern.description}\n`; response += `${pattern.command}\n\n`; }); response += `\`\`\`\n\n`; } // Use extracted patterns for key reminders response += `## Key Information (from Documentation)\n`; if (cliData?.usage?.file_structure?.pattern) { response += `- File pattern: \`${cliData.usage.file_structure.pattern}\`\n`; } if (cliData?.usage?.syntax_patterns?.save_operations) { const saveExample = Object.values(cliData.usage.syntax_patterns.save_operations)[0]; if (saveExample) { response += `- Save syntax: See documentation for \`save:\` operations\n`; } } return { content: [ { type: "text", text: response, }, ], }; } async handleGetSchemaInfo(args) { const { section } = args; const schema = this.knowledgeLoader.getSchema(); let response = `# Rocketship Schema Information\n\n`; if (section === "plugins" || section === "full") { response += `## Available Plugins\n\n`; const pluginEnum = schema?.properties?.tests?.items?.properties?.steps?.items?.properties ?.plugin?.enum; if (pluginEnum) { response += `The following plugins are available in the current schema:\n\n`; // Just list the plugins - let users explore examples for details for (const plugin of pluginEnum) { response += `- **${plugin}**`; // Check if we have examples for this plugin const examples = this.knowledgeLoader .getAllExamples() .filter((e) => e.includes(plugin)); if (examples.length > 0) { response += ` - See examples: ${examples.join(", ")}\n`; } else { response += `\n`; } } response += `\nFor detailed usage and configuration, use \`get_rocketship_examples feature_type="<plugin>"\`.\n`; } response += `\n`; } if (section === "assertions" || section === "full") { response += `## Valid Assertion Types\n\n`; const assertionTypes = schema?.properties?.tests?.items?.properties?.steps?.items?.properties ?.assertions?.items?.properties?.type?.enum; if (assertionTypes) { response += `Available assertion types from the schema:\n\n`; for (const type of assertionTypes) { response += `- **${type}**\n`; } response += `\nFor usage examples and syntax, use \`get_rocketship_examples\` to see real test files.\n`; } response += `\n`; } if (section === "save" || section === "full") { response += `## Save Syntax\n\n`; response += `Save data from responses for use in later steps:\n\n`; response += `\`\`\`yaml\n`; response += `save:\n`; response += ` - json_path: ".field_name" # No $ prefix!\n`; response += ` as: "variable_name"\n`; response += ` - header: "Content-Type"\n`; response += ` as: "content_type"\n`; response += `\`\`\`\n\n`; response += `**Variable usage examples:**\n`; response += `- Config: \`{{ .vars.api_url }}\` (from vars section)\n`; response += `- Environment: \`{{ .env.API_KEY }}\` (from system)\n`; response += `- Runtime: \`{{ user_id }}\` (from save operations)\n\n`; } if (section === "structure" || section === "full") { response += `## File Structure Requirements\n\n`; response += `Based on the current schema:\n\n`; // Dynamically extract all structural information from schema const requiredTopLevel = schema?.required || []; const topLevelProps = schema?.properties || {}; response += `### Top-level fields:\n`; for (const [key, prop] of Object.entries(topLevelProps)) { const isRequired = requiredTopLevel.includes(key); response += `- **${key}** (${isRequired ? "required" : "optional"})`; if (prop && typeof prop === "object" && "type" in prop) { response += `: ${prop.type}`; } response += `\n`; } response += `\n`; // Show test structure if available if (topLevelProps.tests?.items?.properties) { const testRequired = topLevelProps.tests.items.required || []; const testProps = topLevelProps.tests.items.properties || {}; response += `### Test structure:\n`; for (const [key, prop] of Object.entries(testProps)) { const isRequired = testRequired.includes(key); response += `- **${key}** (${isRequired ? "required" : "optional"})`; if (prop && typeof prop === "object" && "type" in prop) { response += `: ${prop.type}`; } response += `\n`; } response += `\n`; } // Show step structure if available if (topLevelProps.tests?.items?.properties?.steps?.items?.properties) { const stepRequired = topLevelProps.tests.items.properties.steps.items.required || []; const stepProps = topLevelProps.tests.items.properties.steps.items.properties || {}; response += `### Step structure:\n`; for (const [key, prop] of Object.entries(stepProps)) { const isRequired = stepRequired.includes(key); response += `- **${key}** (${isRequired ? "required" : "optional"})`; if (prop && typeof prop === "object" && "type" in prop) { response += `: ${prop.type}`; } response += `\n`; } response += `\n`; } response += `For complete examples with proper syntax, use \`get_rocketship_examples\`.\n\n`; } return { content: [ { type: "text", text: response, }, ], }; } async handleGetCLIGuidance(args) { const { command } = args; const cliData = this.knowledgeLoader.getCLIData(); let response = `# Rocketship CLI Guidance: ${command}\n\n`; if (cliData?.version?.version) { response += `*Current CLI Version: ${cliData.version.version}*\n\n`; } // Handle specific command help if (cliData?.help?.[command]) { const commandHelp = cliData.help[command]; response += `## Command: \`rocketship ${command}\`\n\n`; if (commandHelp.description) { response += `**Description:** ${commandHelp.description}\n\n`; } if (commandHelp.help) { response += `### Usage Information\n`; response += `\`\`\`\n${commandHelp.help}\`\`\`\n\n`; } // Add usage patterns for this specific command if (cliData?.usage?.common_patterns) { const commandPatterns = cliData.usage.common_patterns.filter((p) => p.command.includes(command) || p.name.toLowerCase().includes(command)); if (commandPatterns.length > 0) { response += `### Common Usage Patterns\n`; response += `\`\`\`bash\n`; commandPatterns.forEach((pattern) => { response += `# ${pattern.description}\n`; response += `${pattern.command}\n\n`; }); response += `\`\`\`\n\n`; } } // Add examples from CLI help for this command if (cliData?.usage?.examples_from_help?.[command]) { response += `### Examples from CLI Help\n`; response += `\`\`\`\n${cliData.usage.examples_from_help[command]}\`\`\`\n\n`; } } else if (command === "structure") { // Special handling for structure guidance response += `## File Structure Guidance\n\n`; if (cliData?.usage?.file_structure) { response += `### Recommended Structure\n`; if (cliData.usage.file_structure.pattern) { response += `Pattern: \`${cliData.usage.file_structure.pattern}\`\n\n`; } if (cliData.usage.file_structure.examples) { Object.entries(cliData.usage.file_structure.examples).forEach(([key, example]) => { response += `\`\`\`\n${example}\`\`\`\n\n`; }); } } if (cliData?.help?.validate?.help) { response += `### Validation Command\n`; response += `\`\`\`\n${cliData.help.validate.help}\`\`\`\n\n`; } } else if (command === "usage") { // Special handling for general usage patterns response += `## General Usage Patterns\n\n`; if (cliData?.usage?.common_patterns) { response += `### Common Command Patterns\n`; response += `\`\`\`bash\n`; cliData.usage.common_patterns.forEach((pattern) => { response += `# ${pattern.description}\n`; response += `${pattern.command}\n\n`; }); response += `\`\`\`\n\n`; } } else { response += `No specific guidance available for command: ${command}\n\n`; response += `Available commands: ${this.getAvailableCLICommands().join(", ")}\n\n`; } // Use actual flag data - NO FALLBACKS response += `## Current CLI Flags\n\n`; if (cliData?.flags && Object.keys(cliData.flags).length > 0) { for (const [flag, info] of Object.entries(cliData.flags)) { const flagInfo = info; response += `- \`${flagInfo.short ? flagInfo.short + ", " : ""}${flagInfo.long}\`: ${flagInfo.description}\n`; } } else { response += `Flag documentation not available in CLI introspection data.\n`; } response += `\n`; // Installation instructions moved to dedicated get_rocketship_cli_installation_instructions tool response += `## Installation\n\n`; response += `For Rocketship CLI installation instructions, use the \`get_rocketship_cli_installation_instructions\` tool.\n\n`; return { content: [ { type: "text", text: response, }, ], }; } async handleGetInstallationInstructions(args) { const { platform = "auto" } = args; const cliData = this.knowledgeLoader.getCLIData(); let response = `# Rocketship CLI Installation Instructions\n\n`; if (cliData?.version?.version) { response += `*Installing: ${cliData.version.version}*\n\n`; } // Platform-specific instructions response += `## Installation Methods\n\n`; if (cliData?.installation) { // Show recommended method if available if (cliData.installation.recommended) { response += `**Recommended:** ${cliData.installation.recommended}\n\n`; } // Show available installation methods if (cliData.installation.methods && cliData.installation.methods.length > 0) { response += `### āœ… Available Methods\n\n`; for (const method of cliData.installation.methods) { // Platform filtering if (platform !== "auto") { const methodPlatform = this.detectPlatformFromMethod(method); if (methodPlatform && methodPlatform !== platform) { continue; // Skip methods for other platforms } } response += `#### ${method.name}\n\n`; response += `${method.description}\n\n`; response += `\`\`\`bash\n${method.command}\n\`\`\`\n\n`; } } // Show methods that are NOT available if (cliData.installation.notAvailable && cliData.installation.notAvailable.length > 0) { response += `### āŒ NOT Available\n\n`; response += `The following installation methods are **NOT supported**:\n\n`; for (const na of cliData.installation.notAvailable) { response += `- ${na}\n`; } response += `\n`; } // Include complete installation section from documentation if (cliData.installation.fromReadme) {