chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
ghcontext "github.com/github/github-mcp-server/pkg/context"
|
||||
)
|
||||
|
||||
// mcpJSONRPCRequest represents the structure of an MCP JSON-RPC request.
|
||||
// We only parse the fields needed for routing and optimization.
|
||||
type mcpJSONRPCRequest struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
Params struct {
|
||||
// For tools/call
|
||||
Name string `json:"name,omitempty"`
|
||||
Arguments json.RawMessage `json:"arguments,omitempty"`
|
||||
// For prompts/get
|
||||
// Name is shared with tools/call
|
||||
// For resources/read
|
||||
URI string `json:"uri,omitempty"`
|
||||
} `json:"params"`
|
||||
}
|
||||
|
||||
// WithMCPParse creates a middleware that parses MCP JSON-RPC requests early in the
|
||||
// request lifecycle and stores the parsed information in the request context.
|
||||
// This enables:
|
||||
// - Registry filtering via ForMCPRequest (only register needed tools/resources/prompts)
|
||||
// - Avoiding duplicate JSON parsing in downstream middlewares
|
||||
// - Access to owner/repo for secret-scanning middleware
|
||||
//
|
||||
// The middleware reads the request body, parses it, restores the body for downstream
|
||||
// handlers, and stores the parsed MCPMethodInfo in the request context.
|
||||
func WithMCPParse() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
// Skip health check endpoints
|
||||
if r.URL.Path == "/_ping" {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Only parse POST requests (MCP uses JSON-RPC over POST)
|
||||
if r.Method != http.MethodPost {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Read the request body
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
// Log but continue - don't block requests on parse errors
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Restore the body for downstream handlers
|
||||
r.Body = io.NopCloser(bytes.NewReader(body))
|
||||
|
||||
// Skip empty bodies
|
||||
if len(body) == 0 {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the JSON-RPC request
|
||||
var mcpReq mcpJSONRPCRequest
|
||||
err = json.Unmarshal(body, &mcpReq)
|
||||
if err != nil {
|
||||
// Log but continue - could be a non-MCP request or malformed JSON
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Skip if not a valid JSON-RPC 2.0 request
|
||||
if mcpReq.JSONRPC != "2.0" || mcpReq.Method == "" {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Build the MCPMethodInfo
|
||||
methodInfo := &ghcontext.MCPMethodInfo{
|
||||
Method: mcpReq.Method,
|
||||
}
|
||||
|
||||
// Extract item name based on method type
|
||||
|
||||
switch mcpReq.Method {
|
||||
case "tools/call":
|
||||
methodInfo.ItemName = mcpReq.Params.Name
|
||||
// Parse arguments if present
|
||||
if len(mcpReq.Params.Arguments) > 0 {
|
||||
var args map[string]any
|
||||
err := json.Unmarshal(mcpReq.Params.Arguments, &args)
|
||||
if err == nil {
|
||||
methodInfo.Arguments = args
|
||||
// Extract owner and repo if present
|
||||
if owner, ok := args["owner"].(string); ok {
|
||||
methodInfo.Owner = owner
|
||||
}
|
||||
if repo, ok := args["repo"].(string); ok {
|
||||
methodInfo.Repo = repo
|
||||
}
|
||||
}
|
||||
}
|
||||
case "prompts/get":
|
||||
methodInfo.ItemName = mcpReq.Params.Name
|
||||
case "resources/read":
|
||||
methodInfo.ItemName = mcpReq.Params.URI
|
||||
default:
|
||||
// Whatever
|
||||
}
|
||||
|
||||
// Store the parsed info in context
|
||||
ctx = ghcontext.WithMCPMethodInfo(ctx, methodInfo)
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user