mcp: respond with jsonrpc error when MCP request is denied (#5694)

## 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
This commit is contained in:
Denis Mishin 2025-07-08 08:07:26 -07:00 committed by GitHub
parent e179c1553c
commit f5c5326c72
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 657 additions and 24 deletions

View file

@ -174,21 +174,24 @@ func Test_getEvaluatorRequestWithPortInHostHeader(t *testing.T) {
func Test_MCP_TraceAttributes(t *testing.T) {
t.Parallel()
// Test MCP request parsing
mcpBody := `{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"database_query","arguments":{"query":"SELECT * FROM users","limit":10}}}`
req := &envoy_service_auth_v3.CheckRequest{
Attributes: &envoy_service_auth_v3.AttributeContext{
Request: &envoy_service_auth_v3.AttributeContext_Request{
Http: &envoy_service_auth_v3.AttributeContext_HttpRequest{
Method: http.MethodPost,
Headers: map[string]string{
"content-type": "application/json",
},
Body: mcpBody,
},
},
},
}
mcpReq, ok := evaluator.RequestMCPFromCheckRequest(req)
require.True(t, ok, "should successfully parse MCP request")
mcpReq, err := evaluator.RequestMCPFromCheckRequest(req)
require.NoError(t, err, "should successfully parse MCP request")
assert.Equal(t, "tools/call", mcpReq.Method)
require.NotNil(t, mcpReq.ToolCall)
@ -201,21 +204,21 @@ func Test_MCP_TraceAttributes(t *testing.T) {
mcpBodyList := `{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}`
req.Attributes.Request.Http.Body = mcpBodyList
mcpReq, ok = evaluator.RequestMCPFromCheckRequest(req)
require.True(t, ok, "should successfully parse MCP list request")
mcpReq, err = evaluator.RequestMCPFromCheckRequest(req)
require.NoError(t, err, "should successfully parse MCP list request")
assert.Equal(t, "tools/list", mcpReq.Method)
assert.Nil(t, mcpReq.ToolCall)
// Test invalid JSON
req.Attributes.Request.Http.Body = `invalid json`
mcpReq, ok = evaluator.RequestMCPFromCheckRequest(req)
assert.False(t, ok, "should fail to parse invalid JSON")
mcpReq, err = evaluator.RequestMCPFromCheckRequest(req)
assert.Error(t, err, "should fail to parse invalid JSON")
// Test empty body
req.Attributes.Request.Http.Body = ""
mcpReq, ok = evaluator.RequestMCPFromCheckRequest(req)
assert.False(t, ok, "should fail to parse empty body")
mcpReq, err = evaluator.RequestMCPFromCheckRequest(req)
assert.Error(t, err, "should fail to parse empty body")
}
type mockDataBrokerServiceClient struct {