Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1,880 changes: 1,880 additions & 0 deletions helm/kmcp-crds/templates/mcpserver-crd.yaml

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions pkg/cli/internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ func (b *Builder) Build(opts Options) error {
return b.buildDockerImage(opts, "node")
case "go":
return b.buildDockerImage(opts, "go")
case "java":
return b.buildDockerImage(opts, "java")
default:
return fmt.Errorf("unsupported project type: %s", projectType)
}
Expand All @@ -77,6 +79,11 @@ func (b *Builder) detectProjectType(dir string) (string, error) {
return "go", nil
}

// Check for Java project
if b.fileExists(filepath.Join(dir, "pom.xml")) {
return "java", nil
}

return "", fmt.Errorf("unknown project type")
}

Expand Down
12 changes: 10 additions & 2 deletions pkg/cli/internal/commands/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import (
"strings"
"time"

"github.com/kagent-dev/kmcp/api/v1alpha1"
"github.com/kagent-dev/kmcp/pkg/cli/internal/manifest"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"

"github.com/kagent-dev/kmcp/api/v1alpha1"
"github.com/kagent-dev/kmcp/pkg/cli/internal/manifest"
)

const (
Expand Down Expand Up @@ -511,6 +512,8 @@ func getDefaultCommand(framework string) string {
return "./server"
case manifest.FrameworkTypeScript:
return "node"
case manifest.FrameworkJava:
return "java"
default:
return "python"
}
Expand All @@ -527,6 +530,11 @@ func getDefaultArgs(framework string, targetPort int) []string {
return []string{}
case manifest.FrameworkTypeScript:
return []string{"dist/index.js"}
case manifest.FrameworkJava:
if deployTransport == transportHTTP {
return []string{"-jar", "app.jar", "--transport", "http", "--host", "0.0.0.0", "--port", fmt.Sprintf("%d", targetPort)}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

were you able to get --transport http to work? I kept running into an issue requiring a sessionId to be present.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you test now?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested with your latest changes but the /mcp endpoint was missing. I pushed a commit to fix that and its now working with the inspector so the command dist/kmcp deploy --file my-java/kmcp.yaml --transport http now works out of the box. Could you take a look at the changes and let me know if they look good to you?

}
return []string{"-jar", "app.jar"}
default:
return []string{"src/main.py"}
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/cli/internal/commands/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@ import (
"regexp"
"strings"

"github.com/spf13/cobra"

"github.com/kagent-dev/kmcp/pkg/cli/internal/frameworks"
"github.com/kagent-dev/kmcp/pkg/cli/internal/manifest"
"github.com/kagent-dev/kmcp/pkg/cli/internal/templates"

"github.com/spf13/cobra"
)

const (
frameworkFastMCPPython = "fastmcp-python"
frameworkMCPGo = "mcp-go"
frameworkTypeScript = "typescript"
frameworkJava = "java"
)

var initCmd = &cobra.Command{
Expand Down
39 changes: 39 additions & 0 deletions pkg/cli/internal/commands/init_java.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package commands

import (
"fmt"

"github.com/spf13/cobra"
)

var initJavaCmd = &cobra.Command{
Use: "java [project-name]",
Short: "Initialize a new Java MCP server project",
Long: `Initialize a new MCP server project using the Java framework.

This command will create a new directory with a basic Java MCP project structure,
including a pom.xml file, Maven project structure, and an example tool.`,
Args: cobra.ExactArgs(1),
RunE: runInitJava,
}

func init() {
initCmd.AddCommand(initJavaCmd)
}

func runInitJava(_ *cobra.Command, args []string) error {
projectName := args[0]
framework := frameworkJava

if err := runInitFramework(projectName, framework, nil); err != nil {
return err
}

fmt.Printf("✓ Successfully created Java MCP server project: %s\n", projectName)
fmt.Printf("\nNext steps:\n")
fmt.Printf("1. cd %s\n", projectName)
fmt.Printf("2. mvn clean install\n")
fmt.Printf("3. mvn exec:java -Dexec.mainClass=\"com.example.Main\"\n")
fmt.Printf("4. Add tools with: kmcp add-tool my-tool\n")
return nil
}
89 changes: 85 additions & 4 deletions pkg/cli/internal/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import (
"os/exec"
"path/filepath"

"github.com/kagent-dev/kmcp/pkg/cli/internal/manifest"
"github.com/spf13/cobra"

"github.com/kagent-dev/kmcp/pkg/cli/internal/manifest"
)

var runCmd = &cobra.Command{
Expand All @@ -29,13 +30,15 @@ Supported frameworks:

Examples:
kmcp run --project-dir ./my-project # Run with inspector (default)
kmcp run --no-inspector # Run server directly without inspector`,
kmcp run --no-inspector # Run server directly without inspector
kmcp run --transport http # Run with HTTP transport`,
RunE: executeRun,
}

var (
projectDir string
noInspector bool
projectDir string
noInspector bool
runTransport string
)

func init() {
Expand All @@ -54,6 +57,12 @@ func init() {
false,
"Run the server directly without launching the MCP inspector",
)
runCmd.Flags().StringVar(
&runTransport,
"transport",
"stdio",
"Transport mode (stdio or http)",
)
}

func executeRun(_ *cobra.Command, _ []string) error {
Expand Down Expand Up @@ -82,6 +91,8 @@ func executeRun(_ *cobra.Command, _ []string) error {
return runMCPGo(projectDir, manifest)
case "typescript":
return runTypeScript(projectDir, manifest)
case "java":
return runJava(projectDir, manifest)
default:
return fmt.Errorf("unsupported framework: %s", manifest.Framework)
}
Expand Down Expand Up @@ -279,3 +290,73 @@ func runTypeScript(projectDir string, manifest *manifest.ProjectManifest) error
// Run the inspector
return runMCPInspector(configPath, manifest.Name, projectDir)
}

func runJava(projectDir string, manifest *manifest.ProjectManifest) error {
// Check if mvn is available
if _, err := exec.LookPath("mvn"); err != nil {
mvnInstallURL := "https://maven.apache.org/install.html"
return fmt.Errorf("mvn is required to run Java projects locally. Please install Maven: %s", mvnInstallURL)
}

// Run mvn clean install first to ensure dependencies are up to date
if Verbose {
fmt.Printf("Running mvn clean install in: %s\n", projectDir)
}
installCmd := exec.Command("mvn", "clean", "install", "-DskipTests")
installCmd.Dir = projectDir
installCmd.Stdout = os.Stdout
installCmd.Stderr = os.Stderr
if err := installCmd.Run(); err != nil {
return fmt.Errorf("failed to run mvn clean install: %w", err)
}

// Prepare Maven arguments based on transport mode
mavenArgs := []string{"-q", "exec:java", "-Dexec.mainClass=com.example.Main"}
if runTransport == transportHTTP {
mavenArgs = append(mavenArgs, "-Dexec.args=--transport http --host 0.0.0.0 --port 3000")
}

if noInspector {
// Run the server directly
if runTransport == transportHTTP {
fmt.Printf("Running server directly: mvn exec:java -Dexec.mainClass=\"com.example.Main\" --transport http --host 0.0.0.0 --port 3000\n")
fmt.Printf("Server is running on http://localhost:3000\n")
fmt.Printf("Health check: http://localhost:3000/health\n")
fmt.Printf("MCP endpoint: http://localhost:3000/mcp\n")
} else {
fmt.Printf("Running server directly: mvn exec:java -Dexec.mainClass=\"com.example.Main\"\n")
fmt.Printf("Server is running and waiting for MCP protocol input on stdin...\n")
}
fmt.Printf("Press Ctrl+C to stop the server\n")

serverCmd := exec.Command("mvn", mavenArgs...)
serverCmd.Dir = projectDir
serverCmd.Stdout = os.Stdout
serverCmd.Stderr = os.Stderr
serverCmd.Stdin = os.Stdin
return serverCmd.Run()
}

// Create server configuration for inspector
var serverConfig map[string]interface{}
if runTransport == transportHTTP {
serverConfig = map[string]interface{}{
"type": "streamable-http",
"url": "http://localhost:3000/mcp",
}
} else {
serverConfig = map[string]interface{}{
"command": "mvn",
"args": mavenArgs,
}
}

// Create MCP inspector config
configPath := filepath.Join(projectDir, "mcp-server-config.json")
if err := createMCPInspectorConfig(manifest.Name, serverConfig, configPath); err != nil {
return err
}

// Run the inspector
return runMCPInspector(configPath, manifest.Name, projectDir)
}
18 changes: 10 additions & 8 deletions pkg/cli/internal/frameworks/common/base_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
"text/template"

"github.com/stoewer/go-strcase"

"github.com/kagent-dev/kmcp/pkg/cli/internal/templates"
"golang.org/x/text/cases"
"golang.org/x/text/language"

"github.com/kagent-dev/kmcp/pkg/cli/internal/templates"
)

// Base Generator for MCP projects
Expand Down Expand Up @@ -128,13 +128,15 @@ func (g *BaseGenerator) GenerateTool(projectRoot string, config templates.ToolCo
func (g *BaseGenerator) GenerateToolFile(filePath string, config templates.ToolConfig) error {
// Prepare template data
toolName := config.ToolName
toolNamePascalCase := cases.Title(language.English).String(toolName)
data := map[string]interface{}{
"ToolName": toolName,
"ToolNameTitle": cases.Title(language.English).String(toolName),
"ToolNameUpper": strings.ToUpper(toolName),
"ToolNameLower": strings.ToLower(toolName),
"ClassName": cases.Title(language.English).String(toolName) + "Tool",
"Description": config.Description,
"ToolName": toolName,
"ToolNameTitle": cases.Title(language.English).String(toolName),
"ToolNameUpper": strings.ToUpper(toolName),
"ToolNameLower": strings.ToLower(toolName),
"ToolNamePascalCase": toolNamePascalCase,
"ClassName": cases.Title(language.English).String(toolName) + "Tool",
"Description": config.Description,
}

// Create the directory if it doesn't exist
Expand Down
3 changes: 3 additions & 0 deletions pkg/cli/internal/frameworks/frameworks.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

"github.com/kagent-dev/kmcp/pkg/cli/internal/frameworks/golang"
"github.com/kagent-dev/kmcp/pkg/cli/internal/frameworks/java"
"github.com/kagent-dev/kmcp/pkg/cli/internal/frameworks/python"
"github.com/kagent-dev/kmcp/pkg/cli/internal/frameworks/typescript"
"github.com/kagent-dev/kmcp/pkg/cli/internal/templates"
Expand All @@ -25,6 +26,8 @@ func GetGenerator(framework string) (Generator, error) {
return golang.NewGenerator(), nil
case "typescript":
return typescript.NewGenerator(), nil
case "java":
return java.NewGenerator(), nil
default:
return nil, fmt.Errorf("unsupported framework: %s", framework)
}
Expand Down
Loading
Loading