Generate MCP tool specs from annotated Java interfaces
A Java/OSGi example showing annotation-driven MCP tool registration with hierarchical tool groups and sync/async tool methods.
Why it matters
Enable developers to dynamically create and manage MCP server tools by automatically generating tool specifications from annotated Java interfaces and classes at runtime, eliminating manual tool definition work.
Outcomes
What it gets done
Annotate Java interfaces and classes to define tool groups
Generate MCP tool specifications automatically from annotations
Add or remove tools dynamically from running MCP servers
Implement service APIs that map directly to MCP tool definitions
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/mcp-mcp-dynamic-tool-groups | bash Overview
MCP Dynamic Tool Groups
MCP Dynamic Tool Groups is a Java/OSGi example project demonstrating annotation-based MCP tool declaration via the mcp_annotations_java library. It shows a hierarchical tool-group structure and both sync and async tool methods. Use it as a reference when building an MCP server on Java/OSGi and wanting annotation-driven tool metadata instead of manual registration; not a general-purpose MCP gateway or client.
What it does
MCP Dynamic Tool Groups is a Java/OSGi example project (part of ECF's MCPToolGroups) that shows how to expose a set of related methods as MCP tools using annotations rather than hand-written boilerplate. It demonstrates the McpToolGroup, McpTool, and McpToolParam annotations from the mcp_annotations_java project, applied to an ExampleToolGroup interface that declares four arithmetic operations: synchronous add and multiply, plus asynchronous asyncAdd and asyncMultiply, both returning a Reactor Mono<Double>. Each parameter and each method carries a description string, which is how the annotation layer supplies the metadata MCP tool-discovery calls need on top of the input/output plumbing.
When to use - and when NOT to
Use this project as a reference when building an MCP server on the Java/OSGi stack and wanting tool metadata declared once, in annotations on an interface, rather than duplicated across a hand-rolled registration call. It specifically demonstrates a hierarchical structure: the parent Java package is the top-level tool group, the ExampleToolGroup interface/class is a nested child group, and its two core methods are the leaf tools - useful when a project has many related tools that benefit from being organized rather than listed flat. It is not a general-purpose MCP client or gateway - it is a narrow example of the annotation and registration pattern, built around a toy arithmetic use case, and it assumes an existing OSGi component runtime.
Capabilities
- McpToolGroup annotation to mark an interface (and its containing package) as a tool group, with a group-level description.
- McpTool and McpToolParam annotations to describe individual methods and their parameters for MCP tool-discovery metadata.
- Both synchronous (add, multiply) and asynchronous (asyncAdd, asyncMultiply, returning Mono
<Double>) tool method support. - An OSGi Declarative Services component (ToolGroupComponent) that implements the annotated interface and registers itself with both a sync and an async tool-group server component via @Reference injection and an @Activate lifecycle method.
- Static, hierarchical grouping of tools: parent package as top-level group, child interface/class as a nested group, methods as leaf tools.
How to install
syncServer.addToolGroups(this, ExampleToolGroup.class);
This is the registration call the example's @Activate method uses to hand its implemented tool-group interface off to the sync (and, in a parallel call, the async) tool-group server component at OSGi component activation time.
Who it's for
Java and OSGi developers building MCP-compliant servers who want an annotation-driven way to declare tool metadata and organize many tools into a group hierarchy, instead of registering each tool by hand. Most relevant to teams already using OSGi Declarative Services who are adding MCP tool support to an existing component-based Java application.
Source README
MCP Dynamic Tool Groups
The Model Context Protocol (MCP) includes support for tools, allowing AI models to a) Get metadata (descriptions) of tool input and output; b) Provide input, call/take action and c) get output via the use of one or more of the available tools.
In this example application the com.composent.ai.mcp.examples.toolgroup.api project declares a ExampleToolGroup interface class, with McpTool and McpToolGroup metadata:
@McpToolGroup(description="Arithmetic operations exposed as mcp tools")
public interface ExampleToolGroup {
@McpTool(description = "computes the sum of the two double precision input arguments a and b")
double add(@McpToolParam(description = "x is the first argument") double x,
@McpToolParam(description = "y is the second argument") double y);
@McpTool(description = "return the product of the two given double precision arguments named a and b")
double multiply(@McpToolParam(description = "x is the first argument") double x,
@McpToolParam(description = "y is the second argument") double y);
@McpTool(description = "return asynchronously the sum of the two double precision input arguments a and b")
Mono<Double> asyncAdd(@McpToolParam(description = "x is the first argument") double x,
@McpToolParam(description = "y is the second argument") double y);
@McpTool(description = "return asynchronously the product of the two given double precision arguments named a and b")
Mono<Double> asyncMultiply(@McpToolParam(description = "x is the first argument") double x,
@McpToolParam(description = "y is the second argument") double y);
}
Each method is annotated with the @McpTool and @McpToolParam annotations from the mcp_annotations_java project. There are both synchronous mcp server methods (add, multiply) and asynchronous mcp server methods (asyncAdd and asyncMultiply).
Notice also the use of @McpToolGroup annotation for the ExampleToolGroup class along with the parent package. This shows the static creation of a hierarchical relationship between the top-level toolgroup (package), and a child toolgroup (class) with two tools (add and multiply) as leaves of this simple toolgroup tree.
Here is an OSGi component implementing the ExampleToolGroup interface.
public class ToolGroupComponent implements ExampleToolGroup {
private static Logger logger = LoggerFactory.getLogger(ToolGroupComponent.class);
// This reference will wait for the SyncToolGroupServerComponent
// to be activated
@Reference
private SyncToolGroupServerComponent syncServer;
// This reference will wait for the AsyncToolGroupServerComponent
// to be activated
@Reference
private AsyncToolgroupServerComponent asyncServer;
@Activate
void activate() {
// Add to syncServer
syncServer.addToolGroups(this, ExampleToolGroup.class);
// Add to asyncServer
asyncServer.addToolGroups(this, ExampleToolGroup.class);
}
@Override
public double add(double x, double y) {
logger.debug("Adding x={} y={}", x, y);
return x + y;
}
@Override
public double multiply(double x, double y) {
logger.debug("Multiplying x={} y={}", x, y);
return x * y;
}
@Override
public Mono<Double> asyncAdd(double x, double y) {
logger.debug("Async Adding x={} y={}", x, y);
return Mono.just(add(x, y));
}
@Override
public Mono<Double> asyncMultiply(double x, double y) {
logger.debug("Async Multiplying x={} y={}", x, y);
return Mono.just(multiply(x, y));
}
}
The ExampleToolGroup tools are processed and added to the appropriate (sync or async) server at runtime with this line:
syncServer.addToolGroups(this, ExampleToolGroup.class);
FAQ
Common questions
Discussion
Questions & comments ยท 0
Sign In Sign in to leave a comment.