fastMCP4J

Fast lightweight Java MCP server framework - Build Model Context Protocol servers with minimal boilerplate and full TypeScript SDK compatibility

FastMCP4J

Java library for building MCP servers — annotation-driven, minimal dependencies

AI Agents → Share this skill with Claude for code generation

Java Maven License Tests

Lightweight. 12 dependencies. No containers.

Just annotate and run. See below →

Note: Beta release (v0.3.1-beta) — Multi-class modules, bash tools, telemetry. API stable.


Quick Start (2 minutes)

Add dependency

Maven:

<dependency>
    <groupId>io.github.terseprompts.fastmcp</groupId>
    <artifactId>fastmcp-java</artifactId>
    <version>0.3.1-beta</version>
</dependency>

Gradle:

dependencies {
    implementation 'io.github.terseprompts.fastmcp:fastmcp-java:0.3.1-beta'
}

Create your server

@McpServer(name = "Assistant", version = "1.0")
public class MyAssistant {

    @McpTool(description = "Summarize text")
    public String summarize(@McpParam(description = "Text") String text) {
        return "Summary: " + text.substring(0, Math.min(100, text.length()));
    }

    public static void main(String[] args) {
        FastMCP.server(MyAssistant.class)
            .stdio()           // or .sse() or .streamable()
            .run();
    }
}

Run it

mvn exec:java -Dexec.mainClass="com.example.MyAssistant"

That's it. Your MCP server is running.

Working example: EchoServer.java


Who This Is For

You want to...FastMCP4J
Expose Java tools to AI agents✅ Perfect fit
Build MCP servers quickly✅ Annotation-driven, minimal code
Add MCP to existing Spring app✅ Drop-in, no framework lock-in
Lightweight MCP-only solution✅ 12 dependencies, not 50+
Fast startup & low memory✅ <500ms cold start, ~64MB

How to Use

Make a tool

@McpTool(description = "Add two numbers")
public int add(int a, int b) {
    return a + b;
}

Make it async

@McpTool(description = "Process data")
@McpAsync  // ← just add this
public Mono<String> process(@McpContext Context ctx, String input) {
    return Mono.fromCallable(() -> {
        ctx.reportProgress(50, "Processing...");
        return slowOperation(input);
    });
}

Add memory

@McpServer(name = "MyServer", version = "1.0")
@McpMemory  // ← just add this
public class MyServer {
    // AI now remembers things across sessions
}

Add all built-in tools

@McpServer(name = "MyServer", version = "1.0")
@McpMemory     // AI remembers
@McpTodo       // AI manages tasks
@McpPlanner    // AI breaks tasks into steps
@McpFileRead   // AI reads your files
@McpFileWrite  // AI writes files
public class MyServer {
    // All tools enabled, zero implementation needed
}

Organize tools across multiple classes

@McpServer(
    name = "MyServer",
    version = "1.0",
    modules = {StringTools.class, MathTools.class}  // Explicit modules
)
public class MyServer {
    // Tools from StringTools and MathTools are included
}

Or use package scanning for auto-discovery:

@McpServer(
    name = "MyServer",
    version = "1.0",
    scanBasePackage = "com.example.tools"  // Auto-discover all tools
)
public class MyServer {
    // All @McpTool classes in the package are included
}

Add bash/shell execution

@McpServer(name = "MyServer", version = "1.0")
@McpBash(timeout = 30)  // Shell command execution with security guardrails
public class MyServer {
    // Provides 'execute_command' tool with OS-aware shell selection
}

Add telemetry

@McpServer(name = "MyServer", version = "1.0")
@McpTelemetry(enabled = true, exportConsole = true)  // Metrics & tracing
public class MyServer {
    // Automatic tool invocation tracking with console export
}

Choose transport

FastMCP.server(MyServer.class)
    .stdio()       // For CLI tools, local agents
    .sse()         // For web clients, long-lived connections
    .streamable()  // For bidirectional streaming (recommended)
    .run();

