first commit
This commit is contained in:
194
examples/mcps/http-no-ping-server/main.go
Normal file
194
examples/mcps/http-no-ping-server/main.go
Normal file
@@ -0,0 +1,194 @@
|
||||
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)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user