mirror of
https://github.com/mark3labs/kit.git
synced 2026-06-14 03:30:26 +00:00
docs(sdk): document MCPAuthHandler and OAuth opt-in behavior
Reflect the refactor that made MCPAuthHandler an explicit, opt-in dependency for remote MCP OAuth. Four surfaces updated: - README.md: new 'MCP OAuth (remote MCP servers)' subsection under the Go SDK section, outlining the three consumer patterns (nil / CLI / custom) and linking to the full options docs. - pkg/kit/README.md: type cheat-sheet now lists MCPAuthHandler, DefaultMCPAuthHandler, and CLIMCPAuthHandler alongside the existing MCPTokenStore entries. - skills/kit-sdk/SKILL.md: Options example annotated with nil-disables- OAuth semantics; new 'MCP OAuth Authorization' section precedes the existing token-storage section; re-exported types list expanded. - www/pages/sdk/options.md: Options fields table gains MCPAuthHandler row; new top-level 'MCP OAuth Authorization' section with consumer matrix, CLI/custom/fully-custom code samples, and a warning callout about the OnAuthURL nil-hang footgun.
This commit is contained in:
@@ -588,6 +588,38 @@ are pointer types so explicit `0.0` is distinguishable from "leave alone"; a
|
||||
non-zero `MaxTokens` suppresses automatic right-sizing the same way `--max-tokens`
|
||||
does on the CLI.
|
||||
|
||||
### MCP OAuth (remote MCP servers)
|
||||
|
||||
When a remote MCP server returns 401, Kit runs the full OAuth flow (dynamic
|
||||
client registration → PKCE → token exchange → persistence) but delegates the
|
||||
user-facing step — showing the authorization URL and receiving the callback —
|
||||
to an `MCPAuthHandler` that you pass explicitly via `Options.MCPAuthHandler`.
|
||||
If nil, OAuth is disabled and the authorization-required error surfaces to the
|
||||
caller; the SDK never auto-opens a browser or binds a localhost port.
|
||||
|
||||
```go
|
||||
// CLI/TUI apps: opens the system browser + prints status to stderr.
|
||||
authHandler, _ := kit.NewCLIMCPAuthHandler()
|
||||
defer authHandler.Close()
|
||||
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
MCPAuthHandler: authHandler,
|
||||
})
|
||||
|
||||
// Custom UX: reuse the SDK's port + callback server, supply your own
|
||||
// presentation via OnAuthURL (TUI modal, QR code, web redirect, etc.).
|
||||
// h, _ := kit.NewDefaultMCPAuthHandler()
|
||||
// h.OnAuthURL = func(server, authURL string) { myUI.Show(server, authURL) }
|
||||
//
|
||||
// Full control (web apps, daemons): implement kit.MCPAuthHandler yourself —
|
||||
// no localhost binding, no side effects.
|
||||
```
|
||||
|
||||
Tokens are persisted to `$XDG_CONFIG_HOME/.kit/mcp_tokens.json` by default; swap
|
||||
in a custom `MCPTokenStoreFactory` for encrypted, DB-backed, or in-memory
|
||||
storage. See the [SDK options docs](/sdk/options#mcp-oauth-authorization) for
|
||||
the full matrix.
|
||||
|
||||
### Custom Tools
|
||||
|
||||
Create custom tools with automatic schema generation — no external dependencies needed:
|
||||
|
||||
+7
-4
@@ -224,10 +224,13 @@ kit.LLMResponse // {Content, FinishReason, Usage}
|
||||
kit.LLMFilePart // {Filename, Data []byte, MediaType}
|
||||
|
||||
// MCP OAuth types
|
||||
kit.MCPServer // *server.MCPServer for in-process MCP transport
|
||||
kit.MCPServerConfig // Configuration for an MCP server (stdio, SSE, or in-process)
|
||||
kit.MCPTokenStore // Persists OAuth tokens for a single MCP server
|
||||
kit.MCPToken // OAuth token (access token, refresh token, expiry)
|
||||
kit.MCPServer // *server.MCPServer for in-process MCP transport
|
||||
kit.MCPServerConfig // Configuration for an MCP server (stdio, SSE, or in-process)
|
||||
kit.MCPAuthHandler // Interface: handles user-facing OAuth authorization
|
||||
kit.DefaultMCPAuthHandler // Port + callback-server mechanics; set OnAuthURL for presentation
|
||||
kit.CLIMCPAuthHandler // CLI wrapper: opens browser, prints status
|
||||
kit.MCPTokenStore // Persists OAuth tokens for a single MCP server
|
||||
kit.MCPToken // OAuth token (access token, refresh token, expiry)
|
||||
kit.MCPTokenStoreFactory // Creates an MCPTokenStore for a given server URL
|
||||
|
||||
// Conversion helpers
|
||||
|
||||
+70
-3
@@ -125,7 +125,12 @@ host, err := kit.New(ctx, &kit.Options{
|
||||
AutoCompact: true, // auto-compact near context limit
|
||||
CompactionOptions: &kit.CompactionOptions{...}, // nil = defaults
|
||||
|
||||
// MCP OAuth
|
||||
// MCP OAuth — both fields are opt-in. If MCPAuthHandler is nil,
|
||||
// remote MCP servers that require OAuth will fail to connect with
|
||||
// an authorization-required error instead of silently opening a
|
||||
// browser. CLI consumers use NewCLIMCPAuthHandler; other embedders
|
||||
// implement MCPAuthHandler or configure DefaultMCPAuthHandler.
|
||||
MCPAuthHandler: mcpAuthHandler, // nil = OAuth disabled
|
||||
MCPTokenStoreFactory: func(serverURL string) (kit.MCPTokenStore, error) {
|
||||
return myCustomStore(serverURL), nil // custom OAuth token storage
|
||||
},
|
||||
@@ -820,9 +825,65 @@ err = host.SubscribeMCPResource(ctx, "myserver", "file:///path/to/file")
|
||||
err = host.UnsubscribeMCPResource(ctx, "myserver", "file:///path/to/file")
|
||||
```
|
||||
|
||||
### MCP OAuth Authorization
|
||||
|
||||
When a remote MCP server requires OAuth, Kit runs the full authorization flow
|
||||
(dynamic client registration → PKCE → user consent → token exchange → token
|
||||
persistence) but delegates the **user-facing step** — displaying the
|
||||
authorization URL and receiving the callback — to an `MCPAuthHandler`.
|
||||
|
||||
The SDK ships three building blocks:
|
||||
|
||||
| Building block | When to use |
|
||||
|---|---|
|
||||
| **No handler** (`Options.MCPAuthHandler = nil`) | Default. OAuth is disabled; 401s from remote MCP servers surface as errors. Correct for library, daemon, and web-app embedders that don't want side effects. |
|
||||
| **`kit.NewCLIMCPAuthHandler()`** | CLI/TUI apps. Opens the system browser, prints status to stderr (or via `NotifyFunc`), runs a localhost callback server. This is what the `kit` binary uses. |
|
||||
| **`kit.NewDefaultMCPAuthHandler()` + `OnAuthURL`** | Custom UX. Get the transport mechanics (port reservation + callback server) from the SDK; wire your own presentation in the `OnAuthURL(serverName, authURL)` closure. |
|
||||
| **Implement `kit.MCPAuthHandler` directly** | Full control. No localhost binding — e.g. return the URL from an HTTP endpoint and have the consumer POST the callback URL back. |
|
||||
|
||||
**CLI-style embedder (browser + stderr):**
|
||||
|
||||
```go
|
||||
authHandler, err := kit.NewCLIMCPAuthHandler()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer authHandler.Close() // release the reserved port
|
||||
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
MCPAuthHandler: authHandler,
|
||||
})
|
||||
```
|
||||
|
||||
**Custom UX embedder (TUI modal, QR code, web redirect, etc.):**
|
||||
|
||||
```go
|
||||
authHandler, _ := kit.NewDefaultMCPAuthHandler()
|
||||
authHandler.OnAuthURL = func(serverName, authURL string) {
|
||||
// Render the URL however you like — no browser or terminal assumptions.
|
||||
myUI.ShowAuthPrompt(serverName, authURL)
|
||||
}
|
||||
defer authHandler.Close()
|
||||
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
MCPAuthHandler: authHandler,
|
||||
})
|
||||
```
|
||||
|
||||
**Important:** `DefaultMCPAuthHandler` with no `OnAuthURL` set will silently
|
||||
drop the authorization URL and block until the 2-minute callback timeout
|
||||
fires. Always set `OnAuthURL`, or use a higher-level wrapper like
|
||||
`CLIMCPAuthHandler`.
|
||||
|
||||
### MCP OAuth Token Storage
|
||||
|
||||
For remote MCP servers that use OAuth, you can provide a custom token store:
|
||||
Once authorization succeeds, the resulting access/refresh tokens are persisted
|
||||
by an `MCPTokenStore`. By default tokens are written to
|
||||
`$XDG_CONFIG_HOME/.kit/mcp_tokens.json` (fallback `~/.config/.kit/mcp_tokens.json`),
|
||||
keyed by server URL, with `0600` file permissions.
|
||||
|
||||
Provide a custom store for encrypted storage, database persistence, or
|
||||
in-memory-only flows:
|
||||
|
||||
```go
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
@@ -832,7 +893,7 @@ host, _ := kit.New(ctx, &kit.Options{
|
||||
})
|
||||
```
|
||||
|
||||
The `MCPTokenStore` interface requires `GetToken`/`SetToken`/`DeleteToken` methods. Return `kit.ErrMCPNoToken` from `GetToken` when no token is stored. When nil (default), tokens are persisted to `$XDG_CONFIG_HOME/.kit/mcp_tokens.json`.
|
||||
The `MCPTokenStore` interface requires `GetToken`/`SetToken`/`DeleteToken` methods. Return `kit.ErrMCPNoToken` from `GetToken` when no token is stored.
|
||||
|
||||
---
|
||||
|
||||
@@ -1015,6 +1076,12 @@ kit.LLMFilePart // {Filename, Data []byte, MediaType}
|
||||
kit.CompactionResult, kit.CompactionOptions
|
||||
|
||||
// MCP OAuth types
|
||||
kit.MCPAuthHandler // interface: RedirectURI() + HandleAuth(ctx, server, authURL) for OAuth UX
|
||||
kit.DefaultMCPAuthHandler // SDK-provided transport mechanics (port + callback server); set OnAuthURL hook
|
||||
kit.CLIMCPAuthHandler // CLI wrapper around DefaultMCPAuthHandler: opens browser, prints status
|
||||
kit.NewDefaultMCPAuthHandler() // random port, no UX side effects
|
||||
kit.NewDefaultMCPAuthHandlerWithPort() // fixed port (useful when registering a stable redirect URI)
|
||||
kit.NewCLIMCPAuthHandler() // CLI handler: browser + stderr + localhost callback
|
||||
kit.MCPTokenStore // interface for custom OAuth token storage
|
||||
kit.MCPToken // OAuth token struct (access, refresh, expiry)
|
||||
kit.MCPTokenStoreFactory // func(serverURL string) (MCPTokenStore, error)
|
||||
|
||||
@@ -65,7 +65,11 @@ host, err := kit.New(ctx, &kit.Options{
|
||||
// Session (advanced)
|
||||
SessionManager: myCustomSession, // custom SessionManager implementation
|
||||
|
||||
// MCP OAuth
|
||||
// MCP OAuth — both opt-in. Leave MCPAuthHandler nil to disable
|
||||
// OAuth entirely (remote MCP 401s bubble up as errors). CLI apps
|
||||
// pass kit.NewCLIMCPAuthHandler(); custom UX embedders implement
|
||||
// MCPAuthHandler or configure DefaultMCPAuthHandler + OnAuthURL.
|
||||
MCPAuthHandler: authHandler, // nil = OAuth disabled
|
||||
MCPTokenStoreFactory: func(serverURL string) (kit.MCPTokenStore, error) {
|
||||
return myStore(serverURL), nil
|
||||
},
|
||||
@@ -162,9 +166,88 @@ when embedding Kit as a library.
|
||||
|-------|------|---------|-------------|
|
||||
| `AutoCompact` | `bool` | `false` | Auto-compact when near context limit |
|
||||
| `CompactionOptions` | `*CompactionOptions` | — | Configuration for auto-compaction |
|
||||
| `MCPTokenStoreFactory` | `func` | — | Custom OAuth token storage for MCP servers |
|
||||
| `MCPAuthHandler` | `MCPAuthHandler` | — | OAuth handler for remote MCP servers. `nil` disables OAuth (servers returning 401 fail with the authorization-required error). See [MCP OAuth](#mcp-oauth-authorization) below. |
|
||||
| `MCPTokenStoreFactory` | `func` | — | Custom OAuth token storage for MCP servers (default: JSON file in `$XDG_CONFIG_HOME/.kit/mcp_tokens.json`). |
|
||||
| `InProcessMCPServers` | `map[string]*MCPServer` | — | In-process mcp-go servers (no subprocess) |
|
||||
|
||||
## MCP OAuth Authorization
|
||||
|
||||
When a remote MCP server (SSE or Streamable HTTP) requires OAuth, Kit runs
|
||||
the full authorization flow (dynamic client registration → PKCE → user
|
||||
consent → token exchange → token persistence) but delegates the **user-facing
|
||||
step** — displaying the authorization URL and receiving the callback — to
|
||||
an `MCPAuthHandler`.
|
||||
|
||||
The SDK is deliberately inert when `MCPAuthHandler` is `nil`: it does **not**
|
||||
auto-construct a default handler, bind a local TCP port, or open a browser.
|
||||
This keeps library, daemon, and web-app embedders free of surprise I/O.
|
||||
Consumers opt in by passing a handler explicitly.
|
||||
|
||||
| Building block | When to use |
|
||||
|---|---|
|
||||
| `MCPAuthHandler = nil` (default) | OAuth disabled. Remote MCP servers requiring auth fail with a clear error. Correct for libraries, daemons, and web apps. |
|
||||
| `kit.NewCLIMCPAuthHandler()` | CLI/TUI apps. Opens the system browser, prints status to stderr (or via `NotifyFunc`), runs a localhost callback server. Used by the `kit` binary. |
|
||||
| `kit.NewDefaultMCPAuthHandler()` + `OnAuthURL` | Custom UX. Use the SDK's port reservation and callback server; plug in your own presentation via the `OnAuthURL(serverName, authURL)` closure. |
|
||||
| Implement `kit.MCPAuthHandler` directly | Full control. No localhost binding — e.g. return the URL from an HTTP endpoint and have the consumer POST the callback URL back. |
|
||||
|
||||
**CLI-style embedder:**
|
||||
|
||||
```go
|
||||
authHandler, err := kit.NewCLIMCPAuthHandler()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer authHandler.Close() // release the reserved port
|
||||
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
MCPAuthHandler: authHandler,
|
||||
})
|
||||
```
|
||||
|
||||
**Custom UX embedder (TUI modal, QR code, web redirect, etc.):**
|
||||
|
||||
```go
|
||||
authHandler, _ := kit.NewDefaultMCPAuthHandler()
|
||||
authHandler.OnAuthURL = func(serverName, authURL string) {
|
||||
// No browser or terminal assumptions — render however you like.
|
||||
myUI.ShowAuthPrompt(serverName, authURL)
|
||||
}
|
||||
defer authHandler.Close()
|
||||
|
||||
host, _ := kit.New(ctx, &kit.Options{
|
||||
MCPAuthHandler: authHandler,
|
||||
})
|
||||
```
|
||||
|
||||
**Fully custom handler (no local port binding at all):**
|
||||
|
||||
```go
|
||||
type WebAuthHandler struct {
|
||||
redirectURI string
|
||||
callbacks chan string
|
||||
}
|
||||
|
||||
func (h *WebAuthHandler) RedirectURI() string { return h.redirectURI }
|
||||
|
||||
func (h *WebAuthHandler) HandleAuth(ctx context.Context, serverName, authURL string) (string, error) {
|
||||
// Push the URL to the user's existing browser session via your web app,
|
||||
// then block on the callback that your HTTP handler pushes onto the channel.
|
||||
h.pushToUserSession(serverName, authURL)
|
||||
select {
|
||||
case callbackURL := <-h.callbacks:
|
||||
return callbackURL, nil
|
||||
case <-ctx.Done():
|
||||
return "", ctx.Err()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
::: warning
|
||||
`DefaultMCPAuthHandler` with no `OnAuthURL` set will silently drop the
|
||||
authorization URL and hang until the 2-minute callback timeout fires. Always
|
||||
set `OnAuthURL`, or use a higher-level wrapper like `CLIMCPAuthHandler`.
|
||||
:::
|
||||
|
||||
## Precedence
|
||||
|
||||
For any given generation or provider field, the effective value is resolved
|
||||
|
||||
Reference in New Issue
Block a user