Skip to content

Commit 19656ee

Browse files
committed
feat: add java init framework
Signed-off-by: ankittk <ankit.kumar@getweave.com>
1 parent 41071d1 commit 19656ee

25 files changed

Lines changed: 1759 additions & 17 deletions

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 20 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cli/internal/build/build.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ func (b *Builder) Build(opts Options) error {
5252
return b.buildDockerImage(opts, "node")
5353
case "go":
5454
return b.buildDockerImage(opts, "go")
55+
case "java":
56+
return b.buildDockerImage(opts, "java")
5557
default:
5658
return fmt.Errorf("unsupported project type: %s", projectType)
5759
}
@@ -77,6 +79,11 @@ func (b *Builder) detectProjectType(dir string) (string, error) {
7779
return "go", nil
7880
}
7981

82+
// Check for Java project
83+
if b.fileExists(filepath.Join(dir, "pom.xml")) {
84+
return "java", nil
85+
}
86+
8087
return "", fmt.Errorf("unknown project type")
8188
}
8289

pkg/cli/internal/commands/deploy.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import (
88
"strings"
99
"time"
1010

11-
"github.com/kagent-dev/kmcp/api/v1alpha1"
12-
"github.com/kagent-dev/kmcp/pkg/cli/internal/manifest"
1311
"github.com/spf13/cobra"
1412
corev1 "k8s.io/api/core/v1"
1513
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1614
"sigs.k8s.io/yaml"
15+
16+
"github.com/kagent-dev/kmcp/api/v1alpha1"
17+
"github.com/kagent-dev/kmcp/pkg/cli/internal/manifest"
1718
)
1819

