Skip to content

Commit 0575dff

Browse files
EItanyaclaude
andauthored
feat: add --token flag to kagent invoke for API key passthrough (#1465)
## Summary - Add `--token` flag to `kagent invoke` that injects an `Authorization: Bearer` header into A2A requests - Enables using the API key passthrough feature (#1327) from the CLI, where the Bearer token is forwarded directly to LLM providers - Uses a custom `http.RoundTripper` to inject the header on every request made by the A2A client ## Test plan - [x] Tested with valid OpenAI API key as Bearer token — agent responds normally - [x] Tested without token — fails with "api_key client option must be set" - [x] Tested with invalid token — fails with 401 auth error - [x] Tested streaming mode with token — works correctly - [x] Verified mutual exclusion CRD validation (apiKeyPassthrough + apiKeySecret rejected) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b108bde commit 0575dff

File tree

2 files changed

+29
-2
lines changed

2 files changed

+29
-2
lines changed

go/core/cli/cmd/kagent/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ func main() {
9595
invokeCmd.Flags().StringVarP(&invokeCfg.File, "file", "f", "", "File to read the task from")
9696
invokeCmd.Flags().StringVarP(&invokeCfg.URLOverride, "url-override", "u", "", "URL override")
9797
invokeCmd.Flags().MarkHidden("url-override") //nolint:errcheck
98+
invokeCmd.Flags().StringVar(&invokeCfg.Token, "token", "", "Bearer token to include in A2A requests (for API key passthrough)")
9899

99100
bugReportCmd := &cobra.Command{
100101
Use: "bug-report",

go/core/cli/internal/cli/agent/invoke.go

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"io"
7+
"net/http"
78
"os"
89
"strings"
910
"time"
@@ -21,6 +22,19 @@ type InvokeCfg struct {
2122
Agent string
2223
Stream bool
2324
URLOverride string
25+
Token string
26+
}
27+
28+
// bearerTokenTransport is an http.RoundTripper that injects an Authorization: Bearer header.
29+
type bearerTokenTransport struct {
30+
base http.RoundTripper
31+
token string
32+
}
33+
34+
func (t *bearerTokenTransport) RoundTrip(req *http.Request) (*http.Response, error) {
35+
req = req.Clone(req.Context())
36+
req.Header.Set("Authorization", "Bearer "+t.token)
37+
return t.base.RoundTrip(req)
2438
}
2539

2640
func InvokeCmd(ctx context.Context, cfg *InvokeCfg) {
@@ -64,10 +78,22 @@ func InvokeCmd(ctx context.Context, cfg *InvokeCfg) {
6478
return
6579
}
6680

81+
var a2aClientOpts []a2aclient.Option
82+
a2aClientOpts = append(a2aClientOpts, a2aclient.WithTimeout(cfg.Config.Timeout))
83+
84+
if cfg.Token != "" {
85+
a2aClientOpts = append(a2aClientOpts, a2aclient.WithHTTPClient(&http.Client{
86+
Transport: &bearerTokenTransport{
87+
base: http.DefaultTransport,
88+
token: cfg.Token,
89+
},
90+
}))
91+
}
92+
6793
var a2aClient *a2aclient.A2AClient
6894
var err error
6995
if cfg.URLOverride != "" {
70-
a2aClient, err = a2aclient.NewA2AClient(cfg.URLOverride, a2aclient.WithTimeout(cfg.Config.Timeout))
96+
a2aClient, err = a2aclient.NewA2AClient(cfg.URLOverride, a2aClientOpts...)
7197
if err != nil {
7298
fmt.Fprintf(os.Stderr, "Error creating A2A client: %v\n", err)
7399
return
@@ -85,7 +111,7 @@ func InvokeCmd(ctx context.Context, cfg *InvokeCfg) {
85111
}
86112

87113
a2aURL := fmt.Sprintf("%s/api/a2a/%s/%s", cfg.Config.KAgentURL, cfg.Config.Namespace, cfg.Agent)
88-
a2aClient, err = a2aclient.NewA2AClient(a2aURL, a2aclient.WithTimeout(cfg.Config.Timeout))
114+
a2aClient, err = a2aclient.NewA2AClient(a2aURL, a2aClientOpts...)
89115
if err != nil {
90116
fmt.Fprintf(os.Stderr, "Error creating A2A client: %v\n", err)
91117
return

0 commit comments

Comments
 (0)