Configure port, timeout, capabilities

FastMCP.server(MyServer.class)
    .port(3000)                              // HTTP port
    .requestTimeout(Duration.ofMinutes(5))   // Request timeout
    .keepAliveSeconds(30)                     // Keep-alive interval
    .capabilities(c -> c
        .tools(true)
        .resources(true, true)
        .prompts(true))
    .run();

Icons

Add visual polish to your server, tools, resources, and prompts.

@McpServer(
    name = "my-server",
    icons = {
        "data:image/svg+xml;base64,...:image/svg+xml:64x64:light",
        "data:image/svg+xml;base64,...:image/svg+xml:64x64:dark"
    }
)
@McpTool(
    description = "My tool",
    icons = {"https://example.com/icon.png"}
)
public class MyServer { }

Resources & Prompts

@McpResource(uri = "config://settings")
public String getSettings() {
    return "{\"theme\": \"dark\"}";
}

@McpPrompt(name = "code-review")
public String codeReviewPrompt(@McpParam(description = "Code to review") String code) {
    return "Review this code:\n" + code;
}

Built-in Tools

Add ONE annotation, get complete functionality.

AnnotationTools You Get
@McpMemorylist, read, create, replace, insert, delete, rename
@McpTodoadd, list, updateStatus, updateTask, delete, clearCompleted
@McpPlannercreatePlan, listPlans, getPlan, addTask, addSubtask
@McpFileReadreadLines, readFile, grep, getStats
@McpFileWritewriteFile, appendFile, writeLines, deleteFile, createDirectory

Annotations Reference

AnnotationTargetPurpose
@McpServerTYPEDefine your MCP server
@McpToolMETHODExpose as callable tool
@McpResourceMETHODExpose as resource
@McpPromptMETHODExpose as prompt template
@McpParamPARAMETERAdd description, examples, constraints, defaults
@McpAsyncMETHODMake tool async (return Mono<?>)
@McpContextPARAMETERInject request context
@McpPreHookMETHODRun before tool call (params: toolName, order)
@McpPostHookMETHODRun after tool call (params: toolName, order)
@McpBashTYPEEnable bash/shell command execution tool
@McpTelemetryTYPEEnable metrics and tracing (params: enabled, exportConsole, exportOtlp, sampleRate)
@McpMemoryTYPEEnable memory tools
@McpTodoTYPEEnable todo/task management tools
@McpPlannerTYPEEnable planning tools
@McpFileReadTYPEEnable file reading tools
@McpFileWriteTYPEEnable file writing tools

@McpParam advanced options:

@McpTool(description = "Create task")
public String createTask(
    @McpParam(
        description = "Task name",
        examples = {"backup", "sync"},
        constraints = "Cannot be empty",
        defaultValue = "default",
        required = false
    ) String taskName
) { return "Created: " + taskName; }

Hooks — Run Code Before/After Tools

Two hook types supported:

@McpPreHook — Runs before tool is called. Receives Map<String, Object> arguments.

@McpPostHook — Runs after tool completes. Receives Map<String, Object> arguments, Object result.

Use for logging, validation, authentication, audit trails, metrics.

@McpServer(name = "MyServer", version = "1.0")
public class MyServer {

    @McpTool(description = "Calculate")
    public int calculate(int x, int y) {
        return x + y;
    }

    // Run before ALL tools (*)
    @McpPreHook(toolName = "*", order = 1)
    void authenticate(Map<String, Object> args) {
        String token = (String) args.get("token");
        if (!isValid(token)) throw new SecurityException("Unauthorized");
    }

    // Run after specific tool only
    @McpPostHook(toolName = "calculate", order = 1)
    void logResult(Map<String, Object> args, Object result) {
        System.out.println("Result: " + result);
    }

    public static void main(String[] args) {
        FastMCP.server(MyServer.class).stdio().run();
    }
}

Hook options:

  • toolName — Target specific tool name, or "*" for all tools. Empty = inferred from method name
  • order — Execution priority (lower = first). Default: 0

