Skip to content

feat: setup mcp install with onboarding#13

Merged
arunanshub merged 28 commits into
mainfrom
feat/setup-mcp-install-onboarding
May 20, 2026
Merged

feat: setup mcp install with onboarding#13
arunanshub merged 28 commits into
mainfrom
feat/setup-mcp-install-onboarding

Conversation

@arunanshub
Copy link
Copy Markdown
Contributor

@arunanshub arunanshub commented May 15, 2026

closes safedep/control-tower#789, safedep/control-tower#787

arunanshub and others added 24 commits May 11, 2026 19:08
setup was conflated with protect due to earlier naming churn
(protect → endpoint, but endpoint was taken by cloud endpoint mgmt).
Canonical MCP config commands now live at safedep protect mcp
install/uninstall per the CLI Experience doc.

setup mcp (the first-timer wizard shortcut = auth login + protect mcp
install) is deferred to its own PR once the #787 onboarding path is
ready.
Spec and plan files generated during the design session do not belong
in project docs. Abhisek's review: remove these; internal skill
convention uses docs/specs not docs/superpowers.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Claude Code stores user-level MCP servers in ~/.claude.json (where
context7, notion etc. live), not ~/.claude/settings.json which is for
permissions, model, and plugin settings. Entries in ~/.claude.json also
require "type": "http" for remote servers. Workspace injection stays at
.claude/settings.json within the project directory.
Antigravity defaults to SSE transport when no type is specified,
causing "session not found" errors against Streamable HTTP servers.
Adding "type": "streamable-http" explicitly directs Antigravity to
use the correct transport.
VS Code uses "servers" (not "mcpServers") as the root key and
"type": "http" + "url" for remote HTTP servers. Global user config
lives in a platform-specific path (AppData on Windows/WSL2) that
cannot be reliably resolved from the CLI, so only workspace injection
is supported via .vscode/mcp.json. Detected via ~/.vscode-server
(WSL2 Remote) or ~/.config/Code/User (native Linux).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Previously, agents like VS Code (workspace-only) were detected and
logged as "Configuring vscode" even without --workspace, but nothing
was actually written. configurableAgents() now filters to agents that
have at least one applicable injector given the current workspaceDir.
…ser/mcp.json

VS Code Remote-WSL creates and reads ~/.config/Code/User/mcp.json on
the Linux/WSL2 side. Global injection now writes there alongside the
existing workspace injection at .vscode/mcp.json.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Linux/WSL2:  ~/.config/Code/User/mcp.json
macOS:       ~/Library/Application Support/Code/User/mcp.json
Windows:     ~\AppData\Roaming\Code\User\mcp.json

Detection uses the same OS-specific directory so VS Code is not
falsely detected on systems where the relevant directory is absent.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
/sse is the legacy SSE endpoint. /mcp is the Streamable HTTP endpoint.
Antigravity's "type": "streamable-http" was already correct.
An empty file (zero bytes) causes json.Unmarshal to return "unexpected
end of JSON input". VS Code creates ~/.config/Code/User/mcp.json as
an empty file before any MCP servers are configured. Treat len==0 the
same as file-not-found in readJSONFile, removeMCPConfig, and
removeVSCodeMCPConfig.
Adds NormalizeTenantDomain and GenerateTenantDomain functions to convert
organization names into valid tenant domain slugs. Implements NFKD unicode
normalization, combining mark stripping, hyphenation, and random adjective-noun
suffix generation with cryptographically secure randomness.

- NormalizeTenantDomain: slugify arbitrary strings into valid domain names
- GenerateTenantDomain: create unique slugs by combining org names with random
  adjective-noun-suffix patterns
- Comprehensive table-driven tests covering edge cases, unicode, length limits
…rding

# Conflicts:
#	internal/cmd/protect/mcp/service.go
@arunanshub arunanshub requested a review from abhisek May 15, 2026 09:13
@arunanshub arunanshub marked this pull request as ready for review May 18, 2026 06:09
@safedep
Copy link
Copy Markdown

safedep Bot commented May 18, 2026

SafeDep Report Summary

Green Malicious Packages Badge Green Vulnerable Packages Badge Green Risky License Badge

