Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions WASM_DEVELOPMENT_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# DANP-Engine WASM Module Development Manual

This document provides a clear guide for developers on creating, compiling, and integrating new WASM modules into the DANP-Engine.

## Introduction

WASM (WebAssembly) modules are a core component of the DANP-Engine, offering a secure and portable way to execute decentralized AI tools and various computational tasks. By encapsulating functionality within WASM modules, we ensure that code runs in a sandboxed environment, protecting the host system's security.

## Technology Stack

To streamline the development process and maintain consistency across the project, we have chosen the following technologies for writing WASM modules:

- **Go:** Aligns with the core language of the DANP-Engine.
- **extism/go-pdk:** A powerful Go PDK (Plugin Development Kit) that greatly simplifies WASM module development, especially input/output handling and interaction with the host environment, avoiding complex manual memory management.
- **TinyGo:** A Go compiler for embedded systems and WebAssembly, which produces smaller WASM files.

## Steps to Write a New Module

Here are the complete steps to add a new WASM module to the DANP-Engine:

### 1. Create the Directory

In the `wasm-examples/` directory at the project root, create a new subdirectory for your module. For example, for the `say_hello` module:

```bash
mkdir -p wasm-examples/say_hello
```

### 2. Write the Go Code

In your module's directory, create a `main.go` file. Use `extism/go-pdk` to handle inputs and outputs.

**Example: `wasm-examples/say_hello/main.go`**

```go
package main

import (
"fmt"
"github.com/extism/go-pdk"
)

//export say_hello
func say_hello() int32 {
// Read the input string from the host
name := pdk.InputString()

// Create the greeting message
greeting := fmt.Sprintf("Hello, %s!", name)

// Return the greeting to the host
pdk.OutputString(greeting)
return 0 // Indicate success
}

func main() {}
```

### 3. Compile the WASM Module

We provide a unified command for compiling, which handles Go module initialization, dependency fetching, and WASM compilation automatically. To produce the smallest possible WASM files, we use several optimization flags.

Run the following command in your module's directory:

```bash
cd wasm-examples/YOUR_MODULE_NAME && \
go mod init YOUR_MODULE_NAME >/dev/null 2>&1 && \
go get github.com/extism/go-pdk && \
GOOS=wasip1 GOARCH=wasm tinygo build -o YOUR_MODULE_NAME.wasm -target wasi -opt=z -no-debug -scheduler=none main.go
```

Replace `YOUR_MODULE_NAME` with the name of your module.

**Optimization Flags:**
- `-opt=z`: Aggressively optimizes for size, potentially at the cost of some execution speed.
- `-no-debug`: Strips all debug information from the binary.
- `-scheduler=none`: Removes the Go scheduler, which is not needed for simple, non-concurrent WASM modules.

### 4. Integrate into `mcp_manifest.yaml`


Finally, in the `config/mcp_manifest.yaml` file, add your new module to the `modules` list.

**Example:**

```yaml
modules:
- name: "say_hello"
wasm_path: "file://wasm-examples/say_hello/say_hello.wasm"
tools:
- name: "say_hello"
description: "Greet someone by name"
inputs:
- name: "name"
type: "string"
required: true
description: "Name to greet"
outputs:
type: "string"
description: "Greeting message"
```

## Notes

- **Input/Output:** `extism/go-pdk` greatly simplifies I/O. For simple strings, you can use `pdk.InputString()` and `pdk.OutputString()`. For complex JSON data, read the bytes with `pdk.Input()`, then parse with `json.Unmarshal`.
- **Error Handling:** It is recommended to add proper error handling in your WASM functions and pass error messages back to the host via return values or output strings.
- **Performance:** Keep your WASM modules lightweight and efficient. Avoid long-running or memory-intensive tasks within the WASM module itself.
14 changes: 14 additions & 0 deletions config/mcp_manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,17 @@ modules:
outputs:
type: "string"
description: "Greeting message"

