Exposing WASM binaries as MCP tools
WebAssembly (WASM) has emerged as a powerful technology for running high-performance code in various environments. In this article, we’ll explore how to expose WASM binaries as Model Context Protocol (MCP) tools in WildFly. With WildFly AI Feature Pack 0.5.0 we have introduced a new subsystem to expose WASM WebAssembly System Interface (WASI) binaries via CDI, and since we also support MCP tools declaration via CDI, just exposing those binaries as MCP tools was a small step forward.
What are MCP tools?
The Model Context Protocol (MCP) defines a standard way for systems (like an IDE or a CLI) to interact with MCP servers. These servers expose tools (functions that can be executed remotely with specific inputs) and resources (data sources that can be accessed). This allows the client system to leverage the server’s capabilities and data for various tasks.
Benefits of WASM-based MCP tools
-
Near-native performance for compute-intensive operations
-
Ability to reuse existing tools written in languages like C, C++, or Rust
-
Secure execution within WildFly’s sandbox environment
-
Cross-platform compatibility without recompilation
Defining WASM Binaries for MCP Tool Integration
Before a WASM module can be exposed as an MCP tool, it needs to be defined within WildFly’s WASM subsystem. This subsystem acts as a registry for your WASM modules. It is based on the chicory-sdk, an experimental Java SDK using the JVM-native Chicory runtime.
Configuration Methods
You can configure these binaries using either the WildFly CLI or by directly editing the XML configuration (standalone.xml
).
Using CLI
Add a WASM module definition via the CLI like this:
/subsystem=wasm/wasm-tool=my-wasm-tool:add(path="/path/to/your/binary.wasm")
-
my-wasm-tool
: A unique name for this binary definition. -
path
: The location of the.wasm
file. This path can be absolute (as in the example above) or relative to the additionalrelative-to
attribute.
Using XML Configuration
Alternatively, add the definition within the <subsystem xmlns="urn:wildfly:wasm:1.0">
element in your configuration file:
<subsystem xmlns="urn:jboss:domain:wasm:1.0">
<wasm-tool name="my-wasm-tool" path="path/relative/to/module/root/binary.wasm"/>
<!-- Other binary definitions -->
</subsystem>
Next Step: CDI Integration
Once defined in the WASM subsystem, these binaries can be injected using CDI.
The first way you can use them is to get them by name as an org.wildfly.wasm.api.WasmInvoker
using the @WasmTool
annotation.
This very basic interface wraps the call to the binary, passing the parameters as a byte array and getting the result as a byte array.
@Inject
@WasmTool(value = "my-wasm-tool")
org.wildfly.wasm.api.WasmInvoker greet;
public String greet(String person) {
// "greet" is the name of the exported method of the WASM module
byte[] output = greet.call("greet", person.getBytes(StandardCharsets.UTF_8));
return new String(output, StandardCharsets.UTF_8);
}
A second way is to use the @WasmToolService
over an interface to create a dynamic proxy wrapping the call to the WASM module and providing a way to serialize and deserialize your arguments to the byte array required by the invoker.
@WasmToolService(wasmToolConfigurationName = "pizza", wasmMethodName = "retrievePizzeriaAddresses", argumentSerializer = CityJsonSerializer.class)
public interface Pizza {
public String pizzas(String city);
}
Here you can see that we are calling the exported method retrievePizzeriaAddresses
on the WASM module registered with the name pizza in WildFly configuration and the `CityJsonSerializer`class is in charge with converting the java.lang.String city to a JSON object to be consumed by the WASM module.
Once defined in the WASM subsystem, these binaries can be injected using CDI and subsequently exposed as MCP tools, which we’ll cover in the next step.
Next Step: MCP Integration
Well this is the simplest part as we 'just' have to use our MCP annotations and WildFly Glow will provision the MCP server for us.
Note also that WildFly MCP server support is SSE only (no stdio).
Our WASM service now becomes:
import org.wildfly.mcp.api.Tool;
import org.wildfly.mcp.api.ToolArg;
import org.wildfly.wasm.api.WasmToolService;
@WasmToolService(wasmToolConfigurationName = "pizza", wasmMethodName = "retrievePizzeriaAddresses", argumentSerializer = CityJsonSerializer.class)
public interface Pizza {
@Tool(name="pizzaRetriever", description = "Get the address for the best hawaian pizzas")
public String pizzas(@ToolArg(description = "The city where we are looking for Hawaian pizza") String city);
}
And now there is a new pizzaRetriever MCP tool available :)
The source for this example is here.