Skip to content

Commit e4c3f87

Browse files
authored
Merge pull request #45 from DANP-LABS/feat/add-wasm-examples
Feat/add wasm examples
2 parents 8b72c24 + 8b02eeb commit e4c3f87

File tree

11 files changed

+207
-15
lines changed

11 files changed

+207
-15
lines changed

WASM_DEVELOPMENT_GUIDE.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# DANP-Engine WASM Module Development Manual
2+
3+
This document provides a clear guide for developers on creating, compiling, and integrating new WASM modules into the DANP-Engine.
4+
5+
## Introduction
6+
7+
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.
8+
9+
## Technology Stack
10+
11+
To streamline the development process and maintain consistency across the project, we have chosen the following technologies for writing WASM modules:
12+
13+
- **Go:** Aligns with the core language of the DANP-Engine.
14+
- **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.
15+
- **TinyGo:** A Go compiler for embedded systems and WebAssembly, which produces smaller WASM files.
16+
17+
## Steps to Write a New Module
18+
19+
Here are the complete steps to add a new WASM module to the DANP-Engine:
20+
21+
### 1. Create the Directory
22+
23+
In the `wasm-examples/` directory at the project root, create a new subdirectory for your module. For example, for the `say_hello` module:
24+
25+
```bash
26+
mkdir -p wasm-examples/say_hello
27+
```
28+
29+
### 2. Write the Go Code
30+
31+
In your module's directory, create a `main.go` file. Use `extism/go-pdk` to handle inputs and outputs.
32+
33+
**Example: `wasm-examples/say_hello/main.go`**
34+
35+
```go
36+
package main
37+
38+
import (
39+
"fmt"
40+
"github.com/extism/go-pdk"
41+
)
42+
43+
//export say_hello
44+
func say_hello() int32 {
45+
// Read the input string from the host
46+
name := pdk.InputString()
47+
48+
// Create the greeting message
49+
greeting := fmt.Sprintf("Hello, %s!", name)
50+
51+
// Return the greeting to the host
52+
pdk.OutputString(greeting)
53+
return 0 // Indicate success
54+
}
55+
56+
func main() {}
57+
```
58+
59+
### 3. Compile the WASM Module
60+
61+
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.
62+
63+
Run the following command in your module's directory:
64+
65+
```bash
66+
cd wasm-examples/YOUR_MODULE_NAME && \
67+
go mod init YOUR_MODULE_NAME >/dev/null 2>&1 && \
68+
go get github.com/extism/go-pdk && \
69+
GOOS=wasip1 GOARCH=wasm tinygo build -o YOUR_MODULE_NAME.wasm -target wasi -opt=z -no-debug -scheduler=none main.go
70+
```
71+
72+
Replace `YOUR_MODULE_NAME` with the name of your module.
73+
74+
**Optimization Flags:**
75+
- `-opt=z`: Aggressively optimizes for size, potentially at the cost of some execution speed.
76+
- `-no-debug`: Strips all debug information from the binary.
77+
- `-scheduler=none`: Removes the Go scheduler, which is not needed for simple, non-concurrent WASM modules.
78+
79+
### 4. Integrate into `mcp_manifest.yaml`
80+
81+
82+
Finally, in the `config/mcp_manifest.yaml` file, add your new module to the `modules` list.
83+
84+
**Example:**
85+
86+
```yaml
87+
modules:
88+
- name: "say_hello"
89+
wasm_path: "file://wasm-examples/say_hello/say_hello.wasm"
90+
tools:
91+
- name: "say_hello"
92+
description: "Greet someone by name"
93+
inputs:
94+
- name: "name"
95+
type: "string"
96+
required: true
97+
description: "Name to greet"
98+
outputs:
99+
type: "string"
100+
description: "Greeting message"
101+
```
102+
103+
## Notes
104+
105+
- **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`.
106+
- **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.
107+
- **Performance:** Keep your WASM modules lightweight and efficient. Avoid long-running or memory-intensive tasks within the WASM module itself.

config/mcp_manifest.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,17 @@ modules:
3939
outputs:
4040
type: "string"
4141
description: "Greeting message"
42+
43+
- name: "data_validation"
44+
wasm_path: "file://wasm-examples/data-validation/validate.wasm"
45+
tools:
46+
- name: "validate_data"
47+
description: "Validate a JSON string to ensure it contains a 'signature' key"
48+
inputs:
49+
- name: "json_data"
50+
type: "string"
51+
required: true
52+
description: "The JSON data to validate"
53+
outputs:
54+
type: "string"
55+
description: "A JSON string indicating success or failure"

core/mcp/mcp.go

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ import (
1919

2020
// MCPServer represents the core MCP server implementation
2121
type MCPServer struct {
22-
server *server.MCPServer
23-
config *Config
22+
server *server.MCPServer
23+
config *Config
24+
wasmEngine *WASMEngine
25+
registeredTools []string
2426
}
2527

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

136138
// Register WASM module tools from config
137139
log.Printf("Registering %d WASM modules from config", len(config.Modules))
140+
registeredTools := []string{}
138141
for _, module := range config.Modules {
139142
log.Printf("Processing module: %s (WASM path: %s)", module.Name, module.WASMPath)
140143
log.Printf("Loading module with %d tools", len(module.Tools))
@@ -149,12 +152,17 @@ func NewMCPServer(config *Config) *MCPServer {
149152
log.Printf("Failed to register tools from WASM module %s: %v", module.WASMPath, err)
150153
} else {
151154
log.Printf("Successfully registered %d tools for module: %s", len(module.Tools), module.Name)
155+
for _, tool := range module.Tools {
156+
registeredTools = append(registeredTools, tool.Name)
157+
}
152158
}
153159
}
154160

155161
return &MCPServer{
156-
server: mcpServer,
157-
config: config,
162+
server: mcpServer,
163+
config: config,
164+
wasmEngine: wasmEngine,
165+
registeredTools: registeredTools,
158166
}
159167
}
160168

@@ -186,19 +194,9 @@ func (s *MCPServer) Start() error {
186194
return
187195
}
188196

189-
// Get all registered tools
190-
toolNames := []string{}
191-
192-
// Manually track tools from our config
193-
for _, module := range s.config.Modules {
194-
for _, tool := range module.Tools {
195-
toolNames = append(toolNames, tool.Name)
196-
}
197-
}
198-
199197
w.Header().Set("Content-Type", "application/json")
200198
json.NewEncoder(w).Encode(map[string]interface{}{
201-
"tools": toolNames,
199+
"tools": s.registeredTools,
202200
})
203201
})
204202

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module data-validation
2+
3+
go 1.24.4
4+
5+
require github.com/extism/go-pdk v1.1.3 // indirect
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ=
2+
github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4=
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/extism/go-pdk"
8+
)
9+
10+
//export validate_data
11+
func validate_data() int32 {
12+
// Read the input JSON string from the host
13+
input_string := pdk.InputString()
14+
15+
var data map[string]interface{}
16+
err := json.Unmarshal([]byte(input_string), &data)
17+
if err != nil {
18+
// Return error message if JSON is invalid
19+
error_msg := fmt.Sprintf(`{"valid": false, "error": "invalid JSON: %s"}`, err.Error())
20+
pdk.OutputString(error_msg)
21+
return 1 // Indicate failure
22+
}
23+
24+
// Check if the "signature" key exists
25+
if _, ok := data["signature"]; !ok {
26+
error_msg := `{"valid": false, "error": "missing 'signature' key"}`
27+
pdk.OutputString(error_msg)
28+
return 1 // Indicate failure
29+
}
30+
31+
// If validation is successful
32+
success_msg := `{"valid": true}`
33+
pdk.OutputString(success_msg)
34+
return 0 // Indicate success
35+
}
36+
37+
func main() {}
222 KB
Binary file not shown.

wasm-examples/say_hello/go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module say_hello
2+
3+
go 1.24.3
4+
5+
require github.com/extism/go-pdk v1.1.3 // indirect

wasm-examples/say_hello/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ=
2+
github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4=

wasm-examples/say_hello/main.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/extism/go-pdk"
7+
)
8+
9+
//export say_hello
10+
func say_hello() int32 {
11+
// Read the input string from the host
12+
name := pdk.InputString()
13+
14+
// Create the greeting message
15+
greeting := fmt.Sprintf("Hello, %s!", name)
16+
17+
// Return the greeting to the host
18+
pdk.OutputString(greeting)
19+
return 0 // Indicate success
20+
}
21+
22+
func main() {}

0 commit comments

Comments
 (0)