Summary
arctl agent run fails with 401 Unauthorized when the agent manifest contains a type: registry MCP server. At run time, the CLI must call the registry API to resolve the server spec (image, transport, env vars) before it can start the container. The resolver creates a bare HTTP client that never sends an auth token, so this resolution always fails against any server that requires authentication.
When the manifest has no MCP servers (mcpServers: []), the resolver loop is a no-op and the agent starts fine. The bug only surfaces after arctl agent add-mcp adds a type: registry entry.
Reproduce
arctl mcp publish ./my-server --type oci --package-id ... --version 0.1.0
arctl agent add-mcp foo --registry-server-name myorg/foo --registry-server-version 0.1.0
arctl agent run ./my-agent
# → failed to fetch server "myorg/foo" from registry: unexpected status code 401: Unauthorized
Root cause
internal/cli/agent/utils/registry_resolver.go:92 creates registry.NewClient(), which is a plain http.Client with no auth:
client := registry.NewClient() // no token
serverEntry, err := client.FetchServer(registryURL, ...)
Every other CLI API call goes through internal/client.Client (set via agent.SetAPIClient() in pkg/cli/root.go:77), which attaches Authorization: Bearer <token> on every request (client.go:104). The registry resolver bypasses this entirely.
The prompt resolution path at registry_resolver.go:235 has the same bug — it creates client.NewClient(registryURL, "") with an empty token.
Fix
Pass the authenticated apiClient (or its token) into ParseAgentManifestServers / resolveRegistryServer instead of creating a new unauthenticated client. The prompt resolution path (ResolveManifestPrompts) should be fixed the same way.
Workaround
None — ARCTL_API_TOKEN env var and --registry-token flag only configure the main apiClient, not the registry.Client used in the resolution code path.
Summary
arctl agent runfails with401 Unauthorizedwhen the agent manifest contains atype: registryMCP server. At run time, the CLI must call the registry API to resolve the server spec (image, transport, env vars) before it can start the container. The resolver creates a bare HTTP client that never sends an auth token, so this resolution always fails against any server that requires authentication.When the manifest has no MCP servers (
mcpServers: []), the resolver loop is a no-op and the agent starts fine. The bug only surfaces afterarctl agent add-mcpadds atype: registryentry.Reproduce
arctl mcp publish ./my-server --type oci --package-id ... --version 0.1.0 arctl agent add-mcp foo --registry-server-name myorg/foo --registry-server-version 0.1.0 arctl agent run ./my-agent # → failed to fetch server "myorg/foo" from registry: unexpected status code 401: UnauthorizedRoot cause
internal/cli/agent/utils/registry_resolver.go:92createsregistry.NewClient(), which is a plainhttp.Clientwith no auth:Every other CLI API call goes through
internal/client.Client(set viaagent.SetAPIClient()inpkg/cli/root.go:77), which attachesAuthorization: Bearer <token>on every request (client.go:104). The registry resolver bypasses this entirely.The prompt resolution path at
registry_resolver.go:235has the same bug — it createsclient.NewClient(registryURL, "")with an empty token.Fix
Pass the authenticated
apiClient(or its token) intoParseAgentManifestServers/resolveRegistryServerinstead of creating a new unauthenticated client. The prompt resolution path (ResolveManifestPrompts) should be fixed the same way.Workaround
None —
ARCTL_API_TOKENenv var and--registry-tokenflag only configure the mainapiClient, not theregistry.Clientused in the resolution code path.