feat: setup mcp install with onboarding#13
Merged
Conversation
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
SafeDep Report SummaryPackage Details
This report is generated by SafeDep Github App |
abhisek
requested changes
May 20, 2026
abhisek
approved these changes
May 20, 2026
There was a problem hiding this comment.
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 installcommand 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`). | |
| // 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 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.") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




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