WebAssembly modules — how-to¶
Recipes for loading WASM modules into mcp-v8, controlling memory caps, and using the bundled SQLite example.
Load a module via --wasm-module¶
mcp-v8 --wasm-module mylib=/path/to/mylib.wasm
The flag is repeatable; add it once per module:
mcp-v8 \
--wasm-module math=/opt/wasm/math.wasm \
--wasm-module codec=/opt/wasm/codec.wasm
Load modules via --wasm-config¶
For more than a handful of modules, use a JSON config file instead of multiple flags:
mcp-v8 --wasm-config /etc/mcp-v8/wasm.json
Each key in the JSON object is a module name; each value is either a plain path string or an object that also sets a per-module memory cap:
{
"math": "/opt/wasm/math.wasm",
"sqlite": {
"path": "/opt/wasm/sqlite3.wasm",
"max_memory_bytes": 33554432
}
}
You can combine --wasm-module and --wasm-config freely. Duplicate names across
either source are rejected at startup.
Set a per-module memory cap¶
Append :max_memory to the path in the --wasm-module flag:
# 32 MiB cap for this module only
mcp-v8 --wasm-module heavy=/opt/wasm/heavy.wasm:32m
Accepted size suffixes: k/K (kibibytes), m/M (mebibytes), g/G
(gibibytes). A plain integer is treated as bytes.
The cap is enforced as a structural pre-execution check: the module's declared initial memory page count and initial table element count must fit within the budget. Modules that exceed it are rejected before any V8 code runs.
Set the default memory cap¶
--wasm-default-max-memory applies to every module that does not have its own
per-module cap. The server default is 16m.
mcp-v8 --wasm-default-max-memory 32m \
--wasm-module math=/opt/wasm/math.wasm \
--wasm-module heavy=/opt/wasm/heavy.wasm:64m
In this example math is checked against 32 MiB and heavy against 64 MiB.
Build and use the SQLite example¶
The repository ships a ready-made Emscripten build script for SQLite.
Prerequisites: Emscripten SDK
installed and activated (source /path/to/emsdk_env.sh).
cd examples/sqlite-wasm
bash build.sh
The script downloads the SQLite 3.49.1 amalgamation and compiles it to
examples/sqlite-wasm/sqlite3.wasm with STANDALONE_WASM=1 and
ALLOW_MEMORY_GROWTH=1.
Start mcp-v8 with the resulting module:
mcp-v8 --stateless \
--wasm-module sqlite=examples/sqlite-wasm/sqlite3.wasm
Because the SQLite WASM module has WASI imports, mcp-v8 places it under the global
__wasm_sqlite (a compiled WebAssembly.Module object) rather than
auto-instantiating it. Your JS code must provide the required WASI stubs and
instantiate it manually:
// Enumerate what the module needs
var moduleImports = WebAssembly.Module.imports(__wasm_sqlite);
// Build an import object with stubs for each import
var importObject = {};
for (var i = 0; i < moduleImports.length; i++) {
var imp = moduleImports[i];
if (!importObject[imp.module]) importObject[imp.module] = {};
if (imp.kind === "function") {
importObject[imp.module][imp.name] = function () { return 0; };
}
}
var instance = new WebAssembly.Instance(__wasm_sqlite, importObject);
var exports = instance.exports;
// exports.sqlite3_open, exports.malloc, etc. are now available
The complete working example — including proper WASI stubs, a UTF-8
encoder/decoder, and a thin SQLite wrapper class — lives in
examples/sqlite-wasm/example.js. Send its contents as the code parameter of
run_js to run it end-to-end.
Let downstream clients discover a module¶
By default every loaded module is advertised on the server's MCP surface as a
stub tool named runjs__wasm__<name>, so an MCP client finds it via tools/list
or tool search. Calling the stub returns instructions to use the module from
JavaScript via run_js (it is not an executable proxy — see
concepts).
Give the stub a human description so agents know when to use it — either inline in
--wasm-config:
{ "sqlite": { "path": "/opt/wasm/sqlite3.wasm", "description": "In-memory SQLite database (exec/query SQL)." } }
or on the command line (overrides the config value):
mcp-v8 --http-port 8080 \
--wasm-module sqlite=/opt/wasm/sqlite3.wasm \
--wasm-stub-description sqlite="In-memory SQLite database (exec/query SQL)."
Disable the stubs with --wasm-stubs false, or change the runjs__ prefix with
--wasm-stub-prefix <prefix>.