fastMCP4J
Framework rápido e leve para servidores MCP em Java - Construa servidores do Model Context Protocol com mínimo de código repetitivo e compatibilidade total com o SDK TypeScript
Documentação
FastMCP4J
Anote uma classe Java. Implante um servidor MCP.
Ferramentas, recursos, prompts, memória — e um terminal bash em sandbox que não pode tocar na sua máquina. Sem boilerplate, sem contêineres, sem framework de 50 jars.
Agentes de IA → Compartilhe esta skill com o Claude para geração de código
FastMCP4J é um SDK orientado a anotações para o Model Context Protocol (especificação 2.0.1) em Java 17+. Uma anotação transforma uma classe em um servidor MCP; mais uma dá ao seu agente de IA um terminal real — um sandbox bash onde o host é inacessível por construção. Inicialização a frio em menos de 500 ms, doze dependências, tudo testável em processo.
Status: beta (v0.5.0-beta) — API estável, 235 testes passando, publicado no Maven Central.
Início rápido (2 minutos)
Maven
<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'
Essa é toda a história da instalação: Java 17+, um artefato no Maven Central, MCP Java SDK 2.0.1 por baixo. Sem processador de anotações, sem etapa de geração de código, sem runtime de contêiner.
Crie seu servidor
@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();
}
}
mvn exec:java -Dexec.mainClass="com.example.MyAssistant"
É isso. Seu servidor MCP está rodando.
Exemplo funcional: EchoServer.java
Por que isso existe
Toda equipe Java que conecta agentes de IA a ferramentas enfrenta a mesma escolha, e toda opção dói:
| SDK MCP puro | Spring AI / LangChain4j | FastMCP4J | |
|---|---|---|---|
| Linhas por ferramenta | 35+ | varia, mais cola de framework | ~8 |
| Dependências | 1 + sua paciência | 30–50+ jars | 12 jars |
| Inicialização | rápida | do tamanho de framework | <500 ms, ~64 MB |
| Bash em sandbox | faça você mesmo | não incluído | ✅ embutido (bashkit4j) |
| Memória / todo / planner / ferramentas de arquivo embutidas | não | não | ✅ uma anotação cada |
| Lock-in | nenhum | framework | nenhum — MCP entra, MCP sai |
Antes: um dia de encanamento de JSON-schema por ferramenta, e acesso ao shell significa que um prompt malicioso derruba um nó. Depois: anotações na terça, agentes em produção na quarta — e o terminal que eles usam não pode tocar no seu disco.
O tour da API
1 · Crie uma ferramenta — síncrona ou assíncrona
@McpTool(description = "Add two numbers")
public int add(int a, int b) {
return a + b;
}
@McpTool(description = "Process data")
@McpAsync // ← just add this; return Mono<?>
public Mono<String> process(@McpContext Context ctx, String input) {
return Mono.fromCallable(() -> {
ctx.reportProgress(50, "Processing...");
return slowOperation(input);
});
}
2 · Cérebros embutidos — uma anotação cada
@McpServer(name = "MyServer", version = "1.0")
@McpMemory // AI remembers things across sessions
@McpTodo // AI manages tasks
@McpPlanner // AI breaks work into plans
@McpFileRead // AI reads your files
@McpFileWrite // AI writes files
public class MyServer {
// complete tool sets enabled, zero implementation
}
| Anotação | Ferramentas que você obtém |
|---|---|
@McpMemory | list, read, create, replace, insert, delete, rename |
@McpTodo | add, list, updateStatus, updateTask, delete, clearCompleted |
@McpPlanner | createPlan, listPlans, getPlan, addTask, addSubtask |
@McpFileRead | readLines, readFile, grep, getStats |
@McpFileWrite | writeFile, appendFile, writeLines, deleteFile, createDirectory |
3 · Bash em sandbox — dê ao agente um terminal, não sua máquina
@McpServer(name = "Reviewer", version = "1.0")
@McpBash(
allowMountsUnder = "C:/dev", // opt in: all it may ever see
mounts = {"/project=C:/dev/my-app"}, // mount the project — read-only
timeout = 30, maxCommands = 10_000 // bounds runaway scripts
)
public class Reviewer { }
Scripts rodam em um bashkit4j
sandbox em memória: bash estilo POSIX com mais de 160 comandos reimplementados
nativamente, um sistema de arquivos virtual, rede negada por padrão. Nenhum bash real é
nunca iniciado. Então o agente chama a ferramenta bash:
grep -rn TODO /project/src | head -5 # real files, zero risk
echo "findings..." > /notes.md # writes stay in the sandbox
Shell do host (ProcessBuilder, Docker) | Modo sandbox | |
|---|---|---|
| Bash real no host | ✅ roda — superfície de ataque completa | ❌ nunca — bash reimplementado nativamente |
| Sistema de arquivos do host visível | ✅ tudo | ❌ invisível até você montar, somente leitura por padrão |
| Processos do SO por comando | ✅ um por chamada | ❌ zero — em processo |
| Acesso à rede | ✅ aberto | ❌ negado por padrão |
| Um script malicioso | nó derrubado | sandbox redefinido |
O estado persiste entre chamadas (cwd, env, arquivos) — fluxos de trabalho de agentes
em várias etapas funcionam. Montagens são aplicadas dentro da biblioteca nativa: canonicalizadas,
seguras contra symlink e impossíveis sem allowMountsUnder. Precisa do shell real
para automação confiável? mode = BashMode.HOST mantém a ferramenta legada com seus
guardrails de caminho.
O modo sandbox requer a dependência opcional
io.github.terseprompts:bashkit4j:0.2.0— bibliotecas nativas para Windows/Linux/macOS (x86-64 + ARM64) são incluídas e auto-detectadas.
🔭 Escopo futuro — um terceiro modo,
BashMode.DOCKER. A escada de isolamento ganha mais um degrau:HOST= o computador completo (uso confiável) →SANDBOX= um computador virtual, sem execução de código real (padrão) →DOCKER= bash real dentro de uma jaula de contêiner por sessão. O esboço: um contêiner por sessão MCP, criado preguiçosamente e endurecido na execução (--network none --memory 512m --cpus 1 --pids-limit 64 --security-opt no-new-privileges --read-only --tmpfs /tmp); o comando passado como um único elemento argv (sem interpolação de shell do host → sem superfície de injeção); o timeout aplicado dentro do contêiner para que o relógio de parede sobreviva a uma falha do servidor; códigos de saída nomeados ([exit 124: timed out],[exit 137: OOM]), saída limitada, evicção por TTL de inatividade com um reaper de apoio — contêineres permanecem sem estado, estado durável vive apenas no worktree montado. Config permanece somente via env (FASTMCP_DOCKER_IMAGE, timeout). Não iniciado — veja Roadmap; ajuda é bem-vinda.
4 · Escolha um transporte
FastMCP.server(MyServer.class)
.stdio() // CLI tools, local agents
.sse() // web clients, long-lived connections
.streamable() // bidirectional streaming (recommended)
.run();
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();
5 · Recursos e 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;
}
6 · Hooks — antes/depois de cada chamada de ferramenta
// 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 a specific tool only
@McpPostHook(toolName = "calculate", order = 1)
void logResult(Map<String, Object> args, Object result) {
System.out.println("Result: " + result);
}
toolName— ferramenta alvo, ou"*"para todas (vazio = inferido do nome do método)order— prioridade de execução, menor roda primeiro (padrão0)- Pré-hooks recebem os argumentos; pós-hooks recebem argumentos + resultado
7 · Contexto de requisição
@McpTool(description = "Read file with auth")
public String readFile(@McpContext Context context, String path) {
context.info("Reading file: " + path);
String auth = context.getRequestHeaders().get("Authorization");
// ...
}
Context dá a você getClientId(), getSessionId(), getToolName(),
getRequestHeaders(), info/warning/error logging, reportProgress,
listResources(), listPrompts().
8 · Organize em escala
// explicit modules
@McpServer(name = "MyServer", version = "1.0",
modules = {StringTools.class, MathTools.class})
// or package scanning
@McpServer(name = "MyServer", version = "1.0",
scanBasePackage = "com.example.tools")
9 · Ícones e telemetria
@McpServer(
name = "my-server",
icons = {"data:image/svg+xml;base64,...:image/svg+xml:64x64:light"}
)
@McpTelemetry(enabled = true, exportConsole = true, sampleRate = 1.0)
public class MyServer { }
A telemetria coleta contadores de invocação de ferramentas, histogramas de duração e taxas de erro — exportação para console ou OpenTelemetry.
Referência de anotações
| Anotação | Alvo | Propósito |
|---|---|---|
@McpServer | TYPE | Defina seu servidor MCP |
@McpTool | METHOD | Exponha como ferramenta chamável |
@McpResource | METHOD | Exponha como recurso |
@McpPrompt | METHOD | Exponha como modelo de prompt |
@McpParam | PARAMETER | Descrição, exemplos, restrições, padrões |
@McpAsync | METHOD | Torne a ferramenta assíncrona (retorne Mono<?>) |
@McpContext | PARAMETER | Injete contexto de requisição |
@McpPreHook / @McpPostHook | METHOD | Execute código antes/depois de chamadas de ferramenta |
@McpBash | TYPE | Ferramenta bash — em sandbox (padrão) ou shell do host via mode |
@McpTelemetry | TYPE | Métricas e rastreamento |
@McpMemory / @McpTodo / @McpPlanner | TYPE | Conjuntos de ferramentas embutidos |
@McpFileRead / @McpFileWrite | TYPE | Ferramentas de arquivo embutidas |
Opções avançadas do @McpParam:
@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; }
O que o sandbox realmente faz (medido, não afirmado)
FastMCP4J inclui 235 testes (mvn test) — a suíte do sandbox roda contra
a biblioteca nativa real no CI de Windows e Linux, incluindo sondas deliberadas
de fuga:
| Sonda | Resultado |
|---|---|
ls / dentro do sandbox | apenas raiz virtual — pom.xml, target, caminhos do host ausentes |
whoami / hostname | agent@sandbox — identidade virtual, não seu usuário do SO |
| Gravar em uma montagem somente leitura | falha; o arquivo do host comprovadamente nunca aparece |
| Montagem de leitura/gravação | ida e volta ao host através da API Java |
cd + arquivo entre chamadas de ferramenta | persiste dentro de um servidor; selado entre servidores |
| Script excedendo o timeout | chamador recebe TIMEOUT; sandbox substituído por um novo na próxima chamada |
| 4 chamadas de ferramenta concorrentes | todas completam — chamadas serializam no sandbox |
Sandbox sem bashkit4j no classpath | erro claro de inicialização nomeando a dependência |
Para quem é
- Engenheiros de IA/LLM — exponha serviços Java ao Claude, Cursor ou qualquer cliente MCP com esforço de nível de anotação.
- Equipes que entregam ferramentas de agente — memória, todo, planejamento, acesso a arquivos e um terminal em sandbox prontos; hooks e telemetria para produção.
- Plataformas conscientes de segurança — acesso de terminal de agente com o host inacessível por construção, não por engenharia de prompt.
- Codebases existentes com Spring/DI — servidor plug-and-play, sem lock-in de framework; seus beans viram ferramentas com uma anotação.
Requisitos e desempenho
Apenas Java 17+ e Maven 3.8+. Especificação MCP 2.0.1 via SDK Java oficial
(mcp-core + mcp-json-jackson2).
- Inicialização a frio: <500 ms
- Invocação de ferramenta: <5 ms
- Memória: ~64 MB
- Feito sob medida para MCP — não é um framework geral de IA
CI/CD
| Branch | O que roda |
|---|---|
development (staging) | Suíte de testes completa + testes de integração MCP (STDIO / SSE / Streamable / Bash em Sandbox) em cada push e PR |
main | Mesma suíte + publicação no Maven Central (staged; aprovação manual no Sonatype Central) |
Fluxo: branch de feature → development → main (release).
Documentação
- Arquitetura — como funciona
- Roadmap — o que vem a seguir
- Contribuindo — PRs são bem-vindos
- Changelog — histórico de versões
- Claude Skill — para agentes de IA
Licença
MIT © 2026