createOAuthMiddleware returns receiving middleware that authorizes the session lazily, on the first tool call. Authorization is deferred until here (rather than at startup) because the prompts depend on an initialized session whose elicitation capabilities are known. When a token is already availab
(mgr oauthAuthenticator, logger *slog.Logger)
| 103 | // arrives and then the call proceeds; the last-resort channel returns the |
| 104 | // instruction to the user as a tool result and asks them to retry. |
| 105 | func createOAuthMiddleware(mgr oauthAuthenticator, logger *slog.Logger) func(next mcp.MethodHandler) mcp.MethodHandler { |
| 106 | return func(next mcp.MethodHandler) mcp.MethodHandler { |
| 107 | return func(ctx context.Context, method string, request mcp.Request) (mcp.Result, error) { |
| 108 | if method != "tools/call" || mgr.HasToken() { |
| 109 | return next(ctx, method, request) |
| 110 | } |
| 111 | |
| 112 | callReq, ok := request.(*mcp.CallToolRequest) |
| 113 | if !ok { |
| 114 | return next(ctx, method, request) |
| 115 | } |
| 116 | |
| 117 | outcome, err := mgr.Authenticate(ctx, &sessionPrompter{session: callReq.Session}) |
| 118 | if err != nil { |
| 119 | return nil, fmt.Errorf("github authorization failed: %w", err) |
| 120 | } |
| 121 | if outcome != nil && outcome.UserAction != nil { |
| 122 | logger.Info("surfacing github authorization instructions to user") |
| 123 | return &mcp.CallToolResult{ |
| 124 | Content: []mcp.Content{&mcp.TextContent{Text: outcome.UserAction.Message}}, |
| 125 | }, nil |
| 126 | } |
| 127 | return next(ctx, method, request) |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | // ensure sessionPrompter satisfies the Prompter contract. |
| 133 | var _ oauth.Prompter = (*sessionPrompter)(nil) |