- name: "data_validation"
wasm_path: "file://wasm-examples/data-validation/validate.wasm"
tools:
- name: "validate_data"
description: "Validate a JSON string to ensure it contains a 'signature' key"
inputs:
- name: "json_data"
type: "string"
required: true
description: "The JSON data to validate"
outputs:
type: "string"
description: "A JSON string indicating success or failure"
28 changes: 13 additions & 15 deletions core/mcp/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ import (

// MCPServer represents the core MCP server implementation
type MCPServer struct {
server *server.MCPServer
config *Config
server *server.MCPServer
config *Config
wasmEngine *WASMEngine
registeredTools []string
}

// Config holds MCP server configuration
Expand Down Expand Up @@ -135,6 +137,7 @@ func NewMCPServer(config *Config) *MCPServer {

// Register WASM module tools from config
log.Printf("Registering %d WASM modules from config", len(config.Modules))
registeredTools := []string{}
for _, module := range config.Modules {
log.Printf("Processing module: %s (WASM path: %s)", module.Name, module.WASMPath)
log.Printf("Loading module with %d tools", len(module.Tools))
Expand All @@ -149,12 +152,17 @@ func NewMCPServer(config *Config) *MCPServer {
log.Printf("Failed to register tools from WASM module %s: %v", module.WASMPath, err)
} else {
log.Printf("Successfully registered %d tools for module: %s", len(module.Tools), module.Name)
for _, tool := range module.Tools {
registeredTools = append(registeredTools, tool.Name)
}
}
}

return &MCPServer{
server: mcpServer,
config: config,
server: mcpServer,
config: config,
wasmEngine: wasmEngine,
registeredTools: registeredTools,
}
}

Expand Down Expand Up @@ -186,19 +194,9 @@ func (s *MCPServer) Start() error {
return
}

// Get all registered tools
toolNames := []string{}

// Manually track tools from our config
for _, module := range s.config.Modules {
for _, tool := range module.Tools {
toolNames = append(toolNames, tool.Name)
}
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"tools": toolNames,
"tools": s.registeredTools,
})
})

Expand Down
5 changes: 5 additions & 0 deletions wasm-examples/data-validation/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module data-validation

go 1.24.4

require github.com/extism/go-pdk v1.1.3 // indirect
2 changes: 2 additions & 0 deletions wasm-examples/data-validation/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ=
github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4=
37 changes: 37 additions & 0 deletions wasm-examples/data-validation/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package main

import (
"encoding/json"
"fmt"

"github.com/extism/go-pdk"
)

//export validate_data
func validate_data() int32 {
// Read the input JSON string from the host
input_string := pdk.InputString()

var data map[string]interface{}
err := json.Unmarshal([]byte(input_string), &data)
if err != nil {
// Return error message if JSON is invalid
error_msg := fmt.Sprintf(`{"valid": false, "error": "invalid JSON: %s"}`, err.Error())
pdk.OutputString(error_msg)
return 1 // Indicate failure
}

// Check if the "signature" key exists
if _, ok := data["signature"]; !ok {
error_msg := `{"valid": false, "error": "missing 'signature' key"}`
pdk.OutputString(error_msg)
return 1 // Indicate failure
}

// If validation is successful
success_msg := `{"valid": true}`
pdk.OutputString(success_msg)
return 0 // Indicate success
}

func main() {}
Binary file added wasm-examples/data-validation/validate.wasm
Binary file not shown.
5 changes: 5 additions & 0 deletions wasm-examples/say_hello/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module say_hello

go 1.24.3

require github.com/extism/go-pdk v1.1.3 // indirect
2 changes: 2 additions & 0 deletions wasm-examples/say_hello/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ=
github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4=
22 changes: 22 additions & 0 deletions wasm-examples/say_hello/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package main

import (
"fmt"

"github.com/extism/go-pdk"
)

//export say_hello
func say_hello() int32 {
// Read the input string from the host
name := pdk.InputString()

// Create the greeting message
greeting := fmt.Sprintf("Hello, %s!", name)

// Return the greeting to the host
pdk.OutputString(greeting)
return 0 // Indicate success
}

func main() {}
Binary file added wasm-examples/say_hello/say_hello.wasm
Binary file not shown.