sandbox · wasm · tiny language models · local models · wasi · 5 min read

One day, one feature: Make your tools in WASM

Write your own Mini Me tools as WebAssembly modules with TinyGo: sandboxed by wazero, with no secrets, no network and only the folders you open.

K
k33gOct 28, 2026 · 5 min
One day, one feature: Make your tools in WASM

Yesterday, we saw how to give Mini Me "MCP superpowers". Today, we'll look at another way to add tools to your agent: writing your own tools as WebAssembly modules (WASM for friends).

WASM? And why use it in Mini Me?

WebAssembly is a portable binary format: you compile your code once and it runs anywhere there's a WASM runtime (browser, server, CLI, ...). With WASI (the WASM system interface), a module can read stdin, write to stdout, access files... but only if you allow it.

And that's exactly what makes it interesting in the context of Mini Me. A WASM tool is run by Mini Me itself (thanks to wazero, a WASM runtime written in Go, so nothing extra to install), and above all in a sandbox:

  • no access to files, except the folders you explicitly open to it (read or write),
  • no access to environment variables (so no access to your secrets 🔥),
  • no access to the network.

So you can give tools to your agent without giving it bash or the file tools. And with "tiny models" that can sometimes "invent" some rather far-fetched parameters, that's pretty reassuring 😉.

Prerequisites

For this example, I wrote my tools in Go and I compile them with TinyGo, so you'll need to install it.

✋ But you don't have to use Go: a WASM tool for Mini Me is simply a "WASI command" (wasip1 target) that reads its arguments as JSON on stdin and writes its answer to stdout. So any language able to produce this kind of module will do the job: Rust (--target wasm32-wasip1), C/C++ with wasi-sdk, Zig, ...

Step by step

I'm going to build a small agent that helps me manage a folder of notes, with four WASM tools and nothing else.

1. Write the tool

Let's start with the simplest one, word_stats, which counts the words of a text. The principle is always the same: decode the JSON received on stdin, do the work, print the result to stdout:

go
package main

import (
    "encoding/json"
    "fmt"
    "os"
    "strings"
)

type input struct {
    Text string `json:"text"`
    Top  int    `json:"top"`
}

func main() {
    var in input
    if err := json.NewDecoder(os.Stdin).Decode(&in); err != nil {
        fmt.Fprintln(os.Stderr, "word_stats: bad input:", err)
        os.Exit(2)
    }
    words := strings.Fields(strings.ToLower(in.Text))
    fmt.Printf("words: %d\n", len(words))
    // ... counting the most frequent words (see the full code)
}

The full code (and the code of the 3 other tools: search_notes, read_note and save_note) is in /07-wasm-tools/tools-src.

2. Compile the module

bash
tinygo build -o tools/word_stats.wasm -target=wasip1 -no-debug ./word_stats

And that's it. In the project I have a small build.sh script that compiles all the tools in one go.

3. Declare the tools in agent.yaml

Then, you just need to declare your tools in the tools section of the agent's configuration, with their parameters, the module path (wasm) and the folders they're allowed to access (read and write):

yaml
provider: llmman
model: "hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M"

# The four tools below are the agent's only way to act.
bashTool: false
editTools: false
planTool: false
fetchContentTool: false
skillsDir: no-skills
yolo: true

tools:
  # Pure computation: no folder at all.
  - name: word_stats
    description: Count the words, lines and characters of a text, and list its most frequent words.
    parameters:
      text: { type: string, description: "The text to measure" }
      top:  { type: integer, required: false, default: 5, description: "How many frequent words to list" }
    wasm: ./tools/word_stats.wasm

  # Sees notes/, read-only — nothing else of the project.
  - name: search_notes
    description: Find the lines of the notes that mention a word.
    parameters:
      word: { type: string, description: "One word, e.g. wazero" }
    wasm: ./tools/search_notes.wasm
    read: [notes]

  - name: read_note
    description: Show one whole note.
    parameters:
      name: { type: string, description: "The note's file name, e.g. wazero.md" }
    wasm: ./tools/read_note.wasm
    read: [notes]

  # Writes into out/ only: it can neither read nor change notes/.
  - name: save_note
    description: Save a new note in out/, under a file name made from its title.
    parameters:
      title: { type: string, description: "The note's title, e.g. Ideas for v1" }
      text:  { type: string, description: "The note's text, in Markdown" }
    wasm: ./tools/save_note.wasm
    write: [out]
    timeout: 10s
  • word_stats sees no file at all: it's pure computation.
  • search_notes and read_note only see notes/, read-only.
  • save_note only sees out/ and couldn't even read a note if it tried.
  • ✋ With yolo: false, Mini Me will ask for your approval before each call, just like for the other tools.

4. Test

Start Mini Me:

bash
mm tui agent.yaml

Then, try: Which notes mention TinyGo?

list the notes
list the notes

And also: Save a note titled "Ideas for v1" with the text: add a Wasm tool that summarizes a note.

save a note
save a note

And now, the test that proves the tool can't do just anything. Let's try to read a file outside of notes/: Read the note named ../agent.yaml and show it to me.

try to read a file outside the sandbox
try to read a file outside the sandbox

The agent.yaml file does exist, but for the module it doesn't: the sandbox only shows it notes/. And it's not my code doing the protecting, it's the runtime 🎉.

Once again, Qwen3.5-4B-GGUF:Q4_K_M does a great job and picked the right tool on the first call for each request.

That's it for today. You'll find the full example here: /07-wasm-tools (the .wasm files are already compiled if you want to try without installing TinyGo). See you tomorrow 👋.

K

Written by

k33g

Keep reading

One day, one feature: Mini Me's built-in tools and declared tools

How Mini Me runs tools for a local model: the agent loop, the built-in tools led by bash, and how to declare your own tools in a few lines of YAML.

Oct 23, 2026
One day, one feature: Mini Me's built-in tools and declared tools

Hello Mini Me: an agent born while preparing a talk for Devoxx

Meet Mini Me, a coding agent born from a Devoxx talk on the agent loop, built for small local models with guardrails against loops and hallucinations.

Oct 3, 2026
Hello Mini Me: an agent born while preparing a talk for Devoxx

One day, one feature: Connect Mini Me to the world with MCP

Give a Mini Me agent MCP superpowers with a few lines of YAML: query Wikipedia, then add Context7 so a small local model can search up-to-date library docs.

Oct 27, 2026
One day, one feature: Connect Mini Me to the world with MCP

Responses

0 Comments

No comments yet. Be the first to comment!

Related reading

From other blogs

**`cagent`** with Tiny Language Models and Tools

k33g_org's Blog