Files
higress/plugins/wasm-go/mcp-servers

MCP Server Implementation Guide

This guide explains how to implement a Model Context Protocol (MCP) server using the Higress WASM Go SDK. MCP servers provide tools and resources that extend the capabilities of AI assistants.

Overview

An MCP server is a standalone application that communicates with AI assistants through the Model Context Protocol. It can provide:

  • Tools: Functions that can be called by the AI to perform specific tasks
  • Resources: Data that can be accessed by the AI

Note

: MCP server plugins require Higress version 2.1.0 or higher to be used.

Project Structure

A typical MCP server project has the following structure:

my-mcp-server/
├── go.mod                 # Go module definition
├── go.sum                 # Go module checksums
├── main.go                # Entry point that registers tools and resources
├── server/
│   └── server.go          # Server configuration and parsing
└── tools/
    └── my_tool.go         # Tool implementation

Server Configuration

The server configuration defines the parameters needed for the server to function. For example:

// server/server.go
package server

import (
    "encoding/json"
    "errors"

    "github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
)

// Define your server configuration structure
type MyMCPServer struct {
    ApiKey string `json:"apiKey"`
    // Add other configuration fields as needed
}

// Validate the configuration
func (s MyMCPServer) ConfigHasError() error {
    if s.ApiKey == "" {
        return errors.New("missing api key")
    }
    return nil
}

// Parse configuration from JSON
func ParseFromConfig(configBytes []byte, server *MyMCPServer) error {
    return json.Unmarshal(configBytes, server)
}

// Parse configuration from HTTP request
func ParseFromRequest(ctx wrapper.HttpContext, server *MyMCPServer) error {
    return ctx.ParseMCPServerConfig(server)
}

Tool Implementation

Each tool should be implemented as a struct with the following methods:

  1. Description(): Returns a description of the tool
  2. InputSchema(): Returns the JSON schema for the tool's input parameters
  3. Create(): Creates a new instance of the tool with the provided parameters
  4. Call(): Executes the tool's functionality

Example:

// tools/my_tool.go
package tools

import (
    "encoding/json"
    "fmt"
    "net/http"
    
    "my-mcp-server/server"
    
    "github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
)

// Define your tool structure with input parameters
type MyTool struct {
    Param1 string `json:"param1" jsonschema_description:"Description of param1" jsonschema:"example=example value"`
    Param2 int    `json:"param2,omitempty" jsonschema_description:"Description of param2" jsonschema:"default=5"`
}

// Description returns the description field for the MCP tool definition.
// This corresponds to the "description" field in the MCP tool JSON response,
// which provides a human-readable explanation of the tool's purpose and usage.
func (t MyTool) Description() string {
    return `Detailed description of what this tool does and when to use it.`
}

// InputSchema returns the inputSchema field for the MCP tool definition.
// This corresponds to the "inputSchema" field in the MCP tool JSON response,
// which defines the JSON Schema for the tool's input parameters, including
// property types, descriptions, and required fields.
func (t MyTool) InputSchema() map[string]any {
    return wrapper.ToInputSchema(&MyTool{})
}

// Create instantiates a new tool instance based on the input parameters
// from an MCP tool call. It deserializes the JSON parameters into a struct,
// applying default values for optional fields, and returns the configured tool instance.
func (t MyTool) Create(params []byte) wrapper.MCPTool[server.MyMCPServer] {
    myTool := &MyTool{
        Param2: 5, // Default value
    }
    json.Unmarshal(params, &myTool)
    return myTool
}

// Call implements the core logic for handling an MCP tool call. This method is executed
// when the tool is invoked through the MCP framework. It processes the configured parameters,
// makes any necessary API requests, and formats the results to be returned to the caller.
func (t MyTool) Call(ctx wrapper.HttpContext, config server.MyMCPServer) error {
    // Validate configuration
    err := server.ParseFromRequest(ctx, &config)
    if err != nil {
        return err
    }
    err = config.ConfigHasError()
    if err != nil {
        return err
    }
    
    // Implement your tool's logic here
    // ...
    
    // Return results
    ctx.SendMCPToolTextResult(fmt.Sprintf("Result: %s, %d", t.Param1, t.Param2))
    return nil
}

Main Entry Point

The main.go file is the entry point for your MCP server. It registers your tools and resources:

// main.go
package main

import (
    "my-mcp-server/server"
    "my-mcp-server/tools"
    
    "github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
)

func main() {}

func init() {
    wrapper.SetCtx(
        "my-mcp-server", // Server name
        wrapper.ParseRawConfig(server.ParseFromConfig),
        wrapper.AddMCPTool("my_tool", tools.MyTool{}), // Register tools
        // Add more tools as needed
    )
}

Dependencies

Your MCP server must use a specific version of the wasm-go SDK that supports Go 1.24's WebAssembly compilation features:

# Add the required dependency with the specific version tag
go get github.com/alibaba/higress/plugins/wasm-go@wasm-go-1.24

Building the WASM Binary

To compile your Go code into a WebAssembly (WASM) file, use the following command:

GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o main.wasm main.go

This command sets the target operating system to wasip1 (WebAssembly System Interface) and architecture to wasm (WebAssembly), then builds your code as a C-shared library and outputs it as main.wasm.

Using the Makefile

A Makefile is provided to simplify the build process. It includes the following targets:

  • make build: Builds the WASM binary for your MCP server
  • make build-image: Builds a Docker image containing your MCP server
  • make build-push: Builds and pushes the Docker image to a registry
  • make clean: Removes build artifacts
  • make help: Shows available targets and variables

You can customize the build by setting the following variables:

# Build with a custom server name
make SERVER_NAME=my-mcp-server build

# Build with a custom registry
make REGISTRY=my-registry.example.com/ build-image

# Build with a specific version tag
make SERVER_VERSION=1.0.0 build-image

Testing

You can create unit tests for your tools to verify their functionality:

// tools/my_tool_test.go
package tools

import (
    "encoding/json"
    "fmt"
    "testing"
)

func TestMyToolInputSchema(t *testing.T) {
    myTool := MyTool{}
    schema := myTool.InputSchema()
    
    schemaJSON, err := json.MarshalIndent(schema, "", "  ")
    if err != nil {
        t.Fatalf("Failed to marshal schema to JSON: %v", err)
    }
    
    fmt.Printf("MyTool InputSchema:\n%s\n", string(schemaJSON))
    
    if len(schema) == 0 {
        t.Error("InputSchema returned an empty schema")
    }
}