Tencent/WeKnora · error

MCP service is required

Error message

MCP service is required

What it means

ValidateServiceOutboundURLs enforces the SSRF policy on every URL an MCP service may contact. This error is returned when the function is called with a nil *types.MCPService pointer. It is a guard against validating a nonexistent service record.

Source

Thrown at internal/mcp/security.go:17

package mcp

import (
	"fmt"
	"strings"

	"github.com/Tencent/WeKnora/internal/types"
	secutils "github.com/Tencent/WeKnora/internal/utils"
)

// ValidateServiceOutboundURLs validates every URL that the MCP transport or
// OAuth discovery flow may contact. It is intentionally called both at
// persistence boundaries and immediately before client construction so stale
// or imported rows cannot bypass the current SSRF policy.
func ValidateServiceOutboundURLs(service *types.MCPService) error {
	if service == nil {
		return fmt.Errorf("MCP service is required")
	}
	if service.URL != nil {
		serviceURL := strings.TrimSpace(*service.URL)
		if serviceURL != "" {
			if err := secutils.ValidateURLForSSRF(serviceURL); err != nil {
				return fmt.Errorf("MCP service URL failed SSRF validation: %w", err)
			}
		}
	}
	if service.AuthConfig != nil {
		metadataURL := strings.TrimSpace(service.AuthConfig.AuthServerMetadataURL)
		if metadataURL != "" {
			if err := secutils.ValidateURLForSSRF(metadataURL); err != nil {
				return fmt.Errorf("MCP OAuth metadata URL failed SSRF validation: %w", err)
			}
		}
	}
	return nil

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Ensure the MCPService is constructed and non-nil before calling validation (check decode/unmarshal errors from the request body).
  2. Return a 400 Bad Request to clients that submit an empty service payload instead of reaching validation with nil.
  3. If a lookup can miss, check for not-found before validation and return your domain not-found error.
  4. In tests, pass a minimal valid MCPService{} rather than nil unless testing the guard itself.

Example fix

// before
var svc *types.MCPService
_ = json.NewDecoder(r.Body).Decode(&svc)
if err := ValidateServiceOutboundURLs(svc); err != nil { ... }
// after
svc := &types.MCPService{}
if err := json.NewDecoder(r.Body).Decode(svc); err != nil {
    return nil, fmt.Errorf("invalid MCP service payload: %w", err)
}
if err := ValidateServiceOutboundURLs(svc); err != nil { ... }
Defensive patterns

Strategy: validation

Validate before calling

if service == nil {
    return fmt.Errorf("MCP service is required")
}

Type guard

func serviceProvided(s *types.MCPService) bool { return s != nil }

Try / catch

if err := ValidateServiceOutboundURLs(service); err != nil {
    if strings.Contains(err.Error(), "is required") {
        return httpBadRequest("MCP service payload missing")
    }
    return err
}

Prevention

When it happens

Trigger: Passing a nil *types.MCPService to ValidateServiceOutboundURLs — e.g. a lookup returned no row but the code proceeded, or a JSON decode produced a nil pointer.

Common situations: CreateMCPService/UpdateMCPService handlers invoked with a missing/unparsed request body; imported or migrated rows failing to materialize into a service struct; test harness passing nil to check guard behavior.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/43da25ffc2dc3e45. Report an issue: GitHub.