FastMCP4J — Code Generation Skill
You are working with FastMCP4J, a Java library for building MCP (Model Context Protocol) servers.
Package
Current package: io.github.terseprompts.fastmcp
Maven coordinates:
<dependency>
<groupId>io.github.terseprompts.fastmcp</groupId>
<artifactId>fastmcp-java</artifactId>
<version>0.5.0-beta</version>
</dependency>
Gradle:
implementation 'io.github.terseprompts.fastmcp:fastmcp-java:0.5.0-beta'
Core Imports
import io.github.terseprompts.fastmcp.*;
import io.github.terseprompts.fastmcp.annotation.*;
import io.github.terseprompts.fastmcp.annotation.McpParam;
import io.github.terseprompts.fastmcp.context.Context;
import reactor.core.publisher.Mono;
Essential Annotations
| Annotation | Target | Purpose |
|---|---|---|
@McpServer |
TYPE | Define MCP server |
@McpTool |
METHOD | Expose as callable tool |
@McpResource |
METHOD | Expose as resource |
@McpPrompt |
METHOD | Expose as prompt template |
@McpParam |
PARAMETER | Add description, examples, constraints |
@McpAsync |
METHOD | Make tool async (return Mono<?>) |
@McpContext |
PARAMETER | Inject request context |
@McpPreHook |
METHOD | Run before tool call |
@McpPostHook |
METHOD | Run after tool call |
@McpMemory |
TYPE | Enable memory tools |
@McpTodo |
TYPE | Enable todo/task management |
@McpPlanner |
TYPE | Enable planning tools |
@McpFileRead |
TYPE | Enable file reading tools |
@McpFileWrite |
TYPE | Enable file writing tools |
@McpBash |
TYPE | Enable bash tool — sandboxed (default, needs bashkit4j dep) or host shell (mode = BashMode.HOST) |
@McpTelemetry |
TYPE | Enable metrics and tracing |
Code Patterns
Basic Server
@McpServer(name = "MyServer", version = "1.0")
public class MyServer {
@McpTool(description = "Add two numbers")
public int add(int a, int b) {
return a + b;
}
public static void main(String[] args) {
FastMCP.server(MyServer.class)
.stdio()
.run();
}
}
Async Tool
@McpTool(description = "Process data asynchronously")
@McpAsync
public Mono<String> process(String input) {
return Mono.fromCallable(() -> slowOperation(input));
}
Tool with Context
@McpTool(description = "Get client info")
public String getClientInfo(@McpContext Context ctx) {
return "Client: " + ctx.getClientId();
}
Tool with Parameter Metadata
@McpTool(description = "Create task")
public String createTask(
@McpParam(description = "Task name", required = true) String taskName,
@McpParam(description = "Priority", defaultValue = "medium") String priority
) {
return "Created: " + taskName;
}
Multi-Class Modules
@McpServer(
name = "MyServer",
version = "1.0",
modules = {StringTools.class, MathTools.class}
)
public class MyServer { }
Package Scanning
@McpServer(
name = "MyServer",
version = "1.0",
scanBasePackage = "com.example.tools"
)
public class MyServer { }
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 files
@McpFileWrite // AI writes files
@McpBash // AI runs scripts in a bashkit4j sandbox (host unreachable)
@McpTelemetry // Metrics and tracing
public class MyServer { }
Sandboxed Bash (@McpBash)
Default mode is BashMode.SANDBOX: scripts run in a bashkit4j in-memory
virtual computer — own filesystem, processes, and env; the host is
unreachable. State persists across calls. Requires the optional dependency:
<dependency>
<groupId>io.github.terseprompts</groupId>
<artifactId>bashkit4j</artifactId>
<version>0.2.0</version>
</dependency>
@McpServer(name = "Reviewer", version = "1.0")
@McpBash(
allowMountsUnder = "C:/dev", // host prefixes mounts may resolve under (required for mounts)
mounts = {"/project=C:/dev/my-app"}, // "/vfs=host/dir" read-only; append ":rw" for writable
timeout = 30, // per-call timeout in seconds
maxCommands = 10_000, // bounds runaway scripts
username = "agent", hostname = "sandbox",
cwd = "/", env = {"CI=true"}
)
public class Reviewer { }
The exposed bash tool takes command (a full multi-line bash script) and
optional timeout. Real host shell instead: mode = BashMode.HOST (trusted
environments only; then visibleAfterBasePath / notAllowedPaths apply).
Transports
FastMCP.server(MyServer.class)
.stdio() // CLI tools, local agents
.sse() // Web clients, long-lived connections
.streamable() // Bidirectional streaming
.run();
Configuration
FastMCP.server(MyServer.class)
.port(3000)
.requestTimeout(Duration.ofMinutes(5))
.keepAliveSeconds(30)
.capabilities(c -> c
.tools(true)
.resources(true, true)
.prompts(true))
.run();
Hooks
@McpPreHook(toolName = "*", order = 1)
void authenticate(Map<String, Object> args) {
// Validate before all tools
}
@McpPostHook(toolName = "calculate", order = 1)
void logResult(Map<String, Object> args, Object result) {
// Log after specific tool
}
Main Class
io.github.terseprompts.fastmcp.core.FastMCP
Resource/Prompt Patterns
@McpResource(uri = "config://settings")
public String getSettings() {
return "{\"theme\": \"dark\"}";
}
@McpPrompt(name = "code-review")
public String codeReviewPrompt(@McpParam(description = "Code") String code) {
return "Review this code:\n" + code;
}
When Generating Code
- Always use package
io.github.terseprompts.fastmcp - Prefer
@McpToolfor any method that should be exposed to AI - Add
descriptionto tools and parameters - Use
@McpAsync+Mono<?>for I/O operations - Use
@McpContextto inject Context when needed (logging, progress, client info) - Default to
.stdio()for CLI tools,.streamable()for web - Add built-in tool annotations (
@McpMemory, etc.) when user needs those features