Package Details
Package Malware Vulnerability Risky License Report
icon github.com/brunoga/deep @ v1.3.1
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/fatih/structs @ v1.1.0
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/huandu/xstrings @ v1.5.0
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/jedib0t/go-pretty/v6 @ v6.7.9
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/knadh/koanf/maps @ v0.1.2
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/knadh/koanf/parsers/yaml @ v1.1.0
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/knadh/koanf/providers/env @ v1.1.0
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/knadh/koanf/providers/file @ v1.2.1
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/knadh/koanf/providers/posflag @ v1.0.1
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/knadh/koanf/providers/structs @ v1.0.0
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/knadh/koanf/v2 @ v2.3.2
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/mattn/go-colorable @ v0.1.14
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/mitchellh/copystructure @ v1.2.0
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/mitchellh/reflectwalk @ v1.0.2
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/rs/zerolog @ v1.34.0
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/stretchr/objx @ v0.5.3
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/vektra/mockery/v3 @ v3.7.0
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/xeipuuv/gojsonpointer @ v0.0.0-20190905194746-02993c407bfb
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/xeipuuv/gojsonreference @ v0.0.0-20180127040603-bd5ef7bd5415
go.mod
ok icon
ok icon
ok icon
🔗
icon github.com/xeipuuv/gojsonschema @ v1.2.0
go.mod
ok icon
ok icon
ok icon
🔗
icon golang.org/x/exp @ v0.0.0-20260212183809-81e46e3db34a
go.mod
ok icon
ok icon
ok icon
🔗
icon golang.org/x/mod @ v0.34.0
go.mod
ok icon
ok icon
ok icon
🔗
icon golang.org/x/oauth2 @ v0.36.0
go.mod
ok icon
ok icon
ok icon
🔗
icon golang.org/x/text @ v0.36.0
go.mod
ok icon
ok icon
ok icon
🔗
icon golang.org/x/tools @ v0.43.0
go.mod
ok icon
ok icon
ok icon
🔗

View complete scan results →

This report is generated by SafeDep Github App

Comment thread internal/auth/jwt.go Outdated
@abhisek abhisek requested a review from Copilot May 20, 2026 08:20
@arunanshub arunanshub merged commit 684b9e7 into main May 20, 2026
8 checks passed
@arunanshub arunanshub deleted the feat/setup-mcp-install-onboarding branch May 20, 2026 08:23
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new guided onboarding command (safedep setup mcp install) that authenticates via OAuth device flow, provisions credentials/API keys, and injects the SafeDep MCP server configuration into detected AI agent configs. It also extends the auth layer to support first-time tenant registration, email-verification retry handling, and silent token refresh for control-plane calls.

Changes:

  • Introduce the setup mcp install command tree and onboarding service that combines auth + MCP config injection.
  • Refactor/extend auth flows: device-flow retry policy, tenant registration prompts + registration API call, and centralized credential persistence helpers.
  • Add silent refresh-token-based access token refresh for control-plane usage, plus mockery tooling/config and new unit tests across auth helpers.

Reviewed changes

