mcp: introduce Requests (#267)
Handlers and client/server methods take a single request argument combining the session and params. This slightly simplifies the signature for handlers, since there is no longer an often-ignored session argument. But more importantly, it opens the door to adding more information in requests, such as auth info and HTTP request headers. For #243.
This commit is contained in:
parent
e097918484
commit
6e03217c83
1 changed files with 517 additions and 380 deletions
|
|
@ -408,15 +408,12 @@ We provide a mechanism to add MCP-level middleware on the both the client and se
|
|||
|
||||
```go
|
||||
// A MethodHandler handles MCP messages.
|
||||
// The params argument is an XXXParams struct pointer, such as *GetPromptParams.
|
||||
// For methods, a MethodHandler must return either an XXResult struct pointer and a nil error, or
|
||||
// nil with a non-nil error.
|
||||
// For notifications, a MethodHandler must return nil, nil.
|
||||
type MethodHandler[S Session] func(
|
||||
ctx context.Context, _ *S, method string, params Params) (result Result, err error)
|
||||
// For methods, exactly one of the return values must be nil.
|
||||
// For notifications, both must be nil.
|
||||
type MethodHandler func(ctx context.Context, method string, req Request) (result Result, err error)
|
||||
|
||||
// Middleware is a function from MethodHandlers to MethodHandlers.
|
||||
type Middleware[S Session] func(MethodHandler[S]) MethodHandler[S]
|
||||
type Middleware func(MethodHandler) MethodHandler
|
||||
|
||||
// AddMiddleware wraps the client/server's current method handler using the provided
|
||||
// middleware. Middleware is applied from right to left, so that the first one
|
||||
|
|
@ -424,17 +421,17 @@ type Middleware[S Session] func(MethodHandler[S]) MethodHandler[S]
|
|||
//
|
||||
// For example, AddMiddleware(m1, m2, m3) augments the server method handler as
|
||||
// m1(m2(m3(handler))).
|
||||
func (c *Client) AddSendingMiddleware(middleware ...Middleware[*ClientSession])
|
||||
func (c *Client) AddReceivingMiddleware(middleware ...Middleware[*ClientSession])
|
||||
func (s *Server) AddSendingMiddleware(middleware ...Middleware[*ServerSession])
|
||||
func (s *Server) AddReceivingMiddleware(middleware ...Middleware[*ServerSession])
|
||||
func (c *Client) AddSendingMiddleware(middleware ...Middleware)
|
||||
func (c *Client) AddReceivingMiddleware(middleware ...Middleware)
|
||||
func (s *Server) AddSendingMiddleware(middleware ...Middleware)
|
||||
func (s *Server) AddReceivingMiddleware(middleware ...Middleware)
|
||||
```
|
||||
|
||||
As an example, this code adds server-side logging:
|
||||
|
||||
```go
|
||||
func withLogging(h mcp.MethodHandler[*mcp.ServerSession]) mcp.MethodHandler[*mcp.ServerSession]{
|
||||
return func(ctx context.Context, s *mcp.ServerSession, method string, params mcp.Params) (res mcp.Result, err error) {
|
||||
func withLogging(h mcp.MethodHandler) mcp.MethodHandler{
|
||||
return func(ctx context.Context, method string, req mcp.Request) (res mcp.Result, err error) {
|
||||
log.Printf("request: %s %v", method, params)
|
||||
defer func() { log.Printf("response: %v, %v", res, err) }()
|
||||
return h(ctx, s , method, params)
|
||||
|
|
@ -597,8 +594,9 @@ type Tool struct {
|
|||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type ToolHandlerFor[In, Out any] func(context.Context, *ServerSession, *CallToolParamsFor[In]) (*CallToolResultFor[Out], error)
|
||||
type ToolHandler = ToolHandlerFor[map[string]any, any]
|
||||
// A ToolHandlerFor handles a call to tools/call with typed arguments and results.
|
||||
type ToolHandlerFor[In, Out any] func(context.Context, *ServerRequest[*CallToolParamsFor[In]]) (*CallToolResultFor[Out], error)
|
||||
|
||||
```
|
||||
|
||||
Add tools to a server with the `AddTool` method or function. The function is generic and infers schemas from the handler
|
||||
|
|
@ -648,8 +646,8 @@ type AddParams struct {
|
|||
Y int `json:"y"`
|
||||
}
|
||||
|
||||
func addHandler(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[AddParams]) (*mcp.CallToolResultFor[int], error) {
|
||||
return &mcp.CallToolResultFor[int]{StructuredContent: params.Arguments.X + params.Arguments.Y}, nil
|
||||
func addHandler(ctx context.Context, req *mcp.ServerRequest[*mcp.CallToolParamsFor[AddParams]]) (*mcp.CallToolResultFor[int], error) {
|
||||
return &mcp.CallToolResultFor[int]{StructuredContent: req.Params.Arguments.X + req.Params.Arguments.Y}, nil
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -665,8 +663,6 @@ Client sessions can call the spec method `ListTools` or an iterator method `Tool
|
|||
|
||||
```go
|
||||
func (cs *ClientSession) CallTool(context.Context, *CallToolParams[json.RawMessage]) (*CallToolResult, error)
|
||||
|
||||
func CallTool[TArgs any](context.Context, *ClientSession, *CallToolParams[TArgs]) (*CallToolResult, error)
|
||||
```
|
||||
|
||||
**Differences from mcp-go**: We provide a full JSON Schema implementation for validating tool input schemas against incoming arguments. The `jsonschema.Schema` type provides exported features for all keywords in the JSON Schema draft2020-12 spec. Tool definers can use it to construct any schema they want. The `jsonschema.For[T]` function can infer a schema from a Go struct. These combined features eliminate the need for variadic arguments to construct tool schemas.
|
||||
|
|
@ -752,10 +748,10 @@ If a server author wants to support resource subscriptions, they must provide ha
|
|||
```go
|
||||
type ServerOptions struct {
|
||||
...
|
||||
// Function called when a client session subscribes to a resource.
|
||||
SubscribeHandler func(context.Context, ss *ServerSession, *SubscribeParams) error
|
||||
// Function called when a client session unsubscribes from a resource.
|
||||
UnsubscribeHandler func(context.Context, ss *ServerSession, *UnsubscribeParams) error
|
||||
// Function called when a client session subscribes to a resource.
|
||||
SubscribeHandler func(context.Context, *ServerRequest[*SubscribeParams]) error
|
||||
// Function called when a client session unsubscribes from a resource.
|
||||
UnsubscribeHandler func(context.Context, *ServerRequest[*UnsubscribeParams]) error
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -774,10 +770,10 @@ When a list of tools, prompts or resources changes as the result of an AddXXX or
|
|||
```go
|
||||
type ClientOptions struct {
|
||||
...
|
||||
ToolListChangedHandler func(context.Context, *ClientSession, *ToolListChangedParams)
|
||||
PromptListChangedHandler func(context.Context, *ClientSession, *PromptListChangedParams)
|
||||
ToolListChangedHandler func(context.Context, *ClientRequest[*ToolListChangedParams])
|
||||
PromptListChangedHandler func(context.Context, *ClientRequest[*PromptListChangedParams])
|
||||
// For both resources and resource templates.
|
||||
ResourceListChangedHandler func(context.Context, *ClientSession, *ResourceListChangedParams)
|
||||
ResourceListChangedHandler func(context.Context, *ClientRequest[*ResourceListChangedParams])
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -788,13 +784,10 @@ type ClientOptions struct {
|
|||
Clients call the spec method `Complete` to request completions. If a server installs a `CompletionHandler`, it will be called when the client sends a completion request.
|
||||
|
||||
```go
|
||||
// A CompletionHandler handles a call to completion/complete.
|
||||
type CompletionHandler func(context.Context, *ServerSession, *CompleteParams) (*CompleteResult, error)
|
||||
|
||||
type ServerOptions struct {
|
||||
...
|
||||
// If non-nil, called when a client sends a completion request.
|
||||
CompletionHandler CompletionHandler
|
||||
CompletionHandler func(context.Context, *ServerRequest[*CompleteParams]) (*CompleteResult, error)
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue