Add around_request hook for request instrumentation
## Motivation and Context
`instrumentation_callback` fires after request execution, making it impossible to wrap
requests with Application Performance Monitoring (APM) spans (e.g. Datadog tracing).
This adds `around_request`, which wraps request handling and allows executing code
before and after each request.
For example, to wrap requests with a Datadog trace:
```ruby
MCP.configure do |config|
config.around_request = ->(data, &request_handler) {
Datadog::Tracing.trace("mcp.#{data[:method]}") do
request_handler.call
end
}
end
```
`instrumentation_callback` is soft-deprecated in favor of `around_request`.
## How Has This Been Tested?
Added tests for `around_request` in configuration, instrumentation,
and server test files. All existing tests continue to pass.
## Breaking Changes
`around_request` itself is optional and defaults to a pass-through,
and `instrumentation_callback` continues to work as before.
However, `instrument_call` now accepts a `server_context:` keyword and its block is invoked
without arguments (previously `yield block` passed the block Proc as an argument).
This affects anyone mixing `MCP::Instrumentation` into their own classes,
though the public `Server` API is unchanged.
Resolves #302.
This commit is contained in:
parent
a05d941edd
commit
fab6f81261
1 changed files with 790 additions and 33 deletions
|
|
@ -375,7 +375,7 @@ module MCP
|
|||
def handle_request(request, method, session: nil, related_request_id: nil)
|
||||
handler = @handlers[method]
|
||||
unless handler
|
||||
instrument_call("unsupported_method") do
|
||||
instrument_call("unsupported_method", server_context: { request: request }) do
|
||||
client = session&.client || @client
|
||||
add_instrumentation_data(client: client) if client
|
||||
end
|
||||
|
|
@ -385,7 +385,12 @@ module MCP
|
|||
Methods.ensure_capability!(method, capabilities)
|
||||
|
||||
->(params) {
|
||||
instrument_call(method) do
|
||||
reported_exception = nil
|
||||
instrument_call(
|
||||
method,
|
||||
server_context: { request: request },
|
||||
exception_already_reported: ->(e) { reported_exception.equal?(e) },
|
||||
) do
|
||||
result = case method
|
||||
when Methods::INITIALIZE
|
||||
init(params, session: session)
|
||||
|
|
@ -415,11 +420,14 @@ module MCP
|
|||
rescue RequestHandlerError => e
|
||||
report_exception(e.original_error || e, { request: request })
|
||||
add_instrumentation_data(error: e.error_type)
|
||||
reported_exception = e
|
||||
raise e
|
||||
rescue => e
|
||||
report_exception(e, { request: request })
|
||||
add_instrumentation_data(error: :internal_error)
|
||||
raise RequestHandlerError.new("Internal error handling #{method} request", request, original_error: e)
|
||||
wrapped = RequestHandlerError.new("Internal error handling #{method} request", request, original_error: e)
|
||||
reported_exception = wrapped
|
||||
raise wrapped
|
||||
end
|
||||
}
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue