Full Blog TOC

Full Blog Table Of Content with Keywords Available HERE

Monday, September 15, 2025

Create GO MCP Server and a Valid SSL Certificate

 



In this post we create GO bassed MCP server. 

The server includes both SSE and Streaming support. Gemini CLI supports only SSE, but the standard seems to be going to HTTM Streaming protocol.

We support both HTTP server on port 80, and HTTPS server on port 443. When using HTTPS server on port 443, we also start a certificate management listener on port 80 which will allocate a valid certificate as long as we have a valid DNS pointing to our server. Notice that for the SSL certification allocated we need to allow all source IPs to the port 80 listener, since the request will arrive from the Lets' encrypt servers.


package main

import (
"context"
"encoding/json"
"fmt"
"net/http"

"github.com/modelcontextprotocol/go-sdk/jsonschema"
"github.com/modelcontextprotocol/go-sdk/mcp"
"golang.org/x/crypto/acme/autocert"
)

func main() {
implementation := mcp.Implementation{
Name: "Demo MCP Server",
}
mcpServer := mcp.NewServer(&implementation, nil)

mcpServer.AddTool(toolSchema(), toolExecute)

useSse := false
var mcpHandler http.Handler
if useSse {
mcpHandler = mcp.NewSSEHandler(func(*http.Request) *mcp.Server {
return mcpServer
})
} else {
mcpHandler = mcp.NewStreamableHTTPHandler(func(request *http.Request) *mcp.Server {
return mcpServer
}, nil)
}

useTls := true
if useTls {
certManager := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("my.domain.com"),
Cache: autocert.DirCache("certs"),
}

httpServer := &http.Server{
Addr: "0.0.0.0:443",
Handler: mcpHandler,
TLSConfig: certManager.TLSConfig(),
}

go func() {
err := http.ListenAndServe("0.0.0.0:80", certManager.HTTPHandler(nil))
if err != nil {
panic(err)
}
}()

err := httpServer.ListenAndServeTLS("", "")
if err != nil {
panic(err)
}
} else {
err := http.ListenAndServe("0.0.0.0:80", mcpHandler)
if err != nil {
panic(err)
}
}
}


Now we implement the tool that can both read input parameters and return a string result for the AI agent to send to the LLM.


type inputParameters struct {
Name string `json:"name"`
}

func toolSchema() *mcp.Tool {
return &mcp.Tool{
Name: "greet",
Description: "Say hi from me",
InputSchema: &jsonschema.Schema{
Type: "object",
Required: []string{"name"},
Properties: map[string]*jsonschema.Schema{
"name": {
Type: "string",
},
},
},
}
}

func toolExecute(
_ context.Context,
_ *mcp.ServerSession,
params *mcp.CallToolParamsFor[map[string]any],
) (
*mcp.CallToolResultFor[any],
error,
) {
bytes, err := json.Marshal(params.Arguments)
if err != nil {
panic(err)
}

var input inputParameters
err = json.Unmarshal(bytes, &input)
if err != nil {
panic(err)
}

content := mcp.TextContent{
Text: fmt.Sprintf("Hi %v, Demo MCP is at your service", input.Name),
}

result := mcp.CallToolResultFor[any]{
Content: []mcp.Content{
&content,
},
}

return &result, nil
}



No comments:

Post a Comment