Compare commits

..

3 Commits
err ... err-api

Author SHA1 Message Date
duwenxin
9e884f52ea resolve comments 2026-02-02 18:38:04 -05:00
duwenxin
8a0f179f15 refactor error return 2026-02-02 18:38:04 -05:00
duwenxin
87ae5ae816 refactor api handler 2026-02-02 18:38:04 -05:00
10 changed files with 235 additions and 132 deletions

View File

@@ -19,7 +19,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware" "github.com/go-chi/chi/v5/middleware"
@@ -235,8 +234,10 @@ func toolInvokeHandler(s *Server, w http.ResponseWriter, r *http.Request) {
params, err := parameters.ParseParams(tool.GetParameters(), data, claimsFromAuth) params, err := parameters.ParseParams(tool.GetParameters(), data, claimsFromAuth)
if err != nil { if err != nil {
// If auth error, return 401 // If auth error, return 401
if errors.Is(err, util.ErrUnauthorized) { errMsg := fmt.Sprintf("error parsing authenticated parameters from ID token: %w", err)
s.logger.DebugContext(ctx, fmt.Sprintf("error parsing authenticated parameters from ID token: %s", err)) var clientServerErr *util.ClientServerError
if errors.As(err, &clientServerErr) && clientServerErr.Code == http.StatusUnauthorized {
s.logger.DebugContext(ctx, errMsg)
_ = render.Render(w, r, newErrResponse(err, http.StatusUnauthorized)) _ = render.Render(w, r, newErrResponse(err, http.StatusUnauthorized))
return return
} }
@@ -259,34 +260,49 @@ func toolInvokeHandler(s *Server, w http.ResponseWriter, r *http.Request) {
// Determine what error to return to the users. // Determine what error to return to the users.
if err != nil { if err != nil {
errStr := err.Error() var tbErr util.ToolboxError
var statusCode int
// Upstream API auth error propagation if errors.As(err, &tbErr) {
switch { switch tbErr.Category() {
case strings.Contains(errStr, "Error 401"): case util.CategoryAgent:
statusCode = http.StatusUnauthorized // Agent Errors -> 200 OK
case strings.Contains(errStr, "Error 403"): s.logger.DebugContext(ctx, fmt.Sprintf("Tool invocation agent error: %v", err))
statusCode = http.StatusForbidden _ = render.Render(w, r, newErrResponse(err, http.StatusOK))
} return
if statusCode == http.StatusUnauthorized || statusCode == http.StatusForbidden { case util.CategoryServer:
if clientAuth { // Server Errors -> Check the specific code inside
// Propagate the original 401/403 error. var clientServerErr *util.ClientServerError
s.logger.DebugContext(ctx, fmt.Sprintf("error invoking tool. Client credentials lack authorization to the source: %v", err)) statusCode := http.StatusInternalServerError // Default to 500
if errors.As(err, &clientServerErr) {
if clientServerErr.Code != 0 {
statusCode = clientServerErr.Code
}
}
// Process auth error
if statusCode == http.StatusUnauthorized || statusCode == http.StatusForbidden {
if clientAuth {
// Token error, pass through 401/403
s.logger.DebugContext(ctx, fmt.Sprintf("Client credentials lack authorization: %v", err))
_ = render.Render(w, r, newErrResponse(err, statusCode))
return
}
// ADC/Config error, return 500
statusCode = http.StatusInternalServerError
}
s.logger.ErrorContext(ctx, fmt.Sprintf("Tool invocation server error: %v", err))
_ = render.Render(w, r, newErrResponse(err, statusCode)) _ = render.Render(w, r, newErrResponse(err, statusCode))
return return
} }
// ADC lacking permission or credentials configuration error. } else {
internalErr := fmt.Errorf("unexpected auth error occured during Tool invocation: %w", err) // Unknown error -> 500
s.logger.ErrorContext(ctx, internalErr.Error()) s.logger.ErrorContext(ctx, fmt.Sprintf("Tool invocation unknown error: %v", err))
_ = render.Render(w, r, newErrResponse(internalErr, http.StatusInternalServerError)) _ = render.Render(w, r, newErrResponse(err, http.StatusInternalServerError))
return return
} }
err = fmt.Errorf("error while invoking tool: %w", err)
s.logger.DebugContext(ctx, err.Error())
_ = render.Render(w, r, newErrResponse(err, http.StatusBadRequest))
return
} }
resMarshal, err := json.Marshal(res) resMarshal, err := json.Marshal(res)

View File

@@ -23,7 +23,6 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"strings"
"sync" "sync"
"time" "time"
@@ -444,15 +443,17 @@ func httpHandler(s *Server, w http.ResponseWriter, r *http.Request) {
code := rpcResponse.Error.Code code := rpcResponse.Error.Code
switch code { switch code {
case jsonrpc.INTERNAL_ERROR: case jsonrpc.INTERNAL_ERROR:
// Map Internal RPC Error (-32603) to HTTP 500
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
case jsonrpc.INVALID_REQUEST: case jsonrpc.INVALID_REQUEST:
errStr := err.Error() var clientServerErr *util.ClientServerError
if errors.Is(err, util.ErrUnauthorized) { if errors.As(err, &clientServerErr) {
w.WriteHeader(http.StatusUnauthorized) switch clientServerErr.Code {
} else if strings.Contains(errStr, "Error 401") { case http.StatusUnauthorized:
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusUnauthorized)
} else if strings.Contains(errStr, "Error 403") { case http.StatusForbidden:
w.WriteHeader(http.StatusForbidden) w.WriteHeader(http.StatusForbidden)
}
} }
} }
} }

View File

@@ -21,7 +21,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/googleapis/genai-toolbox/internal/prompts" "github.com/googleapis/genai-toolbox/internal/prompts"
"github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc" "github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc"
@@ -124,7 +123,11 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
} }
if clientAuth { if clientAuth {
if accessToken == "" { if accessToken == "" {
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.ErrUnauthorized return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.NewClientServerError(
"missing access token in the 'Authorization' header",
http.StatusUnauthorized,
nil,
)
} }
} }
@@ -172,7 +175,11 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// Check if any of the specified auth services is verified // Check if any of the specified auth services is verified
isAuthorized := tool.Authorized(verifiedAuthServices) isAuthorized := tool.Authorized(verifiedAuthServices)
if !isAuthorized { if !isAuthorized {
err = fmt.Errorf("unauthorized Tool call: Please make sure your specify correct auth headers: %w", util.ErrUnauthorized) err = util.NewClientServerError(
"unauthorized Tool call: Please make sure you specify correct auth headers",
http.StatusUnauthorized,
nil,
)
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
} }
logger.DebugContext(ctx, "tool invocation authorized") logger.DebugContext(ctx, "tool invocation authorized")
@@ -194,30 +201,44 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// run tool invocation and generate response. // run tool invocation and generate response.
results, err := tool.Invoke(ctx, resourceMgr, params, accessToken) results, err := tool.Invoke(ctx, resourceMgr, params, accessToken)
if err != nil { if err != nil {
errStr := err.Error() var tbErr util.ToolboxError
// Missing authService tokens.
if errors.Is(err, util.ErrUnauthorized) { if errors.As(err, &tbErr) {
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err switch tbErr.Category() {
} case util.CategoryAgent:
// Upstream auth error // MCP - Tool execution error
if strings.Contains(errStr, "Error 401") || strings.Contains(errStr, "Error 403") { // Return SUCCESS but with IsError: true
if clientAuth { text := TextContent{
// Error with client credentials should pass down to the client Type: "text",
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err Text: err.Error(),
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
case util.CategoryServer:
// MCP Spec - Protocol error
// Return JSON-RPC ERROR
var clientServerErr *util.ClientServerError
rpcCode := jsonrpc.INTERNAL_ERROR // Default to Internal Error (-32603)
if errors.As(err, &clientServerErr) {
if clientServerErr.Code == http.StatusUnauthorized || clientServerErr.Code == http.StatusForbidden {
if clientAuth {
rpcCode = jsonrpc.INVALID_REQUEST
} else {
rpcCode = jsonrpc.INTERNAL_ERROR
}
}
}
return jsonrpc.NewError(id, rpcCode, err.Error(), nil), err
} }
// Auth error with ADC should raise internal 500 error } else {
// Unknown error -> 500
return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err
} }
text := TextContent{
Type: "text",
Text: err.Error(),
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
} }
content := make([]TextContent, 0) content := make([]TextContent, 0)

View File

@@ -21,7 +21,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/googleapis/genai-toolbox/internal/prompts" "github.com/googleapis/genai-toolbox/internal/prompts"
"github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc" "github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc"
@@ -124,7 +123,11 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
} }
if clientAuth { if clientAuth {
if accessToken == "" { if accessToken == "" {
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.ErrUnauthorized return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.NewClientServerError(
"missing access token in the 'Authorization' header",
http.StatusUnauthorized,
nil,
)
} }
} }
@@ -172,7 +175,11 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// Check if any of the specified auth services is verified // Check if any of the specified auth services is verified
isAuthorized := tool.Authorized(verifiedAuthServices) isAuthorized := tool.Authorized(verifiedAuthServices)
if !isAuthorized { if !isAuthorized {
err = fmt.Errorf("unauthorized Tool call: Please make sure your specify correct auth headers: %w", util.ErrUnauthorized) err = util.NewClientServerError(
"unauthorized Tool call: Please make sure you specify correct auth headers",
http.StatusUnauthorized,
nil,
)
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
} }
logger.DebugContext(ctx, "tool invocation authorized") logger.DebugContext(ctx, "tool invocation authorized")
@@ -194,31 +201,45 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// run tool invocation and generate response. // run tool invocation and generate response.
results, err := tool.Invoke(ctx, resourceMgr, params, accessToken) results, err := tool.Invoke(ctx, resourceMgr, params, accessToken)
if err != nil { if err != nil {
errStr := err.Error() var tbErr util.ToolboxError
// Missing authService tokens.
if errors.Is(err, util.ErrUnauthorized) { if errors.As(err, &tbErr) {
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err switch tbErr.Category() {
} case util.CategoryAgent:
// Upstream auth error // MCP - Tool execution error
if strings.Contains(errStr, "Error 401") || strings.Contains(errStr, "Error 403") { // Return SUCCESS but with IsError: true
if clientAuth { text := TextContent{
// Error with client credentials should pass down to the client Type: "text",
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err Text: err.Error(),
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
case util.CategoryServer:
// MCP Spec - Protocol error
// Return JSON-RPC ERROR
var clientServerErr *util.ClientServerError
rpcCode := jsonrpc.INTERNAL_ERROR // Default to Internal Error (-32603)
if errors.As(err, &clientServerErr) {
if clientServerErr.Code == http.StatusUnauthorized || clientServerErr.Code == http.StatusForbidden {
if clientAuth {
rpcCode = jsonrpc.INVALID_REQUEST
} else {
rpcCode = jsonrpc.INTERNAL_ERROR
}
}
}
return jsonrpc.NewError(id, rpcCode, err.Error(), nil), err
} }
// Auth error with ADC should raise internal 500 error } else {
// Unknown error -> 500
return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err
} }
text := TextContent{
Type: "text",
Text: err.Error(),
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
} }
content := make([]TextContent, 0) content := make([]TextContent, 0)
sliceRes, ok := results.([]any) sliceRes, ok := results.([]any)

View File

@@ -21,7 +21,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/googleapis/genai-toolbox/internal/prompts" "github.com/googleapis/genai-toolbox/internal/prompts"
"github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc" "github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc"
@@ -117,7 +116,12 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
} }
if clientAuth { if clientAuth {
if accessToken == "" { if accessToken == "" {
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.ErrUnauthorized errMsg := "missing access token in the 'Authorization' header"
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, errMsg, nil), util.NewClientServerError(
errMsg,
http.StatusUnauthorized,
nil,
)
} }
} }
@@ -165,7 +169,11 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// Check if any of the specified auth services is verified // Check if any of the specified auth services is verified
isAuthorized := tool.Authorized(verifiedAuthServices) isAuthorized := tool.Authorized(verifiedAuthServices)
if !isAuthorized { if !isAuthorized {
err = fmt.Errorf("unauthorized Tool call: Please make sure your specify correct auth headers: %w", util.ErrUnauthorized) err = util.NewClientServerError(
"unauthorized Tool call: Please make sure you specify correct auth headers",
http.StatusUnauthorized,
nil,
)
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
} }
logger.DebugContext(ctx, "tool invocation authorized") logger.DebugContext(ctx, "tool invocation authorized")
@@ -187,29 +195,44 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// run tool invocation and generate response. // run tool invocation and generate response.
results, err := tool.Invoke(ctx, resourceMgr, params, accessToken) results, err := tool.Invoke(ctx, resourceMgr, params, accessToken)
if err != nil { if err != nil {
errStr := err.Error() var tbErr util.ToolboxError
// Missing authService tokens.
if errors.Is(err, util.ErrUnauthorized) { if errors.As(err, &tbErr) {
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err switch tbErr.Category() {
} case util.CategoryAgent:
// Upstream auth error // MCP - Tool execution error
if strings.Contains(errStr, "Error 401") || strings.Contains(errStr, "Error 403") { // Return SUCCESS but with IsError: true
if clientAuth { text := TextContent{
// Error with client credentials should pass down to the client Type: "text",
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err Text: err.Error(),
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
case util.CategoryServer:
// MCP Spec - Protocol error
// Return JSON-RPC ERROR
var clientServerErr *util.ClientServerError
rpcCode := jsonrpc.INTERNAL_ERROR // Default to Internal Error (-32603)
if errors.As(err, &clientServerErr) {
if clientServerErr.Code == http.StatusUnauthorized || clientServerErr.Code == http.StatusForbidden {
if clientAuth {
rpcCode = jsonrpc.INVALID_REQUEST
} else {
rpcCode = jsonrpc.INTERNAL_ERROR
}
}
}
return jsonrpc.NewError(id, rpcCode, err.Error(), nil), err
} }
// Auth error with ADC should raise internal 500 error } else {
// Unknown error -> 500
return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err
} }
text := TextContent{
Type: "text",
Text: err.Error(),
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
} }
content := make([]TextContent, 0) content := make([]TextContent, 0)

View File

@@ -21,7 +21,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/googleapis/genai-toolbox/internal/prompts" "github.com/googleapis/genai-toolbox/internal/prompts"
"github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc" "github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc"
@@ -117,7 +116,11 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
} }
if clientAuth { if clientAuth {
if accessToken == "" { if accessToken == "" {
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.ErrUnauthorized return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.NewClientServerError(
"missing access token in the 'Authorization' header",
http.StatusUnauthorized,
nil,
)
} }
} }
@@ -165,7 +168,11 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// Check if any of the specified auth services is verified // Check if any of the specified auth services is verified
isAuthorized := tool.Authorized(verifiedAuthServices) isAuthorized := tool.Authorized(verifiedAuthServices)
if !isAuthorized { if !isAuthorized {
err = fmt.Errorf("unauthorized Tool call: Please make sure your specify correct auth headers: %w", util.ErrUnauthorized) err = util.NewClientServerError(
"unauthorized Tool call: Please make sure you specify correct auth headers",
http.StatusUnauthorized,
nil,
)
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
} }
logger.DebugContext(ctx, "tool invocation authorized") logger.DebugContext(ctx, "tool invocation authorized")
@@ -187,29 +194,44 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// run tool invocation and generate response. // run tool invocation and generate response.
results, err := tool.Invoke(ctx, resourceMgr, params, accessToken) results, err := tool.Invoke(ctx, resourceMgr, params, accessToken)
if err != nil { if err != nil {
errStr := err.Error() var tbErr util.ToolboxError
// Missing authService tokens.
if errors.Is(err, util.ErrUnauthorized) { if errors.As(err, &tbErr) {
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err switch tbErr.Category() {
} case util.CategoryAgent:
// Upstream auth error // MCP - Tool execution error
if strings.Contains(errStr, "Error 401") || strings.Contains(errStr, "Error 403") { // Return SUCCESS but with IsError: true
if clientAuth { text := TextContent{
// Error with client credentials should pass down to the client Type: "text",
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err Text: err.Error(),
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
case util.CategoryServer:
// MCP Spec - Protocol error
// Return JSON-RPC ERROR
var clientServerErr *util.ClientServerError
rpcCode := jsonrpc.INTERNAL_ERROR // Default to Internal Error (-32603)
if errors.As(err, &clientServerErr) {
if clientServerErr.Code == http.StatusUnauthorized || clientServerErr.Code == http.StatusForbidden {
if clientAuth {
rpcCode = jsonrpc.INVALID_REQUEST
} else {
rpcCode = jsonrpc.INTERNAL_ERROR
}
}
}
return jsonrpc.NewError(id, rpcCode, err.Error(), nil), err
} }
// Auth error with ADC should raise internal 500 error } else {
// Unknown error -> 500
return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err
} }
text := TextContent{
Type: "text",
Text: err.Error(),
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
} }
content := make([]TextContent, 0) content := make([]TextContent, 0)

View File

@@ -184,7 +184,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
// Use client-side access token // Use client-side access token
if accessToken == "" { if accessToken == "" {
return nil, fmt.Errorf("tool is configured for client OAuth but no token was provided in the request header: %w", util.ErrUnauthorized) return nil, util.NewClientServerError("tool is configured for client OAuth but no token was provided in the request header", http.StatusUnauthorized, nil)
} }
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {

View File

@@ -17,6 +17,7 @@ package tools
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"slices" "slices"
"strings" "strings"
@@ -80,7 +81,7 @@ type AccessToken string
func (token AccessToken) ParseBearerToken() (string, error) { func (token AccessToken) ParseBearerToken() (string, error) {
headerParts := strings.Split(string(token), " ") headerParts := strings.Split(string(token), " ")
if len(headerParts) != 2 || strings.ToLower(headerParts[0]) != "bearer" { if len(headerParts) != 2 || strings.ToLower(headerParts[0]) != "bearer" {
return "", fmt.Errorf("authorization header must be in the format 'Bearer <token>': %w", util.ErrUnauthorized) return "", util.NewClientServerError("authorization header must be in the format 'Bearer <token>'", http.StatusUnauthorized, nil)
} }
return headerParts[1], nil return headerParts[1], nil
} }

View File

@@ -19,6 +19,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http"
"reflect" "reflect"
"regexp" "regexp"
"slices" "slices"
@@ -118,7 +119,7 @@ func parseFromAuthService(paramAuthServices []ParamAuthService, claimsMap map[st
} }
return v, nil return v, nil
} }
return nil, fmt.Errorf("missing or invalid authentication header: %w", util.ErrUnauthorized) return nil, util.NewClientServerError("missing or invalid authentication header", http.StatusUnauthorized, nil)
} }
// CheckParamRequired checks if a parameter is required based on the required and default field. // CheckParamRequired checks if a parameter is required based on the required and default field.

View File

@@ -17,7 +17,6 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@@ -188,5 +187,3 @@ func InstrumentationFromContext(ctx context.Context) (*telemetry.Instrumentation
} }
return nil, fmt.Errorf("unable to retrieve instrumentation") return nil, fmt.Errorf("unable to retrieve instrumentation")
} }
var ErrUnauthorized = errors.New("unauthorized")