Copilot reviewed 29 out of 31 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
README.md Adds the new safedep setup mcp install command to the command index.
Makefile Adds a mocks target for mockery regeneration.
internal/endpoint/identity.go Centralizes the default MCP server URL constant used by multiple commands.
internal/cmd/setup/mcp/service.go Implements the setup/onboarding flow (credentials resolution + agent injection).
internal/cmd/setup/mcp/install.go Wires the setup mcp install cobra command and flags.
internal/cmd/setup/mcp/install_test.go Verifies setup mcp install command registration and flags.
internal/cmd/setup/mcp/cmd.go Registers the setup mcp subtree under setup.
internal/cmd/setup/cmd.go Adds the top-level setup command and registers subcommands.
internal/cmd/safedep.go Hooks the new setup command into the root CLI.
internal/cmd/protect/mcp/install.go Switches to the shared default MCP URL constant.
internal/cmd/auth/login.go Updates device login flow to use shared prompts, registration flow, and shared credential persistence.
internal/auth/save.go Adds helper to persist bootstrap results to the credential store.
internal/auth/save_test.go Adds tests for bootstrap-result persistence behavior and error paths.
internal/auth/register.go Implements first-time tenant registration via onboarding service with retry on uniqueness conflicts.
internal/auth/register_test.go Adds bufconn-based tests for registration retries and error handling.
internal/auth/refresh.go Adds silent refresh-token flow for expired access tokens and persistence of refreshed tokens.
internal/auth/prompts.go Adds interactive registration + tenant picker + verification printing + email verification retry + hostname helper.
internal/auth/mocks/CredentialStore_mock.go Adds generated mock for cloud.CredentialStore.
internal/auth/jwt.go Adds JWT email-claim extraction for pre-filling registration prompts.
internal/auth/jwt_test.go Tests email extraction behavior for standard and namespaced claims.
internal/auth/domain.go Adds tenant-domain normalization and randomized domain generation helpers.
internal/auth/domain_test.go Adds tests for domain normalization/generation behavior.
internal/auth/device.go Adds retry policy support and explicit handling for “email not verified” device-flow failures.
internal/auth/bootstrap.go Adds registration-on-zero-tenants path and treats NotFound from user info as “zero tenants”.
internal/auth/bootstrap_test.go Adds end-to-end bufconn tests covering the new bootstrap registration flow.
internal/app/app.go Adds silent token refresh on control-plane client creation and uses a shared gRPC app name.
internal/agent/registry.go Adds a small helper to filter detected agents.
go.sum Updates dependency checksums for new/updated dependencies and tools.
go.mod Adds new dependencies (oauth2/text) and mockery tool directive.
docs/cmd/setup-mcp-install.md Adds documentation for the new onboarding command.
.mockery.yml Adds mockery configuration for generating mocks into internal/auth/mocks/.
Files not reviewed (1)
  • internal/auth/mocks/CredentialStore_mock.go: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


Guided first-time onboarding: authenticate with SafeDep Cloud and inject the SafeDep MCP server into AI coding agent config files in a single command.

Agents supported: Claude Code, Cursor, Gemini CLI, OpenCode, Antigravity.

| Flag | Description |
|---|---|
| `--mcp-url <url>` | SafeDep MCP server URL (default: `https://mcp.safedep.io/model-context-protocol/threats/v1`). |
Comment thread internal/auth/refresh.go
// tokens, and returns fresh credentials. Returns creds unchanged when not expired.
func RefreshAndPersistIfExpired(ctx context.Context, store cloud.CredentialStore, creds *cloud.Credentials, keychainOpts []cloud.KeychainOption) (*cloud.Credentials, error) {
token, err := creds.GetToken()
if err != nil || !IsExpired(token, time.Now()) {
Comment thread internal/auth/refresh.go
Comment on lines +18 to +25
// RefreshAndPersistIfExpired checks whether creds contain an expired token and,
// if so, silently refreshes it using the stored refresh token, persists the new
// tokens, and returns fresh credentials. Returns creds unchanged when not expired.
func RefreshAndPersistIfExpired(ctx context.Context, store cloud.CredentialStore, creds *cloud.Credentials, keychainOpts []cloud.KeychainOption) (*cloud.Credentials, error) {
token, err := creds.GetToken()
if err != nil || !IsExpired(token, time.Now()) {
return creds, nil
}
Comment on lines +31 to +54
func (s *setupMCPService) install(ctx context.Context, in installInput) error {
apiKey, tenantID, credentialsSaved, err := s.resolveCredentials(ctx, in)
if err != nil {
return err
}

cfg, err := endpoint.BuildMCPConfig(in.MCPURL, apiKey, tenantID, s.resolver)
if err != nil {
s.warnMCPFailed(credentialsSaved, "%v", err)
return nil
}

detected := agent.FilterDetected(s.agents)
if len(detected) == 0 {
tui.Warning("No supported AI agents detected on this machine.")
return nil
}

if err := agent.InjectAll(detected, cfg, in.WorkspaceDir); err != nil {
s.warnMCPFailed(credentialsSaved, "%v", err)
return nil
}

tui.Success("SafeDep MCP server configured. You're ready to use AI agents with SafeDep protection.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants