#!/usr/bin/env node
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 JSZip from "jszip";
import fs from "fs/promises";
import path from "path";
interface MavenProject {
groupId: string;
artifactId: string;
version: string;
name?: string;
description?: string;
javaVersion?: string;
files: {
[filePath: string]: string;
};
}
const server = new Server(
{
name: "maven-project-generator",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "create_maven_project",
description: "Creer un nouveau projet Maven vide avec la structure standard",
inputSchema: {
type: "object",
properties: {
groupId: {
type: "string",
description: "Group ID du projet (ex: com.example)",
},
artifactId: {
type: "string",
description: "Artifact ID du projet (ex: mon-plugin)",
},
version: {
type: "string",
description: "Version du projet (ex: 1.0.0)",
default: "1.0.0",
},
name: {
type: "string",
description: "Nom du projet",
},
description: {
type: "string",
description: "Description du projet",
},
javaVersion: {
type: "string",
description: "Version de Java (ex: 17)",
default: "17",
},
projectType: {
type: "string",
description: "Type de projet Maven",
enum: ["plugin", "library", "application"],
default: "application",
},
},
required: ["groupId", "artifactId"],
},
},
{
name: "add_java_file",
description: "Ajouter un fichier Java au projet Maven",
inputSchema: {
type: "object",
properties: {
packageName: {
type: "string",
description: "Nom du package Java (ex: com.example.myplugin). Optionnel si le package est dans le contenu Java.",
},
className: {
type: "string",
description: "Nom de la classe Java. Optionnel si detectable depuis le contenu.",
},
content: {
type: "string",
description: "Contenu complet du fichier Java",
},
type: {
type: "string",
description: "Type de fichier",
enum: ["main", "test"],
default: "main",
},
filePath: {
type: "string",
description: "Chemin complet du fichier (optionnel, sera auto-genere si non fourni)",
},
},
required: ["content"],
},
},
{
name: "add_resource_file",
description: "Ajouter un fichier de ressources au projet",
inputSchema: {
type: "object",
properties: {
fileName: {
type: "string",
description: "Nom du fichier de ressource",
},
content: {
type: "string",
description: "Contenu du fichier",
},
type: {
type: "string",
description: "Type de ressource",
enum: ["main", "test"],
default: "main",
},
},
required: ["fileName", "content"],
},
},
{
name: "add_any_file",
description: "Ajouter n'importe quel fichier au projet avec chemin personnalise",
inputSchema: {
type: "object",
properties: {
filePath: {
type: "string",
description: "Chemin complet du fichier dans le projet (ex: src/main/java/com/example/Utils.java ou docs/README.md)",
},
content: {
type: "string",
description: "Contenu du fichier",
},
},
required: ["filePath", "content"],
},
},
{
name: "set_pom_xml",
description: "Definir ou modifier le contenu du pom.xml",
inputSchema: {
type: "object",
properties: {
content: {
type: "string",
description: "Contenu complet du pom.xml",
},
},
required: ["content"],
},
},
{
name: "generate_zip",
description: "Generer et sauvegarder le projet Maven en ZIP",
inputSchema: {
type: "object",
properties: {
outputPath: {
type: "string",
description: "Chemin de sortie pour le fichier ZIP (optionnel, defaut: ./maven-project.zip)",
},
},
},
},
{
name: "clear_project",
description: "Vider le projet en cours",
inputSchema: {
type: "object",
properties: {},
},
},
{
name: "list_files",
description: "Lister tous les fichiers du projet en cours",
inputSchema: {
type: "object",
properties: {},
},
},
],
};
});
let currentProject: MavenProject = {
groupId: "",
artifactId: "",
version: "1.0.0",
files: {},
};
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "create_maven_project":
return await createMavenProject(args);
case "add_java_file":
return await addJavaFile(args);
case "add_resource_file":
return await addResourceFile(args);
case "add_any_file":
return await addAnyFile(args);
case "set_pom_xml":
return await setPomXml(args);
case "generate_zip":
return await generateZip(args);
case "clear_project":
return await clearProject();
case "list_files":
return await listFiles();
default:
throw new Error(`Outil inconnu: ${name}`);
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Erreur: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
});
async function createMavenProject(args: any) {
currentProject = {
groupId: args.groupId,
artifactId: args.artifactId,
version: args.version || "1.0.0",
name: args.name || args.artifactId,
description: args.description || "",
javaVersion: args.javaVersion || "17",
files: {},
};
const pomXml = generateDefaultPom(currentProject, args.projectType || "application");
currentProject.files["pom.xml"] = pomXml;
if (args.projectType === "plugin") {
const mainClass = generateDefaultMojo(currentProject);
const packagePath = currentProject.groupId.replace(/\./g, "/");
currentProject.files[`src/main/java/${packagePath}/MyMojo.java`] = mainClass;
} else {
const mainClass = generateDefaultMainClass(currentProject);
const packagePath = currentProject.groupId.replace(/\./g, "/");
currentProject.files[`src/main/java/${packagePath}/Main.java`] = mainClass;
}
return {
content: [
{
type: "text",
text: `Projet Maven cree avec succes!\n\n` +
`Details du projet:\n` +
`- Group ID: ${currentProject.groupId}\n` +
`- Artifact ID: ${currentProject.artifactId}\n` +
`- Version: ${currentProject.version}\n` +
`- Type: ${args.projectType || "application"}\n` +
`- Java Version: ${currentProject.javaVersion}\n\n` +
`Structure creee:\n` +
`- pom.xml\n` +
`- ${args.projectType === "plugin" ? "MyMojo.java (classe de plugin)" : "Main.java (classe principale)"}\n\n` +
`Prochaines etapes:\n` +
`- Utilisez 'add_java_file' pour ajouter des classes\n` +
`- Utilisez 'add_resource_file' pour ajouter des ressources\n` +
`- Utilisez 'generate_zip' pour creer le package final`,
},
],
};
}
function extractJavaInfo(javaContent: string): { packageName: string | null, className: string | null } {
let packageName: string | null = null;
let className: string | null = null;
const packageMatch = javaContent.match(/^\s*package\s+([\w.]+)\s*;/m);
if (packageMatch) {
packageName = packageMatch[1];
}
const publicClassMatch = javaContent.match(/public\s+(?:class|interface|enum)\s+(\w+)/);
if (publicClassMatch) {
className = publicClassMatch[1];
} else {
const anyClassMatch = javaContent.match(/(?:class|interface|enum)\s+(\w+)/);
if (anyClassMatch) {
className = anyClassMatch[1];
}
}
return { packageName, className };
}
async function addJavaFile(args: any) {
const { content, type = "main" } = args;
let { packageName, className, filePath } = args;
if (!packageName || !className) {
const extracted = extractJavaInfo(content);
packageName = packageName || extracted.packageName;
className = className || extracted.className;
}
if (!filePath) {
if (!packageName || !className) {
throw new Error("Impossible de determiner le package et la classe. Veuillez les specifier ou verifier le contenu Java.");
}
const packagePath = packageName.replace(/\./g, "/");
filePath = `src/${type}/java/${packagePath}/${className}.java`;
}
currentProject.files[filePath] = content;
return {
content: [
{
type: "text",
text: `Fichier Java ajoute avec succes!\n\n` +
`Fichier: ${filePath}\n` +
`Package: ${packageName || "N/A"}\n` +
`Classe: ${className || "N/A"}\n` +
`Type: ${type}\n\n` +
`Repertoires crees automatiquement dans la structure Maven.`,
},
],
};
}
async function addResourceFile(args: any) {
const { fileName, content, type = "main" } = args;
const filePath = `src/${type}/resources/${fileName}`;
currentProject.files[filePath] = content;
return {
content: [
{
type: "text",
text: `Fichier de ressource ajoute!\n\n` +
`Fichier: ${filePath}\n` +
`Type: ${type}\n\n` +
`Repertoires crees automatiquement si necessaire.`,
},
],
};
}
async function addAnyFile(args: any) {
const { filePath, content } = args;
currentProject.files[filePath] = content;
return {
content: [
{
type: "text",
text: `Fichier ajoute!\n\n` +
`Fichier: ${filePath}\n\n` +
`Tous les repertoires necessaires seront crees automatiquement dans le ZIP.`,
},
],
};
}
async function setPomXml(args: any) {
currentProject.files["pom.xml"] = args.content;
return {
content: [
{
type: "text",
text: "pom.xml mis a jour avec succes!",
},
],
};
}
async function listFiles() {
if (Object.keys(currentProject.files).length === 0) {
return {
content: [
{
type: "text",
text: "Aucun projet en cours. Creez d'abord un projet avec 'create_maven_project'.",
},
],
};
}
const filesList = Object.keys(currentProject.files)
.sort()
.map(file => `- ${file}`)
.join('\n');
return {
content: [
{
type: "text",
text: `Projet: ${currentProject.artifactId}\n` +
`Nombre de fichiers: ${Object.keys(currentProject.files).length}\n\n` +
`Liste des fichiers:\n${filesList}`,
},
],
};
}
async function generateZip(args: any) {
if (Object.keys(currentProject.files).length === 0) {
throw new Error("Aucun projet a generer. Creez d'abord un projet avec 'create_maven_project'.");
}
const zip = new JSZip();
const projectFolder = zip.folder(currentProject.artifactId);
if (!projectFolder) {
throw new Error("Erreur lors de la creation du dossier dans le ZIP");
}
for (const [filePath, content] of Object.entries(currentProject.files)) {
projectFolder.file(filePath, content);
}
const zipContent = await zip.generateAsync({ type: "nodebuffer" });
const outputPath = args.outputPath || `./maven-project-${currentProject.artifactId}.zip`;
await fs.writeFile(outputPath, zipContent);
const absolutePath = path.resolve(outputPath);
return {
content: [
{
type: "text",
text: `Projet Maven genere avec succes!\n\n` +
`Fichier ZIP cree: ${absolutePath}\n` +
`Nombre de fichiers: ${Object.keys(currentProject.files).length}\n` +
`Projet: ${currentProject.groupId}:${currentProject.artifactId}:${currentProject.version}\n\n` +
`Structure du projet:\n` +
Object.keys(currentProject.files)
.sort()
.map(file => `- ${file}`)
.join('\n') +
`\n\nLe projet est pret a etre importe dans votre IDE!\n` +
`Commandes Maven disponibles:\n` +
`- mvn clean compile\n` +
`- mvn clean package\n` +
`- mvn clean install`,
},
],
};
}
async function clearProject() {
currentProject = {
groupId: "",
artifactId: "",
version: "1.0.0",
files: {},
};
return {
content: [
{
type: "text",
text: "Projet vide avec succes! Vous pouvez maintenant creer un nouveau projet.",
},
],
};
}
function generateDefaultPom(project: MavenProject, type: string): string {
const isPlugin = type === "plugin";
return `<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
${isPlugin ? '<packaging>maven-plugin</packaging>' : ''}
<name>${project.name}</name>
<description>${project.description}</description>
<properties>
<maven.compiler.source>${project.javaVersion}</maven.compiler.source>
<maven.compiler.target>${project.javaVersion}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
${isPlugin ? '<maven.version>3.9.0</maven.version>' : ''}
</properties>
<dependencies>
${isPlugin ? `
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>\${maven.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.9.0</version>
<scope>provided</scope>
</dependency>
` : ''}
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${project.javaVersion}</source>
<target>${project.javaVersion}</target>
</configuration>
</plugin>
${isPlugin ? `
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.9.0</version>
</plugin>
` : ''}
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</build>
</project>`;
}
function generateDefaultMojo(project: MavenProject): string {
return `package ${project.groupId};
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
/**
* Goal qui execute une action personnalisee.
*/
@Mojo(name = "execute")
public class MyMojo extends AbstractMojo {
/**
* Message a afficher.
*/
@Parameter(property = "message", defaultValue = "Hello World!")
private String message;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
getLog().info("=================================");
getLog().info(" ${project.name} Plugin");
getLog().info("=================================");
getLog().info(message);
getLog().info("=================================");
}
}`;
}
function generateDefaultMainClass(project: MavenProject): string {
return `package ${project.groupId};
/**
* Classe principale de l'application.
*/
public class Main {
public static void main(String[] args) {
System.out.println("=================================");
System.out.println(" ${project.name} Application");
System.out.println("=================================");
System.out.println("Hello World!");
System.out.println("Application demarree avec succes!");
System.out.println("=================================");
}
}`;
}
async function main() {
try {
const transport = new StdioServerTransport();
await server.connect(transport);
} catch (error) {
process.exit(1);
}
}
main();