Hook parameters:

  • Pre-hook: Map<String, Object> arguments — Tool input arguments
  • Post-hook: Map<String, Object> arguments, Object result — Input + output

Context Access — Request Metadata

@McpContext — Inject request context into your tool.

Access client info, session data, request metadata.

@McpServer(name = "MyServer", version = "1.0")
public class MyServer {

    @McpTool(description = "Get client info")
    public String getClientInfo(@McpContext Context context) {
        return "Client: " + context.getClientId();
    }

    @McpTool(description = "Get session ID")
    public String getSessionId(@McpContext Context context) {
        return "Session: " + context.getSessionId();
    }

    @McpTool(description = "Read file with context")
    public String readFile(@McpContext Context context, String path) {
        context.info("Reading file: " + path);

        // Access request headers (e.g., for auth)
        Map<String, String> headers = context.getRequestHeaders();
        String authHeader = headers.get("Authorization");

        // ... read file
        return "Content";
    }

    public static void main(String[] args) {
        FastMCP.server(MyServer.class).stdio().run();
    }
}

Context capabilities:

  • getClientId() — Client identifier
  • getSessionId() — Session identifier
  • getToolName() — Current tool name
  • getRequestHeaders() — Client request headers (e.g., auth tokens, custom headers)
  • info(String) — Log info message
  • warning(String) — Log warning
  • error(String) — Log error
  • reportProgress(int, String) — Report progress percentage
  • listResources() — List available resources
  • listPrompts() — List available prompts

New Features

@McpBash — Shell Command Execution

Execute shell commands with OS-aware shell selection and built-in security guardrails.

@McpServer(name = "MyServer", version = "1.0")
@McpBash(
    timeout = 30,                          // Command timeout in seconds
    visibleAfterBasePath = "/sandbox/*",   // Whitelist allowed directories
    notAllowedPaths = {"/etc", "/root"}    // Blacklist dangerous paths
)
public class MyServer { }

Security features:

  • Directory validation (whitelist/blacklist)
  • Dangerous command blocking (rm -rf, wget, curl, ssh, etc.)
  • Directory traversal prevention
  • Cross-platform path handling (Windows/Unix)

Supported shells:

  • Windows: cmd.exe
  • macOS: /bin/zsh
  • Linux: /bin/bash

@McpTelemetry — Metrics & Tracing

Collect metrics and traces for tool invocations.

@McpServer(name = "MyServer", version = "1.0")
@McpTelemetry(
    enabled = true,
    exportConsole = true,              // Human-readable output
    exportOtlp = false,                // OpenTelemetry export
    sampleRate = 1.0,                  // 100% sampling
    includeArguments = false,          // Don't log sensitive args
    metricExportIntervalMs = 60_000
)
public class MyServer { }

Collected metrics:

  • Tool invocation counters
  • Execution duration histograms
  • Error rates

Multi-Class Tool Organization

Split tools across multiple classes for better organization.

Manual modules (fast, explicit):

@McpServer(
    name = "MyServer",
    version = "1.0",
    modules = {StringTools.class, MathTools.class}
)

Package scanning (convenient):

@McpServer(
    name = "MyServer",
    version = "1.0",
    scanBasePackage = "com.example.tools"
)

Why FastMCP4J?

Less code

Raw MCP SDK: 35+ lines per tool FastMCP4J: ~8 lines per tool

Lightweight

FrameworkDependenciesBest For
Spring AI50+ jarsFull-stack AI apps
LangChain4j30+ jarsEnterprise AI pipelines
Quarkus AI40+ jarsCloud-native microservices
FastMCP4J12 jarsMCP servers only

Fast & focused

  • Cold start: <500ms
  • Tool invocation: <5ms
  • Memory: ~64MB
  • Purpose-built for MCP — not a general AI framework

Documentation


License

MIT © 2026


Less boilerplate. More shipping.

Get startedExamplesDocs

Made with ❤️ for the Java community

Related Servers