Dependency Injection (Java/Spring Boot)¶
Automatic wiring of capabilities between agents
MCP Mesh implements Distributed Dynamic Dependency Injection (DDDI) — dependencies are discovered and injected at runtime across the mesh, not at compile time.
Overview¶
MCP Mesh provides automatic dependency injection (DI) that connects agents based on their declared capabilities and dependencies. When a tool declares a dependency via @Selector, the mesh automatically injects a McpMeshTool<T> proxy that routes to the providing agent.
How It Works¶
- Declaration: Tool declares dependencies via
@MeshTool(dependencies = @Selector(...)) - Registration: Agent registers with registry, advertising capabilities
- Resolution: Registry matches dependencies to providers
- Injection: Mesh injects
McpMeshTool<T>instances as method parameters - Invocation: Calling the proxy routes to the remote agent
Declaring Dependencies¶
Simple Dependency¶
@MeshTool(capability = "smart_greeting",
description = "Greet with current date",
dependencies = @Selector(capability = "date_service"))
public GreetingResponse smartGreet(
@Param(value = "name", description = "The name to greet") String name,
McpMeshTool<String> dateService) {
if (dateService != null && dateService.isAvailable()) {
String today = dateService.call();
return new GreetingResponse("Hello " + name + "! Today is " + today);
}
return new GreetingResponse("Hello " + name + "!");
}
Important: Dependencies are injected as McpMeshTool<T> parameters on the method. They may be null if unavailable.
Dependencies with Filters¶
Use the @Selector annotation with tags or version to filter providers:
@MeshTool(capability = "report",
description = "Generate report with formatted data",
dependencies = @Selector(capability = "data_service",
tags = {"+fast", "-deprecated"}))
public String generateReport(
@Param(value = "query", description = "Report query") String query,
McpMeshTool<String> dataService) {
if (dataService == null || !dataService.isAvailable()) {
return "Data service unavailable";
}
return dataService.call("query", query);
}
McpMeshTool<T> API Reference¶
The McpMeshTool<T> interface is the primary way to interact with remote capabilities. The type parameter T indicates the expected return type.
call() - No Arguments¶
Invoke the remote tool with no parameters:
call(Record) - Structured Parameters¶
Pass a Java record whose field names become parameter names:
McpMeshTool<Integer> calculator;
record AddParams(int a, int b) {}
Integer sum = calculator.call(new AddParams(3, 5)); // sum = 8
call(key, value, ...) - Varargs¶
Pass parameters as key-value pairs:
isAvailable()¶
Check if the remote capability is currently reachable:
getEndpoint()¶
Get the remote endpoint URL:
getCapability()¶
Get the capability name this proxy represents:
API Summary¶
| Method | Description | Return Type |
|---|---|---|
call() | No-arg invocation | T |
call(record) | Call with record fields as params | T |
call(k, v, ...) | Call with key-value pairs | T |
isAvailable() | Check provider reachability | boolean |
getEndpoint() | Remote agent endpoint URL | String |
getCapability() | Capability name of this dependency | String |
Type-Safe Responses¶
The generic type parameter T on McpMeshTool<T> controls response deserialization. The SDK automatically converts the remote JSON response to the specified type.
// Primitive types
McpMeshTool<Integer> calculator;
Integer sum = calculator.call(new AddParams(3, 5));
// String responses
McpMeshTool<String> dateService;
String today = dateService.call();
// Complex record types
McpMeshTool<Employee> employeeService;
Employee emp = employeeService.call("id", 42);
// Employee record is auto-deserialized from JSON
record Employee(int id, String name, String department) {}
Graceful Degradation¶
Dependencies may be unavailable if the providing agent is down or not yet started. Always handle null and check availability:
@MeshTool(capability = "agent_status",
description = "Get status with dependency info",
dependencies = @Selector(capability = "date_service"))
public AgentStatus getStatus(McpMeshTool<String> dateService) {
boolean depAvailable = dateService != null && dateService.isAvailable();
if (depAvailable) {
String date = dateService.call();
return new AgentStatus("operational", date);
}
return new AgentStatus("degraded", "date service unavailable");
}
record AgentStatus(String status, String info) {}
Or provide fallback values:
@MeshTool(capability = "time_service",
description = "Get current time",
dependencies = @Selector(capability = "date_service"))
public TimeResponse getTime(McpMeshTool<String> dateService) {
if (dateService != null && dateService.isAvailable()) {
return new TimeResponse(dateService.call());
}
// Fallback to local time
return new TimeResponse(java.time.LocalDateTime.now().toString());
}
Auto-Rewiring¶
When topology changes (agents join/leave), the mesh:
- Detects change via heartbeat response
- Refreshes dependency proxies
- Routes to new providers automatically
No code changes needed - happens transparently.
Multiple Dependencies¶
A single tool can depend on multiple capabilities. Each dependency gets its own McpMeshTool<T> parameter:
@MeshTool(capability = "add_via_mesh",
description = "Add two numbers using remote calculator",
tags = {"math", "cross-agent", "java"},
dependencies = @Selector(capability = "add"))
public CalculationResult addViaMesh(
@Param(value = "a", description = "First number") int a,
@Param(value = "b", description = "Second number") int b,
McpMeshTool<Integer> calculator) {
Integer sum = calculator.call(new AddParams(a, b));
return new CalculationResult("add", a, b, sum);
}
record AddParams(int a, int b) {}
record CalculationResult(String op, int a, int b, int result) {}
LLM Injection¶
For @MeshLlm annotated tools, the LLM is injected as a MeshLlmAgent parameter:
@MeshLlm(providerSelector = @Selector(capability = "llm"),
maxIterations = 5, systemPrompt = "You are a helpful analyst.")
@MeshTool(capability = "analyze",
description = "AI-powered analysis",
tags = {"analysis", "llm", "java"})
public AnalysisResult analyze(
@Param(value = "query", description = "Analysis query") String query,
MeshLlmAgent llm) {
return llm.request()
.user(query)
.generate(AnalysisResult.class);
}
Complete Example¶
package com.example.assistant;
import io.mcpmesh.*;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MeshAgent(name = "assistant", version = "1.0.0",
description = "Assistant with mesh dependencies", port = 9001)
@SpringBootApplication
public class AssistantAgentApplication {
public static void main(String[] args) {
SpringApplication.run(AssistantAgentApplication.class, args);
}
@MeshTool(capability = "smart_greeting",
description = "Greet with current date from mesh",
tags = {"greeting", "assistant", "java"},
dependencies = @Selector(capability = "date_service"))
public GreetingResponse smartGreet(
@Param(value = "name", description = "The name to greet") String name,
McpMeshTool<String> dateService) {
if (dateService != null && dateService.isAvailable()) {
String dateString = dateService.call();
return new GreetingResponse(
"Hello, " + name + "! Today is " + dateString);
}
return new GreetingResponse(
"Hello, " + name + "! (date service unavailable)");
}
@MeshTool(capability = "agent_status",
description = "Get agent status with dependency info",
tags = {"status", "info", "java"},
dependencies = @Selector(capability = "date_service"))
public AgentStatus getStatus(McpMeshTool<String> dateService) {
boolean available = dateService != null && dateService.isAvailable();
String endpoint = available ? dateService.getEndpoint() : "N/A";
String capability = available ? dateService.getCapability() : "N/A";
return new AgentStatus("assistant", available, endpoint, capability);
}
record GreetingResponse(String message) {}
record AgentStatus(String agent, boolean depAvailable,
String depEndpoint, String depCapability) {}
}
See Also¶
meshctl man capabilities --java- Declaring capabilitiesmeshctl man tags --java- Tag-based selectionmeshctl man decorators --java- All Java annotations