Go插件模板
一个WebAssembly插件模板,用于使用hyper-MCP框架在Go中构建MCP(模型上下文协议)插件。
概述
此模板提供了一个创建作为WebAssembly模块运行的MCP插件的入门项目。它包括实现MCP协议处理程序所需的所有依赖关系和样板代码。
项目结构
.
├── .github/workflows # Sample workflows for Github Actions
|-- main.go # Plugin handler implementations
├── exports.go # WASM export wrappers for handlers
├── imports.go # Host function calls
├── types.go # MCP protocol types
├── go.mod # Go module definition
├── go.sum # Go module checksums
├── Dockerfile # Simple build for deploying a WASM
└── .gitignore # Git ignore rules入门指南
先决条件
- 转到1.22或更高版本
- TinyGo 0.40或更高版本
- Docker(用于构建WASM)
clang和lld(用于WASM编译)
发展
- 克隆或使用此模板 启动插件项目
- 实现插件处理程序 在
main.go:
插件处理程序必须在不使用goroutines的情况下实现 *除非* 您修改了Dockerfile构建以删除 -scheduler=none 从蒂尼戈建造旗帜。请注意,不建议这样做,因为hyper-mcp通常会为您处理并发执行。
注: 你只需要实现与你的插件相关的处理程序。例如,如果你的插件只提供工具,那么只实现ListTools()和CallTool()。所有其他处理程序都有开箱即用的默认实现。
ListTools()-描述可用工具CallTool()-执行工具ListResources()-列出可用资源ReadResource()-读取资源内容ListPrompts()-列出可用提示GetPrompt()-获取提示详细信息Complete()-提供自动完成建议ListResourceTemplates()-列出资源模板OnRootsListChanged()-处理根更改
- 本地建设 (WASM目标需要Docker):
GOOS=wasip1 GOARCH=wasm tinygo build -no-debug -panic=trap -scheduler=none -o plugin.wasm
docker build -t your-registry/your-plugin-name .依赖项
模板使用:
- extism/go pdk -Extism插件开发工具包
- 用于JSON序列化和时间处理的标准Go库
插件处理函数
您的插件可以实现以下处理程序的任意组合。 只实现插件所需的处理程序 -该模板为其他所有内容提供了合理的默认值:
| 处理程序 | 用途 | 必需 |
|---|---|---|
ListTools() | 声明可用工具 | 提供插件的工具 |
CallTool() | 执行工具 | 提供插件的工具 |
ListResources() | 声明可用资源 | 资源提供插件 |
ListResourceTemplates() | 声明资源模板 | 动态资源插件 |
ReadResource() | 读取资源内容 | 资源提供插件 |
ListPrompts() | 声明可用提示 | 提示提供插件 |
GetPrompt() | 检索特定提示 | 提示提供插件 |
Complete() | 提供自动补全功能 | 支持补全的插件 |
OnRootsListChanged() | 处理根更改 | 插件对根更改做出反应 |
示例:仅限工具的插件
如果你的插件只提供工具,你只需要实现:
func ListTools(input ListToolsRequest) (*ListToolsResult, error) {
return &ListToolsResult{
Tools: []Tool{
{
Name: "greet",
Description: ptrString("Greet a person"),
InputSchema: ToolSchema{
Type: "object",
Properties: map[string]interface{}{
"name": map[string]interface{}{
"type": "string",
"description": "The person's name",
},
},
Required: []string{"name"},
},
},
},
}, nil
}
func CallTool(input CallToolRequest) (*CallToolResult, error) {
switch input.Request.Name {
case "greet":
name, ok := input.Request.Arguments["name"].(string)
if !ok {
return &CallToolResult{
Content: []json.RawMessage{
[]byte(`{"type":"text","text":"name argument required"}`),
},
}, nil
}
return &CallToolResult{
Content: []json.RawMessage{
[]byte(fmt.Sprintf(`{"type":"text","text":"Hello, %s!"}`, name)),
},
}, nil
default:
return &CallToolResult{
Content: []json.RawMessage{
[]byte(fmt.Sprintf(`{"type":"text","text":"Unknown tool: %s"}`, input.Request.Name)),
},
}, nil
}
}所有其他处理程序将使用其默认实现。
主机功能
您的插件可以调用这些宿主函数与客户端和MCP服务器进行交互。可通过直接函数调用 imports.go:
// Example usage
result, err := CreateElicitation(ElicitRequestParamWithTimeout{...})用户交互
**CreateElicitation(input ElicitRequestParamWithTimeout) (*ElicitResult, error)**
通过客户端的启发界面请求用户输入。当您的插件在执行过程中需要用户指导、决策或确认时,请使用此功能。
result, err := CreateElicitation(ElicitRequestParamWithTimeout{
Message: "Please provide your name",
RequestedSchema: Schema{
Type: "object",
Properties: map[string]json.RawMessage{
"name": json.RawMessage(`{"type":"string"}`),
},
},
Timeout: ptrInt64(30000), // 30 second timeout
})消息生成
**CreateMessage(input CreateMessageRequestParam) (*CreateMessageResult, error)**
通过客户端的采样接口请求消息创建。当您的插件需要人工智能辅助的智能文本生成或分析时,请使用此功能。
result, err := CreateMessage(CreateMessageRequestParam{
MaxTokens: 1024,
Messages: []json.RawMessage{
// conversation history
},
SystemPrompt: ptrString("You are a helpful assistant"),
})资源发现
**ListRoots() (*ListRootsResult, error)**
列出客户端的根目录或资源。使用此功能可以发现可用的根资源(通常是文件系统根),并了解插件可以访问的资源范围。
roots, err := ListRoots()
if err == nil {
for _, root := range roots.Roots {
fmt.Printf("Root: %s at %s\n", *root.Name, root.URI)
}
}日志记录
NotifyLoggingMessage(input LoggingMessageNotificationParam) error
向客户端发送诊断、信息、警告或错误消息。客户端的日志记录级别决定了要处理和显示哪些消息。
NotifyLoggingMessage(LoggingMessageNotificationParam{
Level: LoggingLevelInfo,
Logger: ptrString("my_plugin"),
Data: json.RawMessage(`{"message": "Processing started"}`),
})进度报告
NotifyProgress(input ProgressNotificationParam) error
报告长时间运行操作期间的进度。允许客户端向用户显示进度条或状态信息。
NotifyProgress(ProgressNotificationParam{
Progress: 50,
ProgressToken: "task-1",
Total: ptrFloat64(100),
})列表更改通知
当插件的可用项更改时通知客户端:
NotifyToolListChanged() error
- 在添加、删除或修改可用工具时调用此命令
NotifyResourceListChanged() error
- 在添加、删除或修改可用资源时调用此命令
NotifyPromptListChanged() error
- 在添加、删除或修改可用提示时调用此命令
NotifyResourceUpdated(input ResourceUpdatedNotificationParam) error
- 当您修改特定资源的内容时调用此命令
// When your plugin's tools change
NotifyToolListChanged()
// When a specific resource is updated
NotifyResourceUpdated(ResourceUpdatedNotificationParam{
URI: "resource://my-resource",
})示例:带有进度的交互式工具
func CallTool(input CallToolRequest) (*CallToolResult, error) {
switch input.Request.Name {
case "long_task":
// Log start
NotifyLoggingMessage(LoggingMessageNotificationParam{
Level: LoggingLevelInfo,
Data: json.RawMessage(`{"message": "Starting long task"}`),
})
// Do work with progress updates
for i := 0; i < 10; i++ {
// ... do work ...
NotifyProgress(ProgressNotificationParam{
Progress: float64((i + 1) * 10),
ProgressToken: "task-1",
Total: ptrFloat64(100),
})
}
return &CallToolResult{
Content: []json.RawMessage{
[]byte(`{"type":"text","text":"Task completed"}`),
},
}, nil
default:
return &CallToolResult{
Content: []json.RawMessage{
[]byte(fmt.Sprintf(`{"type":"text","text":"Unknown tool: %s"}`, input.Request.Name)),
},
}, nil
}
}配电楼
使用Docker
包括 Dockerfile 只需封装您的WASM:
GOOS=wasip1 GOARCH=wasm tinygo build -no-debug -panic=trap -scheduler=none -o plugin.wasm
docker build -t your-registry/your-plugin-name .Docker构建:
- 将Go代码编译为
wasip1目标 - 创建仅包含已编译图像的最小图像
plugin.wasm - 输出OCI兼容的容器映像
手动构建
要在没有Docker的情况下手动构建(需要TinyGo 0.40+):
# Build for WASM
GOOS=wasip1 GOARCH=wasm tinygo build -no-debug -panic=trap -scheduler=none -o plugin.wasm
# Result is at: plugin.wasm实施指南
创建工具
下面是一个实现简单工具的示例:
func ListTools(input ListToolsRequest) (*ListToolsResult, error) {
return &ListToolsResult{
Tools: []Tool{
{
Name: "greet",
Description: ptrString("Greet a person"),
InputSchema: ToolSchema{
Type: "object",
Properties: map[string]interface{}{
"name": map[string]interface{}{
"type": "string",
"description": "The person's name",
},
},
Required: []string{"name"},
},
},
},
}, nil
}
func CallTool(input CallToolRequest) (*CallToolResult, error) {
switch input.Request.Name {
case "greet":
name, ok := input.Request.Arguments["name"].(string)
if !ok {
return &CallToolResult{
Content: []json.RawMessage{
[]byte(`{"type":"text","text":"name argument required"}`),
},
}, nil
}
return &CallToolResult{
Content: []json.RawMessage{
[]byte(fmt.Sprintf(`{"type":"text","text":"Hello, %s!"}`, name)),
},
}, nil
default:
return &CallToolResult{
Content: []json.RawMessage{
[]byte(fmt.Sprintf(`{"type":"text","text":"Unknown tool: %s"}`, input.Request.Name)),
},
}, nil
}
}创建资源
实现资源的示例:
func ListResources(input ListResourcesRequest) (*ListResourcesResult, error) {
return &ListResourcesResult{
Resources: []Resource{
{
URI: "resource://example",
Name: "Example Resource",
Description: ptrString("An example resource"),
MimeType: ptrString("text/plain"),
},
},
}, nil
}
func ReadResource(input ReadResourceRequest) (*ReadResourceResult, error) {
switch input.Request.URI {
case "resource://example":
return &ReadResourceResult{
Contents: []json.RawMessage{
[]byte(`{"uri":"resource://example","mimeType":"text/plain","text":"Resource content here"}`),
},
}, nil
default:
return &ReadResourceResult{
Contents: []json.RawMessage{
[]byte(fmt.Sprintf(`{"type":"text","text":"Unknown resource: %s"}`, input.Request.URI)),
},
}, nil
}
}Helper函数
该模板包括一些使用指针的有用辅助函数:
// Helper to create string pointers
func ptrString(s string) *string {
return &s
}
// Helper to create int64 pointers
func ptrInt64(i int64) *int64 {
return &i
}
// Helper to create float64 pointers
func ptrFloat64(f float64) *float64 {
return &f
}
// Helper to create bool pointers
func ptrBool(b bool) *bool {
return &b
}hyper-mcp中的配置
构建并发布插件后,在hyper-mcp中配置它:
{
"plugins": {
"my_plugin": {
"url": "oci://your-registry/your-plugin-name:latest"
}
}
}对于本地开发/测试:
{
"plugins": {
"my_plugin": {
"url": "file:///path/to/plugin.wasm"
}
}
}测试
要在本地测试您的插件:
- 构建它:
docker build -t my-plugin . && docker run --rm -v $(pwd):/workspace my-plugin cp /plugin.wasm /workspace/ - 更新hyper-mcp的配置以指向
file://统一资源定位符 - 使用以下命令启动hyper-mcp
RUST_LOG=debug - 通过Claude Desktop、Cursor IDE或其他MCP客户端进行测试
资源
许可证
与hyper-mcp相同-Apache 2.0
