fix(mcp): redact credentials in JSON logs

JSON log handlers bypass String and serialize exported credentials.
Implement LogValue so remote server entries use the same redacted
representation in text and JSON logs. Extend the regression test
to cover both handlers.

Assisted-by: Codex:GPT-6
This commit is contained in:
localai-org-maint-bot committed 2026-10-03 20:03:58 +00:00
1 parent a849e872ec
commit f93781c307
3 files changed
+9 -2

No files matched your search

+1
View File
@@ -67,6 +67,7 @@ var _ = Describe("MCP remote server OAuth2 configuration", func() {
OAuth2: &MCPOAuth2Config{TokenURL: "https://idp", ClientID: "id", ClientSecret: "oauth-secret"},
}
logger.Info("cfg", "a", withToken, "b", withOAuth2)
slog.New(slog.NewJSONHandler(&buf, nil)).Info("cfg", "a", withToken, "b", withOAuth2)
out := buf.String() + fmt.Sprintf("%v %+v", withToken, MCPRemoteServers{"s": withOAuth2})
Expect(out).To(ContainSubstring("https://x"))
Expect(out).To(ContainSubstring("https://idp"))
+7 -2
View File
@@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"math"
"os"
"path/filepath"
@@ -850,8 +851,7 @@ func exactlyOne(field, literal, env string) error {
return nil
}
// String keeps credentials out of logs: the MCP code logs whole server
// entries at debug level, and both xlog and slog format structs via fmt.
// String redacts credentials when server entries are formatted as text.
func (s MCPRemoteServer) String() string {
auth := "none"
switch {
@@ -863,6 +863,11 @@ func (s MCPRemoteServer) String() string {
return fmt.Sprintf("{url: %s, auth: %s}", s.URL, auth)
}
// LogValue also redacts credentials in structured JSON logs, which bypass String.
func (s MCPRemoteServer) LogValue() slog.Value {
return slog.StringValue(s.String())
}
// @Description MCP STDIO server configuration
type MCPSTDIOServer struct {
Args []string `json:"args,omitempty"`
+1
View File
@@ -135,6 +135,7 @@ Behaviour:
- It is refreshed proactively once **half** of its lifetime (`expires_in`) has elapsed. If the provider omits `expires_in`, a lifetime of 5 minutes is assumed.
- If a refresh fails, the current token keeps being used until 30 seconds before it expires (retries wait at least 5 seconds after a failed fetch, including the first fetch). After that, requests to the MCP server fail with an error; LocalAI never falls back to an unauthenticated request.
- Configuring both `token` and `oauth2` for the same server is rejected when the model configuration is loaded, as are missing or duplicated `client_id`/`client_secret` sources. An environment variable named in `*_env` that is unset or empty makes the connection to that server fail, and the server is reported with an error in the server listing.
- Debug logs of remote server entries redact static tokens and OAuth2 client secrets in both text and JSON formats.
- Token caches and retry timers are local to each connection-owning process. Workers obtain and refresh their own tokens; this cache is not shared across the cluster.
- The environment variables are read by the process that connects to the MCP server. In distributed mode that is the agent worker, so set them there.