Files
bifrost/examples/mcps/http-no-ping-server/main.go
Beyhan Oğur 880f412e2c first commit
2026-04-26 21:52:23 +03:00

195 lines
5.5 KiB
Go

package main
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strings"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
// NoPingMCPServer is an HTTP MCP server that intentionally does not support ping.
// This demonstrates how to configure servers with is_ping_available=false in Bifrost
// when your MCP server implementation doesn't support the optional ping method.
func main() {
// Create MCP server
mcpServer := server.NewMCPServer(
"http-no-ping-server",
"1.0.0",
)
// Define tools using the proper NewTool API
echoTool := mcp.NewTool(
"echo",
mcp.WithDescription("Echo back the input message"),
mcp.WithString("message", mcp.Required(), mcp.Description("Message to echo")),
)
addTool := mcp.NewTool(
"add",
mcp.WithDescription("Add two numbers"),
mcp.WithNumber("a", mcp.Required(), mcp.Description("First number")),
mcp.WithNumber("b", mcp.Required(), mcp.Description("Second number")),
)
greetTool := mcp.NewTool(
"greet",
mcp.WithDescription("Greet someone by name"),
mcp.WithString("name", mcp.Required(), mcp.Description("Name to greet")),
)
// Register tool handlers
mcpServer.AddTool(echoTool, echoHandler)
mcpServer.AddTool(addTool, addHandler)
mcpServer.AddTool(greetTool, greetHandler)
// Create HTTP server using StreamableHTTP transport
httpServer := server.NewStreamableHTTPServer(mcpServer)
port := 3001
addr := fmt.Sprintf("localhost:%d", port)
log.Printf("MCP server listening on http://%s/", addr)
log.Printf("Note: This server does NOT support ping. Use is_ping_available=false in Bifrost config.")
log.Printf("\nExample Bifrost config:")
log.Printf(`
{
"name": "http_no_ping_server",
"connection_type": "http",
"connection_string": "http://%s/",
"is_ping_available": false,
"tools_to_execute": ["*"]
}
`, addr)
// Wrap the HTTP server with middleware that rejects ping requests
wrappedHandler := noPingMiddleware(httpServer)
if err := http.ListenAndServe(addr, wrappedHandler); err != nil {
log.Fatalf("Server error: %v", err)
}
}
// echoHandler handles the echo tool
func echoHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// Extract arguments as JSON
var args struct {
Message string `json:"message"`
}
// Parse the arguments
argBytes, err := json.Marshal(request.Params.Arguments)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Failed to parse arguments: %v", err)), nil
}
if err := json.Unmarshal(argBytes, &args); err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Invalid arguments: %v", err)), nil
}
result := fmt.Sprintf("Echo: %s", args.Message)
return mcp.NewToolResultText(result), nil
}
// addHandler handles the add tool
func addHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// Extract arguments as JSON
var args struct {
A float64 `json:"a"`
B float64 `json:"b"`
}
// Parse the arguments
argBytes, err := json.Marshal(request.Params.Arguments)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Failed to parse arguments: %v", err)), nil
}
if err := json.Unmarshal(argBytes, &args); err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Invalid arguments: %v", err)), nil
}
result := args.A + args.B
return mcp.NewToolResultText(fmt.Sprintf("%v + %v = %v", args.A, args.B, result)), nil
}
// greetHandler handles the greet tool
func greetHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// Extract arguments as JSON
var args struct {
Name string `json:"name"`
}
// Parse the arguments
argBytes, err := json.Marshal(request.Params.Arguments)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Failed to parse arguments: %v", err)), nil
}
if err := json.Unmarshal(argBytes, &args); err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Invalid arguments: %v", err)), nil
}
result := fmt.Sprintf("Hello, %s! Welcome to the MCP server.", args.Name)
return mcp.NewToolResultText(result), nil
}
// noPingMiddleware is HTTP middleware that rejects ping requests
// This allows us to demonstrate a server that doesn't support ping
func noPingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Only intercept POST requests (MCP messages)
if r.Method != http.MethodPost {
next.ServeHTTP(w, r)
return
}
// Read the request body
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read request body", http.StatusBadRequest)
return
}
// Parse the JSON-RPC request to check if it's a ping request
var jsonRequest map[string]interface{}
if err := json.Unmarshal(body, &jsonRequest); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
// Check if this is a ping request
if method, ok := jsonRequest["method"].(string); ok && method == "ping" {
// Reject ping requests with a method not found error
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
var id interface{}
if idVal, ok := jsonRequest["id"]; ok {
id = idVal
}
errorResponse := map[string]interface{}{
"jsonrpc": "2.0",
"error": map[string]interface{}{
"code": -32601,
"message": "Method not found: ping is not supported by this server",
},
"id": id,
}
json.NewEncoder(w).Encode(errorResponse)
return
}
// For non-ping requests, restore the body and pass through to the next handler
r.Body = io.NopCloser(strings.NewReader(string(body)))
next.ServeHTTP(w, r)
})
}