1920
const (
@@ -511,6 +512,8 @@ func getDefaultCommand(framework string) string {
511512
return "./server"
512513
case manifest.FrameworkTypeScript:
513514
return "node"
515+
case manifest.FrameworkJava:
516+
return "java"
514517
default:
515518
return "python"
516519
}
@@ -527,6 +530,11 @@ func getDefaultArgs(framework string, targetPort int) []string {
527530
return []string{}
528531
case manifest.FrameworkTypeScript:
529532
return []string{"dist/index.js"}
533+
case manifest.FrameworkJava:
534+
if deployTransport == transportHTTP {
535+
return []string{"-jar", "app.jar", "--transport", "http", "--host", "0.0.0.0", "--port", fmt.Sprintf("%d", targetPort)}
536+
}
537+
return []string{"-jar", "app.jar"}
530538
default:
531539
return []string{"src/main.py"}
532540
}

pkg/cli/internal/commands/init.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,18 @@ import (
88
"regexp"
99
"strings"
1010

11+
"github.com/spf13/cobra"
12+
1113
"github.com/kagent-dev/kmcp/pkg/cli/internal/frameworks"
1214
"github.com/kagent-dev/kmcp/pkg/cli/internal/manifest"
1315
"github.com/kagent-dev/kmcp/pkg/cli/internal/templates"
14-
15-
"github.com/spf13/cobra"
1616
)
1717

1818
const (
1919
frameworkFastMCPPython = "fastmcp-python"
2020
frameworkMCPGo = "mcp-go"
2121
frameworkTypeScript = "typescript"
22+
frameworkJava = "java"
2223
)
2324

2425
var initCmd = &cobra.Command{
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package commands
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
)
8+
9+
var initJavaCmd = &cobra.Command{
10+
Use: "java [project-name]",
11+
Short: "Initialize a new Java MCP server project",
12+
Long: `Initialize a new MCP server project using the Java framework.
13+
14+
This command will create a new directory with a basic Java MCP project structure,
15+
including a pom.xml file, Maven project structure, and an example tool.`,
16+
Args: cobra.ExactArgs(1),
17+
RunE: runInitJava,
18+
}
19+
20+
func init() {
21+
initCmd.AddCommand(initJavaCmd)
22+
}
23+
24+
func runInitJava(_ *cobra.Command, args []string) error {
25+
projectName := args[0]
26+
framework := frameworkJava
27+
28+
if err := runInitFramework(projectName, framework, nil); err != nil {
29+
return err
30+
}
31+
32+
fmt.Printf("✓ Successfully created Java MCP server project: %s\n", projectName)
33+
fmt.Printf("\nNext steps:\n")
34+
fmt.Printf("1. cd %s\n", projectName)
35+
fmt.Printf("2. mvn clean install\n")
36+
fmt.Printf("3. mvn exec:java -Dexec.mainClass=\"com.example.Main\"\n")
37+
fmt.Printf("4. Add tools with: kmcp add-tool my-tool\n")
38+
return nil
39+
}

pkg/cli/internal/commands/run.go

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import (
66
"os/exec"
77
"path/filepath"
88

9-
"github.com/kagent-dev/kmcp/pkg/cli/internal/manifest"
109
"github.com/spf13/cobra"
10+
11+
"github.com/kagent-dev/kmcp/pkg/cli/internal/manifest"
1112
)
1213

1314
var runCmd = &cobra.Command{
@@ -29,13 +30,15 @@ Supported frameworks:
2930
3031
Examples:
3132
kmcp run --project-dir ./my-project # Run with inspector (default)
32-
kmcp run --no-inspector # Run server directly without inspector`,
33+
kmcp run --no-inspector # Run server directly without inspector
34+
kmcp run --transport http # Run with HTTP transport`,
3335
RunE: executeRun,
3436
}
3537

3638
var (
37-
projectDir string
38-
noInspector bool
39+
projectDir string
40+
noInspector bool
41+
runTransport string
3942
)
4043

4144
func init() {
@@ -54,6 +57,12 @@ func init() {
5457
false,
5558
"Run the server directly without launching the MCP inspector",
5659
)
60+
runCmd.Flags().StringVar(
61+
&runTransport,
62+
"transport",
63+
"stdio",
64+
"Transport mode (stdio or http)",
65+
)
5766
}
5867

5968
func executeRun(_ *cobra.Command, _ []string) error {
@@ -82,6 +91,8 @@ func executeRun(_ *cobra.Command, _ []string) error {
8291
return runMCPGo(projectDir, manifest)
8392
case "typescript":
8493
return runTypeScript(projectDir, manifest)
94+
case "java":
95+
return runJava(projectDir, manifest)
8596
default:
8697
return fmt.Errorf("unsupported framework: %s", manifest.Framework)
8798
}
@@ -279,3 +290,73 @@ func runTypeScript(projectDir string, manifest *manifest.ProjectManifest) error
279290
// Run the inspector
280291
return runMCPInspector(configPath, manifest.Name, projectDir)
281292
}
293+
294+
func runJava(projectDir string, manifest *manifest.ProjectManifest) error {
295+
// Check if mvn is available
296+
if _, err := exec.LookPath("mvn"); err != nil {
297+
mvnInstallURL := "https://maven.apache.org/install.html"
298+
return fmt.Errorf("mvn is required to run Java projects locally. Please install Maven: %s", mvnInstallURL)
299+
}
300+
301+
// Run mvn clean install first to ensure dependencies are up to date
302+
if Verbose {
303+
fmt.Printf("Running mvn clean install in: %s\n", projectDir)
304+
}
305+
installCmd := exec.Command("mvn", "clean", "install", "-DskipTests")
306+
installCmd.Dir = projectDir
307+
installCmd.Stdout = os.Stdout
308+
installCmd.Stderr = os.Stderr
309+
if err := installCmd.Run(); err != nil {
310+
return fmt.Errorf("failed to run mvn clean install: %w", err)
311+
}
312+
313+
// Prepare Maven arguments based on transport mode
314+
mavenArgs := []string{"-q", "exec:java", "-Dexec.mainClass=com.example.Main"}
315+
if runTransport == transportHTTP {
316+
mavenArgs = append(mavenArgs, "-Dexec.args=--transport http --host 0.0.0.0 --port 3000")
317+
}
318+
319+
if noInspector {
320+
// Run the server directly
321+
if runTransport == transportHTTP {
322+
fmt.Printf("Running server directly: mvn exec:java -Dexec.mainClass=\"com.example.Main\" --transport http --host 0.0.0.0 --port 3000\n")
323+
fmt.Printf("Server is running on http://localhost:3000\n")
324+
fmt.Printf("Health check: http://localhost:3000/health\n")
325+
fmt.Printf("MCP endpoint: http://localhost:3000/mcp\n")
326+
} else {
327+
fmt.Printf("Running server directly: mvn exec:java -Dexec.mainClass=\"com.example.Main\"\n")
328+
fmt.Printf("Server is running and waiting for MCP protocol input on stdin...\n")
329+
}
330+
fmt.Printf("Press Ctrl+C to stop the server\n")
331+
332+
serverCmd := exec.Command("mvn", mavenArgs...)
333+
serverCmd.Dir = projectDir
334+
serverCmd.Stdout = os.Stdout
335+
serverCmd.Stderr = os.Stderr
336+
serverCmd.Stdin = os.Stdin
337+
return serverCmd.Run()
338+
}
339+
340+
// Create server configuration for inspector
341+
var serverConfig map[string]interface{}
342+
if runTransport == transportHTTP {
343+
serverConfig = map[string]interface{}{
344+
"type": "streamable-http",
345+
"url": "http://localhost:3000/mcp",
346+
}
347+
} else {
348+
serverConfig = map[string]interface{}{
349+
"command": "mvn",
350+
"args": mavenArgs,
351+
}
352+
}
353+
354+
// Create MCP inspector config
355+
configPath := filepath.Join(projectDir, "mcp-server-config.json")
356+
if err := createMCPInspectorConfig(manifest.Name, serverConfig, configPath); err != nil {
357+
return err
358+
}
359+
360+
// Run the inspector
361+
return runMCPInspector(configPath, manifest.Name, projectDir)
362+
}

pkg/cli/internal/frameworks/common/base_generator.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import (
1010
"text/template"
1111

1212
"github.com/stoewer/go-strcase"
13-
14-
"github.com/kagent-dev/kmcp/pkg/cli/internal/templates"
1513
"golang.org/x/text/cases"
1614
"golang.org/x/text/language"
15+
16+
"github.com/kagent-dev/kmcp/pkg/cli/internal/templates"
1717
)
1818

1919
// Base Generator for MCP projects
@@ -128,13 +128,15 @@ func (g *BaseGenerator) GenerateTool(projectRoot string, config templates.ToolCo
128128
func (g *BaseGenerator) GenerateToolFile(filePath string, config templates.ToolConfig) error {
129129
// Prepare template data
130130
toolName := config.ToolName
131+
toolNamePascalCase := cases.Title(language.English).String(toolName)
131132
data := map[string]interface{}{
132-
"ToolName": toolName,
133-
"ToolNameTitle": cases.Title(language.English).String(toolName),
134-
"ToolNameUpper": strings.ToUpper(toolName),
135-
"ToolNameLower": strings.ToLower(toolName),
136-
"ClassName": cases.Title(language.English).String(toolName) + "Tool",
137-
"Description": config.Description,
133+
"ToolName": toolName,
134+
"ToolNameTitle": cases.Title(language.English).String(toolName),
135+
"ToolNameUpper": strings.ToUpper(toolName),
136+
"ToolNameLower": strings.ToLower(toolName),
137+
"ToolNamePascalCase": toolNamePascalCase,
138+
"ClassName": cases.Title(language.English).String(toolName) + "Tool",
139+
"Description": config.Description,
138140
}
139141

140142
// Create the directory if it doesn't exist

pkg/cli/internal/frameworks/frameworks.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55

66
"github.com/kagent-dev/kmcp/pkg/cli/internal/frameworks/golang"
7+
"github.com/kagent-dev/kmcp/pkg/cli/internal/frameworks/java"
78
"github.com/kagent-dev/kmcp/pkg/cli/internal/frameworks/python"
89
"github.com/kagent-dev/kmcp/pkg/cli/internal/frameworks/typescript"
910
"github.com/kagent-dev/kmcp/pkg/cli/internal/templates"
@@ -25,6 +26,8 @@ func GetGenerator(framework string) (Generator, error) {
2526
return golang.NewGenerator(), nil
2627
case "typescript":
2728
return typescript.NewGenerator(), nil
29+
case "java":
30+
return java.NewGenerator(), nil
2831
default:
2932
return nil, fmt.Errorf("unsupported framework: %s", framework)
3033
}

0 commit comments

Comments
 (0)