mirror of
https://github.com/pomerium/pomerium.git
synced 2025-07-31 23:41:09 +02:00
## Summary Individual MCP method calls may be denied (i.e. via `mcp_tool` criterion) and Pomerium has to respond with MCP protocol error, which is JSON-RPC error message, rather then with HTTP level error which seems to break some MCP clients. ## Related issues Fix https://linear.app/pomerium/issue/ENG-2521/pomerium-does-not-return-an-mcp-error-when-a-tool-call-is-unauthorized ## User Explanation <!-- How would you explain this change to the user? If this change doesn't create any user-facing changes, you can leave this blank. If filled out, add the `docs` label --> ## Checklist - [x] reference any related issues - [x] updated unit tests - [x] add appropriate label (`enhancement`, `bug`, `breaking`, `dependencies`, `ci`) - [x] ready for review
141 lines
3.2 KiB
Go
141 lines
3.2 KiB
Go
package jsonrpc
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
func ParseRequest(data []byte) (*Request, error) {
|
|
decoder := json.NewDecoder(bytes.NewReader(data))
|
|
decoder.UseNumber()
|
|
|
|
var req Request
|
|
if err := decoder.Decode(&req); err != nil {
|
|
return nil, fmt.Errorf("failed to parse JSON-RPC request: %w", err)
|
|
}
|
|
|
|
if req.JSONRPC != Version {
|
|
return nil, fmt.Errorf("invalid JSON-RPC version: %s, expected %s", req.JSONRPC, Version)
|
|
}
|
|
|
|
if req.Method == "" {
|
|
return nil, fmt.Errorf("missing method in JSON-RPC request")
|
|
}
|
|
|
|
return &req, nil
|
|
}
|
|
|
|
func NewErrorResponse(code ErrorCode, id ID, message string, data any) Response {
|
|
return Response{
|
|
JSONRPC: Version,
|
|
ID: id,
|
|
Error: &ErrorResponse{
|
|
Code: code,
|
|
Message: message,
|
|
Data: data,
|
|
},
|
|
}
|
|
}
|
|
|
|
// ID
|
|
// An identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification.
|
|
type ID struct {
|
|
value any
|
|
}
|
|
|
|
func (id ID) IsZero() bool {
|
|
return id.value == nil
|
|
}
|
|
|
|
func (id ID) MarshalJSON() ([]byte, error) {
|
|
if id.value == nil {
|
|
return json.Marshal(nil)
|
|
}
|
|
return json.Marshal(id.value)
|
|
}
|
|
|
|
func (id *ID) UnmarshalJSON(data []byte) error {
|
|
if data == nil || string(data) == "null" {
|
|
id.value = nil
|
|
return nil
|
|
}
|
|
|
|
decoder := json.NewDecoder(bytes.NewReader(data))
|
|
decoder.UseNumber()
|
|
|
|
var v any
|
|
if err := decoder.Decode(&v); err != nil {
|
|
return err
|
|
}
|
|
|
|
switch val := v.(type) {
|
|
case string:
|
|
id.value = val
|
|
case json.Number:
|
|
id.value = val
|
|
case nil:
|
|
id.value = nil
|
|
default:
|
|
return fmt.Errorf("field 'id' must be a string, number, or null, got %T", v)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func NewNumberID(value int) ID {
|
|
return ID{value: json.Number(fmt.Sprintf("%d", value))}
|
|
}
|
|
|
|
func NewStringID(value string) ID {
|
|
return ID{value: value}
|
|
}
|
|
|
|
func NewJSONNumberID(value json.Number) ID {
|
|
return ID{value: value}
|
|
}
|
|
|
|
type Request struct {
|
|
JSONRPC string `json:"jsonrpc"`
|
|
ID ID `json:"id"`
|
|
Method string `json:"method"`
|
|
Params json.RawMessage `json:"params"`
|
|
}
|
|
|
|
type Response struct {
|
|
JSONRPC string `json:"jsonrpc"`
|
|
ID ID `json:"id,omitempty"`
|
|
Error *ErrorResponse `json:"error,omitempty"`
|
|
}
|
|
|
|
type ErrorResponse struct {
|
|
Code ErrorCode `json:"code"`
|
|
Message string `json:"message"`
|
|
Data any `json:"data,omitempty"`
|
|
}
|
|
|
|
type ErrorCode int
|
|
|
|
// JSON-RPC error codes as defined in the specification
|
|
// https://www.jsonrpc.org/specification#error_object
|
|
const (
|
|
// ErrorCodeParseError - Invalid JSON was received by the server.
|
|
// An error occurred on the server while parsing the JSON text.
|
|
ErrorCodeParseError = ErrorCode(-32700)
|
|
|
|
// ErrorCodeInvalidRequest - The JSON sent is not a valid Request object.
|
|
ErrorCodeInvalidRequest = ErrorCode(-32600)
|
|
|
|
// ErrorCodeMethodNotFound - The method does not exist / is not available.
|
|
ErrorCodeMethodNotFound = ErrorCode(-32601)
|
|
|
|
// ErrorCodeInvalidParams - Invalid method parameter(s).
|
|
ErrorCodeInvalidParams = ErrorCode(-32602)
|
|
|
|
// ErrorCodeInternalError - Internal JSON-RPC error.
|
|
ErrorCodeInternalError = ErrorCode(-32603)
|
|
|
|
// Server error range - Reserved for implementation-defined server-errors.
|
|
// Error codes from -32000 to -32099 are reserved for server errors.
|
|
)
|
|
|
|
const Version = "2.0"
|