guardz-generator-mcp
Version:
MCP (Model Context Protocol) Server for TypeScript Type Guard Generation - Generate runtime validation functions from TypeScript types
437 lines • 18.4 kB
JavaScript
#!/usr/bin/env node
/**
* MCP (Model Context Protocol) Server for TypeScript Type Guard Generation
*
* This MCP server generates TypeScript type guards from source code through a standardized
* interface, allowing AI assistants to create robust runtime validation functions.
* The primary functionality is type guard generation - all other tools support this goal.
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
import { TypeGuardGenerator } from 'guardz-generator/dist/core/generators/type-guard/type-guard-generator.js';
import { ServiceFactory } from 'guardz-generator/dist/infrastructure/factories/service-factory.js';
import { setDebugMode, setVerboseMode, } from 'guardz-generator/dist/shared/utils/logging.js';
class GuardzGeneratorMCPServer {
server;
constructor() {
this.server = new Server({
name: 'guardz-generator-mcp',
version: '1.0.0',
});
this.setupToolHandlers();
}
setupToolHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'generate_type_guards',
description: 'Generate TypeScript type guards from source files - PRIMARY FUNCTIONALITY',
inputSchema: {
type: 'object',
properties: {
files: {
type: 'array',
items: { type: 'string' },
description: 'TypeScript files to process for type guard generation',
},
config: {
type: 'string',
description: 'Path to config file (defaults to guardz.generator.config.ts)',
},
type: {
type: 'string',
description: 'Generate type guard for specific type',
},
guardName: {
type: 'string',
description: 'Custom name for the type guard function',
},
includes: {
type: 'array',
items: { type: 'string' },
description: 'Glob patterns for files to include',
},
excludes: {
type: 'array',
items: { type: 'string' },
description: 'Glob patterns for files to exclude',
},
postProcess: {
type: 'boolean',
description: 'Run lint, prettier, and tsc on generated type guard files (default: true)',
},
verbose: {
type: 'boolean',
description: 'Enable verbose logging',
},
debug: {
type: 'boolean',
description: 'Enable debug logging (creates log file)',
},
},
required: ['files'],
},
},
{
name: 'discover_files',
description: 'Helper to find TypeScript files for type guard generation',
inputSchema: {
type: 'object',
properties: {
cliFiles: {
type: 'array',
items: { type: 'string' },
description: 'Files specified via CLI for type guard generation',
},
cliIncludes: {
type: 'array',
items: { type: 'string' },
description: 'Include patterns from CLI for type guard generation',
},
cliExcludes: {
type: 'array',
items: { type: 'string' },
description: 'Exclude patterns from CLI for type guard generation',
},
configPath: {
type: 'string',
description: 'Path to config file',
},
},
},
},
{
name: 'validate_typescript',
description: 'Helper to validate TypeScript before generating type guards',
inputSchema: {
type: 'object',
properties: {
files: {
type: 'array',
items: { type: 'string' },
description: 'TypeScript files to validate before type guard generation',
},
},
required: ['files'],
},
},
{
name: 'format_code',
description: 'Helper to format generated type guard code',
inputSchema: {
type: 'object',
properties: {
files: {
type: 'array',
items: { type: 'string' },
description: 'Files to format (typically generated type guard files)',
},
},
required: ['files'],
},
},
{
name: 'lint_code',
description: 'Helper to lint generated type guard code',
inputSchema: {
type: 'object',
properties: {
files: {
type: 'array',
items: { type: 'string' },
description: 'Files to lint (typically generated type guard files)',
},
fix: {
type: 'boolean',
description: 'Automatically fix linting issues',
},
},
required: ['files'],
},
},
{
name: 'get_project_info',
description: 'Helper to get project context for type guard generation',
inputSchema: {
type: 'object',
properties: {},
},
},
],
};
});
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'generate_type_guards':
return await this.generateTypeGuards(args);
case 'discover_files':
return await this.discoverFiles(args);
case 'validate_typescript':
return await this.validateTypeScript(args);
case 'format_code':
return await this.formatCode(args);
case 'lint_code':
return await this.lintCode(args);
case 'get_project_info':
return await this.getProjectInfo();
default:
throw new Error(`Unknown tool: ${name}`);
}
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
});
}
async generateTypeGuards(args) {
const { files, config, type, guardName, includes, excludes, postProcess = true, verbose = false, debug = false, } = args;
// Set logging modes
setVerboseMode(verbose);
setDebugMode(debug);
try {
// Use the file discovery pipeline
const fileDiscoveryPipeline = ServiceFactory.getFileDiscoveryPipeline();
const discoveryOptions = {
cliFiles: files,
cliIncludes: includes,
cliExcludes: excludes,
configPath: config,
};
const discoveryResult = await fileDiscoveryPipeline.discoverFiles(discoveryOptions);
if (discoveryResult.files.length === 0) {
return {
content: [
{
type: 'text',
text: `No files found from ${discoveryResult.source} source. Please provide files argument, configure includes in guardz.generator.config.ts, or ensure tsconfig.json exists.`,
},
],
};
}
// Create TypeScript program
const typescriptProgramPipeline = ServiceFactory.getTypeScriptProgramPipeline();
const programOptions = {
sourceFiles: discoveryResult.files,
tsConfigPath: config,
};
const programResult = await typescriptProgramPipeline.createProgram(programOptions);
// Generate type guards
const generator = new TypeGuardGenerator(discoveryResult.files, programResult.program);
const options = {
type,
guardName,
postProcess,
};
const generatedFiles = generator.generateAllTypeGuards(options);
// Write files to same directory
await generator.writeTypeGuardsToSameDirectory(generatedFiles);
// Run post-processing if enabled
if (postProcess) {
await generator.runPostProcessing(generatedFiles);
}
const fileList = generatedFiles
.map((f) => f.fileName)
.join('\n- ');
return {
content: [
{
type: 'text',
text: `Successfully generated ${generatedFiles.length} type guard files:\n\n- ${fileList}\n\nFiles were written to the same directory as their source files.`,
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error generating type guards: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
async discoverFiles(args) {
const { cliFiles, cliIncludes, cliExcludes, configPath } = args;
try {
const fileDiscoveryPipeline = ServiceFactory.getFileDiscoveryPipeline();
const discoveryOptions = {
cliFiles,
cliIncludes,
cliExcludes,
configPath,
};
const discoveryResult = await fileDiscoveryPipeline.discoverFiles(discoveryOptions);
return {
content: [
{
type: 'text',
text: `Discovered ${discoveryResult.files.length} files from ${discoveryResult.source} source:\n\n${discoveryResult.files.join('\n')}`,
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error discovering files: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
async validateTypeScript(args) {
const { files } = args;
try {
const typescriptCompiler = ServiceFactory.getTypeScriptCompiler();
await typescriptCompiler.checkFiles(files);
return {
content: [
{
type: 'text',
text: `Successfully validated ${files.length} TypeScript files. No compilation errors found.`,
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `TypeScript validation failed: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
async formatCode(args) {
const { files } = args;
try {
const prettier = ServiceFactory.getPrettier();
const fileSystem = ServiceFactory.getFileSystem();
for (const file of files) {
const content = await fileSystem.readFile(file);
const formatted = await prettier.format(content, { filepath: file });
await fileSystem.writeFile(file, formatted);
}
return {
content: [
{
type: 'text',
text: `Successfully formatted ${files.length} files using Prettier.`,
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error formatting code: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
async lintCode(args) {
const { files, fix = false } = args;
try {
const eslint = ServiceFactory.getESLint();
if (fix) {
await eslint.fixFiles(files);
return {
content: [
{
type: 'text',
text: `Successfully linted and fixed ${files.length} files using ESLint.`,
},
],
};
}
else {
// For now, we'll just run the fix command as the basic lint check
await eslint.fixFiles(files);
return {
content: [
{
type: 'text',
text: `Successfully linted ${files.length} files using ESLint.`,
},
],
};
}
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error linting code: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
async getProjectInfo() {
try {
const fileSystem = ServiceFactory.getFileSystem();
// Check for common config files
const configFiles = [
'guardz.generator.config.ts',
'tsconfig.json',
'package.json',
];
const existingConfigs = [];
for (const config of configFiles) {
if (await fileSystem.fileExists(config)) {
existingConfigs.push(config);
}
}
return {
content: [
{
type: 'text',
text: `Project Information:\n\nAvailable configuration files:\n${existingConfigs.map(f => `- ${f}`).join('\n')}\n\nGuardz Generator MCP version: 1.0.0\nCompatible with guardz-generator: 1.11.6`,
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting project info: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Guardz Generator MCP Server started');
}
}
// Start the server
const server = new GuardzGeneratorMCPServer();
server.run().catch(console.error);
//# sourceMappingURL=mcp-guardz-generator.js.map