Skip to content

Add aitools mcp login, logout and list --check - #7551

Draft
simonfaltum wants to merge 66 commits into
aitools-mcp/06b-servefrom
aitools-mcp/07-login
Draft

simonfaltum wants to merge 66 commits into
aitools-mcp/06b-servefrom
aitools-mcp/07-login

Conversation

@simonfaltum

@simonfaltum simonfaltum commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Part of a stack that adds the hidden databricks aitools mcp command group, on #7550 (aitools mcp serve). It adds provider sign-in for external MCP services such as GitHub, which each user signs in to before the service's tools work.

Changes

  • mcp login <service> prints the sign-in URL, opens it through libs/browser, and waits up to 5 minutes. Server and network errors while polling are retried until the timeout, and the timeout names the last one. Other errors end the sign-in. An already signed-in caller gets "Already signed in" and no URL.
  • mcp logout <service> deletes the caller's credential, even when its service is no longer visible.
  • login and logout refuse a changed profile before any request, as add and serve do.
  • mcp add --login signs in to each sign-in-required server it installed. Its JSON gains a per-server sign_in object with the state login reports. A failed sign-in exits 1 and keeps the installs.
  • mcp list --check checks each profile's token, then each server on its own status. A drifted server or one that cannot be read is skipped, and says why. Each check request ends after 5 seconds. A sign-in that is needed names the login command.
  • login names the URL in JSON only when the page was shown.
  • Hints for a server pinned to a config file set DATABRICKS_CONFIG_FILE.
  • libs/browser gains OpenOutputToStderr for JSON output. Open is unchanged for other callers. The default opener no longer waits for a browser its launcher leaves running.

Why

A sign-in is judged on the attempt that runs: a FAILED credential from an earlier attempt is deleted before the page opens. A failed poll does not mean the user has left the provider's page, so it does not end the wait. One unreachable workspace held list --check for the SDK's five-minute retry budget. The SDK puts the service name into the request path unescaped, so names are escaped.

Decisions to review

  • SignIn returns (alreadySignedIn, urlShown bool, err error), not a state. The command derives the JSON state and URL from them.
  • Polls retry 5xx and network errors. The cost is a longer wait while the error persists, up to the timeout. A 429 that the SDK gave up on still ends the sign-in.
  • The 5-second limit per request is the one cmd/auth uses for profile validation. A workspace slower than that shows unknown where a longer wait would answer.
  • logout sends the DELETE before it looks up the service, so a credential for a deleted service can be removed. A mistyped name costs one request.
  • A custom BROWSER command's output goes to stderr during sign-in, so it cannot land before the JSON document.
  • list --check sets DATABRICKS_CONFIG_FILE in the process environment while it checks a profile, because the token store reads the process environment.
  • JSON state adds FAILED and NOT_SIGNED_IN to the documented values.

Tests

  • libs/browser: TestOpenDoesNotWaitForLauncherChild failed before the fix. The overlapping-opens test stubs the launcher, so no browser opens on any OS.
  • libs/aitools/mcp: polls through server errors, an authentication error ends at once, the timeout's last error.
  • cmd/aitools: TestAddChecksBoundsAProfileThatNeverAnswers hung past five minutes before the fix; TestAddChecksBoundsEachRequestNotTheProfile covers a profile whose requests take longer than the limit together. Unreachable profiles are unknown, not failed. Skipped servers, the sign_in JSON, and the login URL rule are covered too.
  • Acceptance: list-check (GitHub's recorded binary is now also missing) and login-flips-active.
  • Ran: go test -race -count=3 on the touched packages; the Taskfile's gotestsum flags with BROWSER unset, BROWSER=none and a symlinked TMPDIR; ./task test-exp-aitools; go test ./acceptance -run TestAccept/cmd/aitools; GOOS=windows go vet and go test -c for cmd/aitools, libs/browser and libs/exec; ./task lint-q, ./task fmt-q, ./task checks.
  • Not tested: the 5-minute timeout through the CLI (unit tests use milliseconds), and a real provider sign-in. Windows and macOS acceptance come from CI.

This pull request and its description were written by Isaac.

@simonfaltum
simonfaltum changed the base branch from aitools-mcp/06a-bridge to aitools-mcp/06b-serve November 12, 2026 12:42
@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Commit: 4a8c08cc5 | Workflow run

Regression test report

Selected 14 changed tests (limit=20, 0 not selected)

14 tests: regression=14 unreleased=0 coverage=0 failing=0
test branch main (8e70c8b) latest (v1.26.0)
TestAccept/cmd/aitools/mcp/add-agent-not-installed ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/add-default-config ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/add-text ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/add-two-agents ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/add-two-profiles ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/add-unparsable-config ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/drift-profile-missing ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/hidden ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/list-and-remove ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/list-check ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/login-flips-active ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/repair-and-warnings ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/serve-binding-errors ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/serve-passthrough ✅ ❌ ❌
TestAccept/cmd/aitools/mcp/add-agent-not-installed: branch ✅ | main (8e70c8b) ❌ | latest (v1.26.0) ❌

main (8e70c8b):

acceptance_test.go:1374: Diff:
        --- cmd/aitools/mcp/add-agent-not-installed/output.txt
        +++ /tmp/TestAcceptcmdaitoolsmcpadd-agent-not-installed2631153877/002/output.txt
        @@ -2,41 +2,58 @@
         === An agent named with --agents that is not installed is refused; the installed agent still gets the server, and the command exits non-zero
         
         >>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp --agents claude-code,antigravity --output json
        -{
        -  "profile": "logfood",
        -  "host": "[DATABRICKS_URL]",
        -  "scope": "global",
        -  "servers": [
        -    {
        -      "name": "genie_one_mcp-logfood",
        -      "service": "system.ai.genie_one_mcp",
        -      "sign_in_required": false,
        -      "agents": [
        -        {
        -          "name": "claude-code",
        -          "status": "installed",
        -          "path": "[TEST_TMP_DIR]/home/.claude.json"
        -        },
        -        {
        -          "name": "antigravity",
        -          "status": "failed",
        -          "error_category": "UNKNOWN_AGENT",
        -          "message": "Antigravity is not installed; install it first or leave it out of --agents"
        -        }
        -      ]
        -    }
        -  ],
        -  "error_category": "TYPE_UNSPECIFIED"
        -}
        +Error: unknown flag: --names
         
        +Usage:
        +  databricks aitools [command]
        +
        +Available Commands:
        +  install     Install Databricks skills and plugins for coding agents
        +  list        List installed skills and plugins
        +  uninstall   Uninstall Databricks skills and plugins
        +  update      Update installed skills and plugins
        +  version     Show installed skills and plugins version
        +
        +Flags:
        +  -h, --help   help for aitools
        +
        +Global Flags:
        +      --debug            enable debug logging
        +  -o, --output type      output type: text or json (default text)
        +  -p, --profile string   ~/.databrickscfg profile
        +  -t, --target string    bundle target to use (if applicable)
        +
        +Use "databricks aitools [command] --help" for more information about a command.
        +
        +
         === Nothing was created for Antigravity
         
         >>> ls -A home
         .claude
        -.claude.json
        -.databricks
         
         === With no installed agent named, the command is refused before any request, and the text output says what to do
         
         >>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp --agents antigravity
        -Error: Antigravity is not installed; install it first or leave it out of --agents
        +Error: unknown flag: --names
        +
        +Usage:
        +  databricks aitools [command]
        +
        +Available Commands:
        +  install     Install Databricks skills and plugins for coding agents
        +  list        List installed skills and plugins
        +  uninstall   Uninstall Databricks skills and plugins
        +  update      Update installed skills and plugins
        +  version     Show installed skills and plugins version
        +
        +Flags:
        +  -h, --help   help for aitools
        +
        +Global Flags:
        +      --debug            enable debug logging
        +  -o, --output type      output type: text or json (default text)
        +  -p, --profile string   ~/.databrickscfg profile
        +  -t, --target string    bundle target to use (if applicable)
        +
        +Use "databricks aitools [command] --help" for more information about a command.
        +
        
    acceptance_test.go:1152: 
        LOG.create-genie_one_mcp-logfood: {
        LOG.create-genie_one_mcp-logfood:   "comment": "Ask questions about your data",
        LOG.create-genie_one_mcp-logfood:   "created_by": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "effective_owner": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "metastore_id": "7a0ca292-d0e3-bee2-7a0c-a292d0e3c055",
        LOG.create-genie_one_mcp-logfood:   "name": "mcp-services/system.ai.genie_one_mcp",
        LOG.create-genie_one_mcp-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-genie_one_mcp-logfood: }

latest (v1.26.0):

acceptance_test.go:1374: Diff:
        --- cmd/aitools/mcp/add-agent-not-installed/output.txt
        +++ /tmp/TestAcceptcmdaitoolsmcpadd-agent-not-installed1975880216/002/output.txt
        @@ -2,41 +2,58 @@
         === An agent named with --agents that is not installed is refused; the installed agent still gets the server, and the command exits non-zero
         
         >>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp --agents claude-code,antigravity --output json
        -{
        -  "profile": "logfood",
        -  "host": "[DATABRICKS_URL]",
        -  "scope": "global",
        -  "servers": [
        -    {
        -      "name": "genie_one_mcp-logfood",
        -      "service": "system.ai.genie_one_mcp",
        -      "sign_in_required": false,
        -      "agents": [
        -        {
        -          "name": "claude-code",
        -          "status": "installed",
        -          "path": "[TEST_TMP_DIR]/home/.claude.json"
        -        },
        -        {
        -          "name": "antigravity",
        -          "status": "failed",
        -          "error_category": "UNKNOWN_AGENT",
        -          "message": "Antigravity is not installed; install it first or leave it out of --agents"
        -        }
        -      ]
        -    }
        -  ],
        -  "error_category": "TYPE_UNSPECIFIED"
        -}
        +Error: unknown flag: --names
         
        +Usage:
        +  databricks aitools [command]
        +
        +Available Commands:
        +  install     Install Databricks skills and plugins for coding agents
        +  list        List installed skills and plugins
        +  uninstall   Uninstall Databricks skills and plugins
        +  update      Update installed skills and plugins
        +  version     Show installed skills and plugins version
        +
        +Flags:
        +  -h, --help   help for aitools
        +
        +Global Flags:
        +      --debug            enable debug logging
        +  -o, --output type      output type: text or json (default text)
        +  -p, --profile string   ~/.databrickscfg profile
        +  -t, --target string    bundle target to use (if applicable)
        +
        +Use "databricks aitools [command] --help" for more information about a command.
        +
        +
         === Nothing was created for Antigravity
         
         >>> ls -A home
         .claude
        -.claude.json
        -.databricks
         
         === With no installed agent named, the command is refused before any request, and the text output says what to do
         
         >>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp --agents antigravity
        -Error: Antigravity is not installed; install it first or leave it out of --agents
        +Error: unknown flag: --names
        +
        +Usage:
        +  databricks aitools [command]
        +
        +Available Commands:
        +  install     Install Databricks skills and plugins for coding agents
        +  list        List installed skills and plugins
        +  uninstall   Uninstall Databricks skills and plugins
        +  update      Update installed skills and plugins
        +  version     Show installed skills and plugins version
        +
        +Flags:
        +  -h, --help   help for aitools
        +
        +Global Flags:
        +      --debug            enable debug logging
        +  -o, --output type      output type: text or json (default text)
        +  -p, --profile string   ~/.databrickscfg profile
        +  -t, --target string    bundle target to use (if applicable)
        +
        +Use "databricks aitools [command] --help" for more information about a command.
        +
        
    acceptance_test.go:1152: 
        LOG.create-genie_one_mcp-logfood: {
        LOG.create-genie_one_mcp-logfood:   "comment": "Ask questions about your data",
        LOG.create-genie_one_mcp-logfood:   "created_by": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "effective_owner": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "metastore_id": "7a0ca29a-77bf-4c6f-7a0c-a29a77bf4d56",
        LOG.create-genie_one_mcp-logfood:   "name": "mcp-services/system.ai.genie_one_mcp",
        LOG.create-genie_one_mcp-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-genie_one_mcp-logfood: }
TestAccept/cmd/aitools/mcp/add-default-config: branch ✅ | main (8e70c8b) ❌ | latest (v1.26.0) ❌

main (8e70c8b):

acceptance_test.go:1374: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1374
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1115
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:660
        	Error:      	Not equal: 
        	            	expected: "\n=== Without DATABRICKS_CONFIG_FILE the server is added for the default config file\n\n>>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp --agents claude-code\nProfile logfood ([DATABRICKS_URL])\nAgents (named with --agents): claude-code\n\ngenie_one_mcp-logfood  system.ai.genie_one_mcp\n  claude-code  installed  [TEST_TMP_DIR]/home/.claude.json\n\n=== Claude Code config has no pinned variables for the server\n\n>>> gron.py home/.claude.json\njson.mcpServers.genie_one_mcp-logfood.env = {};\n\n=== State holds the binding with no config file\n\n>>> jq .mcp.servers[] | {name, profile, config_file} home/.databricks/aitools/skills/.state.json\n{\n  \"name\": \"genie_one_mcp-logfood\",\n  \"profile\": \"logfood\",\n  \"config_file\": \"\"\n}\n"
        	            	actual  : "\n=== Without DATABRICKS_CONFIG_FILE the server is added for the default config file\n\n>>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp --agents claude-code\nError: unknown flag: --names\n\nUsage:\n  databricks aitools [command]\n\nAvailable Commands:\n  install     Install Databricks skills and plugins for coding agents\n  list        List installed skills and plugins\n  uninstall   Uninstall Databricks skills and plugins\n  update      Update installed skills and plugins\n  version     Show installed skills and plugins version\n\nFlags:\n  -h, --help   help for aitools\n\nGlobal Flags:\n      --debug            enable debug logging\n  -o, --output type      output type: text or json (default text)\n  -p, --profile string   ~/.databrickscfg profile\n  -t, --target string    bundle target to use (if applicable)\n\nUse \"databricks aitools [command] --help\" for more information about a command.\n\n\nExit code: 1\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -4,21 +4,27 @@
        	            	 >>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp --agents claude-code
        	            	-Profile logfood ([DATABRICKS_URL])
        	            	-Agents (named with --agents): claude-code
        	            	+Error: unknown flag: --names
        	            	 
        	            	-genie_one_mcp-logfood  system.ai.genie_one_mcp
        	            	-  claude-code  installed  [TEST_TMP_DIR]/home/.claude.json
        	            	+Usage:
        	            	+  databricks aitools [command]
        	            	 
        	            	-=== Claude Code config has no pinned variables for the server
        	            	+Available Commands:
        	            	+  install     Install Databricks skills and plugins for coding agents
        	            	+  list        List installed skills and plugins
        	            	+  uninstall   Uninstall Databricks skills and plugins
        	            	+  update      Update installed skills and plugins
        	            	+  version     Show installed skills and plugins version
        	            	 
        	            	->>> gron.py home/.claude.json
        	            	-json.mcpServers.genie_one_mcp-logfood.env = {};
        	            	+Flags:
        	            	+  -h, --help   help for aitools
        	            	 
        	            	-=== State holds the binding with no config file
        	            	+Global Flags:
        	            	+      --debug            enable debug logging
        	            	+  -o, --output type      output type: text or json (default text)
        	            	+  -p, --profile string   ~/.databrickscfg profile
        	            	+  -t, --target string    bundle target to use (if applicable)
        	            	 
        	            	->>> jq .mcp.servers[] | {name, profile, config_file} home/.databricks/aitools/skills/.state.json
        	            	-{
        	            	-  "name": "genie_one_mcp-logfood",
        	            	-  "profile": "logfood",
        	            	-  "config_file": ""
        	            	-}
        	            	+Use "databricks aitools [command] --help" for more information about a command.
        	            	 
        	            	+
        	            	+Exit code: 1
        	            	+
        	Test:       	TestAccept/cmd/aitools/mcp/add-default-config
        	Messages:   	cmd/aitools/mcp/add-default-config/output.txt vs /tmp/TestAcceptcmdaitoolsmcpadd-default-config3073359363/002/output.txt
    acceptance_test.go:1152: 
        LOG.create-genie_one_mcp-logfood: {
        LOG.create-genie_one_mcp-logfood:   "comment": "Ask questions about your data",
        LOG.create-genie_one_mcp-logfood:   "created_by": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "effective_owner": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "metastore_id": "7a0ca293-2221-d9fd-7a0c-a2932221db48",
        LOG.create-genie_one_mcp-logfood:   "name": "mcp-services/system.ai.genie_one_mcp",
        LOG.create-genie_one_mcp-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-genie_one_mcp-logfood: }

latest (v1.26.0):

acceptance_test.go:1374: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1374
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1115
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:660
        	Error:      	Not equal: 
        	            	expected: "\n=== Without DATABRICKS_CONFIG_FILE the server is added for the default config file\n\n>>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp --agents claude-code\nProfile logfood ([DATABRICKS_URL])\nAgents (named with --agents): claude-code\n\ngenie_one_mcp-logfood  system.ai.genie_one_mcp\n  claude-code  installed  [TEST_TMP_DIR]/home/.claude.json\n\n=== Claude Code config has no pinned variables for the server\n\n>>> gron.py home/.claude.json\njson.mcpServers.genie_one_mcp-logfood.env = {};\n\n=== State holds the binding with no config file\n\n>>> jq .mcp.servers[] | {name, profile, config_file} home/.databricks/aitools/skills/.state.json\n{\n  \"name\": \"genie_one_mcp-logfood\",\n  \"profile\": \"logfood\",\n  \"config_file\": \"\"\n}\n"
        	            	actual  : "\n=== Without DATABRICKS_CONFIG_FILE the server is added for the default config file\n\n>>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp --agents claude-code\nError: unknown flag: --names\n\nUsage:\n  databricks aitools [command]\n\nAvailable Commands:\n  install     Install Databricks skills and plugins for coding agents\n  list        List installed skills and plugins\n  uninstall   Uninstall Databricks skills and plugins\n  update      Update installed skills and plugins\n  version     Show installed skills and plugins version\n\nFlags:\n  -h, --help   help for aitools\n\nGlobal Flags:\n      --debug            enable debug logging\n  -o, --output type      output type: text or json (default text)\n  -p, --profile string   ~/.databrickscfg profile\n  -t, --target string    bundle target to use (if applicable)\n\nUse \"databricks aitools [command] --help\" for more information about a command.\n\n\nExit code: 1\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -4,21 +4,27 @@
        	            	 >>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp --agents claude-code
        	            	-Profile logfood ([DATABRICKS_URL])
        	            	-Agents (named with --agents): claude-code
        	            	+Error: unknown flag: --names
        	            	 
        	            	-genie_one_mcp-logfood  system.ai.genie_one_mcp
        	            	-  claude-code  installed  [TEST_TMP_DIR]/home/.claude.json
        	            	+Usage:
        	            	+  databricks aitools [command]
        	            	 
        	            	-=== Claude Code config has no pinned variables for the server
        	            	+Available Commands:
        	            	+  install     Install Databricks skills and plugins for coding agents
        	            	+  list        List installed skills and plugins
        	            	+  uninstall   Uninstall Databricks skills and plugins
        	            	+  update      Update installed skills and plugins
        	            	+  version     Show installed skills and plugins version
        	            	 
        	            	->>> gron.py home/.claude.json
        	            	-json.mcpServers.genie_one_mcp-logfood.env = {};
        	            	+Flags:
        	            	+  -h, --help   help for aitools
        	            	 
        	            	-=== State holds the binding with no config file
        	            	+Global Flags:
        	            	+      --debug            enable debug logging
        	            	+  -o, --output type      output type: text or json (default text)
        	            	+  -p, --profile string   ~/.databrickscfg profile
        	            	+  -t, --target string    bundle target to use (if applicable)
        	            	 
        	            	->>> jq .mcp.servers[] | {name, profile, config_file} home/.databricks/aitools/skills/.state.json
        	            	-{
        	            	-  "name": "genie_one_mcp-logfood",
        	            	-  "profile": "logfood",
        	            	-  "config_file": ""
        	            	-}
        	            	+Use "databricks aitools [command] --help" for more information about a command.
        	            	 
        	            	+
        	            	+Exit code: 1
        	            	+
        	Test:       	TestAccept/cmd/aitools/mcp/add-default-config
        	Messages:   	cmd/aitools/mcp/add-default-config/output.txt vs /tmp/TestAcceptcmdaitoolsmcpadd-default-config3560603504/002/output.txt
    acceptance_test.go:1152: 
        LOG.create-genie_one_mcp-logfood: {
        LOG.create-genie_one_mcp-logfood:   "comment": "Ask questions about your data",
        LOG.create-genie_one_mcp-logfood:   "created_by": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "effective_owner": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "metastore_id": "7a0ca29a-d784-565e-7a0c-a29ad7845776",
        LOG.create-genie_one_mcp-logfood:   "name": "mcp-services/system.ai.genie_one_mcp",
        LOG.create-genie_one_mcp-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-genie_one_mcp-logfood: }
TestAccept/cmd/aitools/mcp/add-text: branch ✅ | main (8e70c8b) ❌ | latest (v1.26.0) ❌

main (8e70c8b):

acceptance_test.go:1374: Diff:
        --- cmd/aitools/mcp/add-text/output.txt
        +++ /tmp/TestAcceptcmdaitoolsmcpadd-text4103780529/002/output.txt
        @@ -2,73 +2,28 @@
         === Text output: Cursor approval hint, and the sign-in step for github
         
         >>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp,system.ai.github --agents claude-code,cursor
        -Profile logfood ([DATABRICKS_URL])
        -Agents (named with --agents): claude-code, cursor
        +Error: unknown flag: --names
         
        -genie_one_mcp-logfood  system.ai.genie_one_mcp
        -  claude-code  installed  [TEST_TMP_DIR]/home/.claude.json
        -  cursor       installed  [TEST_TMP_DIR]/home/.cursor/mcp.json
        -Cursor may ask you to approve genie_one_mcp-logfood. Run: cursor-agent mcp enable genie_one_mcp-logfood (the Cursor docs call the CLI agent), or approve it in Cursor's MCP settings.
        +Usage:
        +  databricks aitools [command]
         
        -github-logfood  system.ai.github
        -  claude-code  installed  [TEST_TMP_DIR]/home/.claude.json
        -  cursor       installed  [TEST_TMP_DIR]/home/.cursor/mcp.json
        -system.ai.github needs sign-in. Run: DATABRICKS_CONFIG_FILE=[TEST_TMP_DIR]/.databrickscfg databricks aitools mcp login system.ai.github --profile logfood
        -Cursor may ask you to approve github-logfood. Run: cursor-agent mcp enable github-logfood (the Cursor docs call the CLI agent), or approve it in Cursor's MCP settings.
        +Available Commands:
        +  install     Install Databricks skills and plugins for coding agents
        +  list        List installed skills and plugins
        +  uninstall   Uninstall Databricks skills and plugins
        +  update      Update installed skills and plugins
        +  version     Show installed skills and plugins version
         
        -=== Running it again re-asserts the entries, and nothing is written twice
        +Flags:
        +  -h, --help   help for aitools
         
        ->>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp,system.ai.github --agents claude-code,cursor
        -Profile logfood ([DATABRICKS_URL])
        -Agents (named with --agents): claude-code, cursor
        +Global Flags:
        +      --debug            enable debug logging
        +  -o, --output type      output type: text or json (default text)
        +  -p, --profile string   ~/.databrickscfg profile
        +  -t, --target string    bundle target to use (if applicable)
         
        -genie_one_mcp-logfood  system.ai.genie_one_mcp
        -  claude-code  already installed, re-asserted  [TEST_TMP_DIR]/home/.claude.json
        -  cursor       already installed, re-asserted  [TEST_TMP_DIR]/home/.cursor/mcp.json
        +Use "databricks aitools [command] --help" for more information about a command.
         
        -github-logfood  system.ai.github
        -  claude-code  already installed, re-asserted  [TEST_TMP_DIR]/home/.claude.json
        -  cursor       already installed, re-asserted  [TEST_TMP_DIR]/home/.cursor/mcp.json
        -system.ai.github needs sign-in. Run: DATABRICKS_CONFIG_FILE=[TEST_TMP_DIR]/.databrickscfg databricks aitools mcp login system.ai.github --profile logfood
         
        -=== Cursor config
        -
        ->>> cat home/.cursor/mcp.json
        -{
        -  "mcpServers": {
        -    "genie_one_mcp-logfood": {
        -      "args": [
        -        "aitools",
        -        "mcp",
        -        "serve",
        -        "--profile",
        -        "logfood",
        -        "--service",
        -        "system.ai.genie_one_mcp",
        -        "--agent",
        -        "cursor"
        -      ],
        -      "command": "[CLI]",
        -      "env": {
        -        "DATABRICKS_CONFIG_FILE": "[TEST_TMP_DIR]/.databrickscfg"
        -      }
        -    },
        -    "github-logfood": {
        -      "args": [
        -        "aitools",
        -        "mcp",
        -        "serve",
        -        "--profile",
        -        "logfood",
        -        "--service",
        -        "system.ai.github",
        -        "--agent",
        -        "cursor"
        -      ],
        -      "command": "[CLI]",
        -      "env": {
        -        "DATABRICKS_CONFIG_FILE": "[TEST_TMP_DIR]/.databrickscfg"
        -      }
        -    }
        -  }
        -}
        +Exit code: 1
        
    acceptance_test.go:1152: 
        LOG.create-genie_one_mcp-logfood: {
        LOG.create-genie_one_mcp-logfood:   "comment": "Ask questions about your data",
        LOG.create-genie_one_mcp-logfood:   "created_by": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "effective_owner": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "metastore_id": "7a0ca293-27b3-6cd9-7a0c-a29327b36dc9",
        LOG.create-genie_one_mcp-logfood:   "name": "mcp-services/system.ai.genie_one_mcp",
        LOG.create-genie_one_mcp-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-genie_one_mcp-logfood: }
    acceptance_test.go:1152: 
        LOG.create-github-logfood: {
        LOG.create-github-logfood:   "comment": "Repositories, issues, pull requests",
        LOG.create-github-logfood:   "config": {
        LOG.create-github-logfood:     "source_connection": {
        LOG.create-github-logfood:       "name": "connections/github",
        LOG.create-github-logfood:       "options": {
        LOG.create-github-logfood:         "oauth_provider": "OAUTH_PROVIDER_GITHUB_MCP"
        LOG.create-github-logfood:       }
        LOG.create-github-logfood:     }
        LOG.create-github-logfood:   },
        LOG.create-github-logfood:   "created_by": "tester@databricks.com",
        LOG.create-github-logfood:   "effective_owner": "tester@databricks.com",
        LOG.create-github-logfood:   "metastore_id": "7a0ca293-290b-9ed0-7a0c-a293290ba007",
        LOG.create-github-logfood:   "name": "mcp-services/system.ai.github",
        LOG.create-github-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-github-logfood: }

latest (v1.26.0):

acceptance_test.go:1374: Diff:
        --- cmd/aitools/mcp/add-text/output.txt
        +++ /tmp/TestAcceptcmdaitoolsmcpadd-text3418389416/002/output.txt
        @@ -2,73 +2,28 @@
         === Text output: Cursor approval hint, and the sign-in step for github
         
         >>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp,system.ai.github --agents claude-code,cursor
        -Profile logfood ([DATABRICKS_URL])
        -Agents (named with --agents): claude-code, cursor
        +Error: unknown flag: --names
         
        -genie_one_mcp-logfood  system.ai.genie_one_mcp
        -  claude-code  installed  [TEST_TMP_DIR]/home/.claude.json
        -  cursor       installed  [TEST_TMP_DIR]/home/.cursor/mcp.json
        -Cursor may ask you to approve genie_one_mcp-logfood. Run: cursor-agent mcp enable genie_one_mcp-logfood (the Cursor docs call the CLI agent), or approve it in Cursor's MCP settings.
        +Usage:
        +  databricks aitools [command]
         
        -github-logfood  system.ai.github
        -  claude-code  installed  [TEST_TMP_DIR]/home/.claude.json
        -  cursor       installed  [TEST_TMP_DIR]/home/.cursor/mcp.json
        -system.ai.github needs sign-in. Run: DATABRICKS_CONFIG_FILE=[TEST_TMP_DIR]/.databrickscfg databricks aitools mcp login system.ai.github --profile logfood
        -Cursor may ask you to approve github-logfood. Run: cursor-agent mcp enable github-logfood (the Cursor docs call the CLI agent), or approve it in Cursor's MCP settings.
        +Available Commands:
        +  install     Install Databricks skills and plugins for coding agents
        +  list        List installed skills and plugins
        +  uninstall   Uninstall Databricks skills and plugins
        +  update      Update installed skills and plugins
        +  version     Show installed skills and plugins version
         
        -=== Running it again re-asserts the entries, and nothing is written twice
        +Flags:
        +  -h, --help   help for aitools
         
        ->>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp,system.ai.github --agents claude-code,cursor
        -Profile logfood ([DATABRICKS_URL])
        -Agents (named with --agents): claude-code, cursor
        +Global Flags:
        +      --debug            enable debug logging
        +  -o, --output type      output type: text or json (default text)
        +  -p, --profile string   ~/.databrickscfg profile
        +  -t, --target string    bundle target to use (if applicable)
         
        -genie_one_mcp-logfood  system.ai.genie_one_mcp
        -  claude-code  already installed, re-asserted  [TEST_TMP_DIR]/home/.claude.json
        -  cursor       already installed, re-asserted  [TEST_TMP_DIR]/home/.cursor/mcp.json
        +Use "databricks aitools [command] --help" for more information about a command.
         
        -github-logfood  system.ai.github
        -  claude-code  already installed, re-asserted  [TEST_TMP_DIR]/home/.claude.json
        -  cursor       already installed, re-asserted  [TEST_TMP_DIR]/home/.cursor/mcp.json
        -system.ai.github needs sign-in. Run: DATABRICKS_CONFIG_FILE=[TEST_TMP_DIR]/.databrickscfg databricks aitools mcp login system.ai.github --profile logfood
         
        -=== Cursor config
        -
        ->>> cat home/.cursor/mcp.json
        -{
        -  "mcpServers": {
        -    "genie_one_mcp-logfood": {
        -      "args": [
        -        "aitools",
        -        "mcp",
        -        "serve",
        -        "--profile",
        -        "logfood",
        -        "--service",
        -        "system.ai.genie_one_mcp",
        -        "--agent",
        -        "cursor"
        -      ],
        -      "command": "[CLI]",
        -      "env": {
        -        "DATABRICKS_CONFIG_FILE": "[TEST_TMP_DIR]/.databrickscfg"
        -      }
        -    },
        -    "github-logfood": {
        -      "args": [
        -        "aitools",
        -        "mcp",
        -        "serve",
        -        "--profile",
        -        "logfood",
        -        "--service",
        -        "system.ai.github",
        -        "--agent",
        -        "cursor"
        -      ],
        -      "command": "[CLI]",
        -      "env": {
        -        "DATABRICKS_CONFIG_FILE": "[TEST_TMP_DIR]/.databrickscfg"
        -      }
        -    }
        -  }
        -}
        +Exit code: 1
        
    acceptance_test.go:1152: 
        LOG.create-genie_one_mcp-logfood: {
        LOG.create-genie_one_mcp-logfood:   "comment": "Ask questions about your data",
        LOG.create-genie_one_mcp-logfood:   "created_by": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "effective_owner": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "metastore_id": "7a0ca29a-ca84-62f8-7a0c-a29aca846460",
        LOG.create-genie_one_mcp-logfood:   "name": "mcp-services/system.ai.genie_one_mcp",
        LOG.create-genie_one_mcp-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-genie_one_mcp-logfood: }
    acceptance_test.go:1152: 
        LOG.create-github-logfood: {
        LOG.create-github-logfood:   "comment": "Repositories, issues, pull requests",
        LOG.create-github-logfood:   "config": {
        LOG.create-github-logfood:     "source_connection": {
        LOG.create-github-logfood:       "name": "connections/github",
        LOG.create-github-logfood:       "options": {
        LOG.create-github-logfood:         "oauth_provider": "OAUTH_PROVIDER_GITHUB_MCP"
        LOG.create-github-logfood:       }
        LOG.create-github-logfood:     }
        LOG.create-github-logfood:   },
        LOG.create-github-logfood:   "created_by": "tester@databricks.com",
        LOG.create-github-logfood:   "effective_owner": "tester@databricks.com",
        LOG.create-github-logfood:   "metastore_id": "7a0ca29a-cbc1-f7d4-7a0c-a29acbc1f98c",
        LOG.create-github-logfood:   "name": "mcp-services/system.ai.github",
        LOG.create-github-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-github-logfood: }
TestAccept/cmd/aitools/mcp/add-two-agents: branch ✅ | main (8e70c8b) ❌ | latest (v1.26.0) ❌

main (8e70c8b):

acceptance_test.go:1374: Diff:
        --- cmd/aitools/mcp/add-two-agents/output.txt
        +++ /tmp/TestAcceptcmdaitoolsmcpadd-two-agents3907585141/002/output.txt
        @@ -2,157 +2,28 @@
         === Two services for two agents, as JSON
         
         >>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp,system.ai.github --agents claude-code,codex --output json
        -{
        -  "profile": "logfood",
        -  "host": "[DATABRICKS_URL]",
        -  "scope": "global",
        -  "servers": [
        -    {
        -      "name": "genie_one_mcp-logfood",
        -      "service": "system.ai.genie_one_mcp",
        -      "sign_in_required": false,
        -      "agents": [
        -        {
        -          "name": "claude-code",
        -          "status": "installed",
        -          "path": "[TEST_TMP_DIR]/home/.claude.json"
        -        },
        -        {
        -          "name": "codex",
        -          "status": "installed",
        -          "path": "[TEST_TMP_DIR]/home/.codex/config.toml"
        -        }
        -      ]
        -    },
        -    {
        -      "name": "github-logfood",
        -      "service": "system.ai.github",
        -      "sign_in_required": true,
        -      "agents": [
        -        {
        -          "name": "claude-code",
        -          "status": "installed",
        -          "path": "[TEST_TMP_DIR]/home/.claude.json"
        -        },
        -        {
        -          "name": "codex",
        -          "status": "installed",
        -          "path": "[TEST_TMP_DIR]/home/.codex/config.toml"
        -        }
        -      ]
        -    }
        -  ],
        -  "error_category": "TYPE_UNSPECIFIED"
        -}
        +Error: unknown flag: --names
         
        -=== Claude Code config keeps the entries it already had and gets ours
        +Usage:
        +  databricks aitools [command]
         
        ->>> cat home/.claude.json
        -{
        -  "mcpServers": {
        -    "my-server": {
        -      "type": "stdio",
        -      "command": "my-server",
        -      "args": []
        -    },
        -    "genie_one_mcp-logfood": {
        -      "args": [
        -        "aitools",
        -        "mcp",
        -        "serve",
        -        "--profile",
        -        "logfood",
        -        "--service",
        -        "system.ai.genie_one_mcp",
        -        "--agent",
        -        "claude-code"
        -      ],
        -      "command": "[CLI]",
        -      "env": {
        -        "DATABRICKS_CONFIG_FILE": "[TEST_TMP_DIR]/.databrickscfg"
        -      },
        -      "type": "stdio"
        -    },
        -    "github-logfood": {
        -      "args": [
        -        "aitools",
        -        "mcp",
        -        "serve",
        -        "--profile",
        -        "logfood",
        -        "--service",
        -        "system.ai.github",
        -        "--agent",
        -        "claude-code"
        -      ],
        -      "command": "[CLI]",
        -      "env": {
        -        "DATABRICKS_CONFIG_FILE": "[TEST_TMP_DIR]/.databrickscfg"
        -      },
        -      "type": "stdio"
        -    }
        -  }
        -}
        +Available Commands:
        +  install     Install Databricks skills and plugins for coding agents
        +  list        List installed skills and plugins
        +  uninstall   Uninstall Databricks skills and plugins
        +  update      Update installed skills and plugins
        +  version     Show installed skills and plugins version
         
        -=== Codex config
        +Flags:
        +  -h, --help   help for aitools
         
        ->>> cat home/.codex/config.toml
        -[mcp_servers.genie_one_mcp-logfood]
        -args = ["aitools", "mcp", "serve", "--profile", "logfood", "--service", "system.ai.genie_one_mcp", "--agent", "codex"]
        -command = "[CLI]"
        -[mcp_servers.genie_one_mcp-logfood.env]
        -DATABRICKS_CONFIG_FILE = "[TEST_TMP_DIR]/.databrickscfg"
        +Global Flags:
        +      --debug            enable debug logging
        +  -o, --output type      output type: text or json (default text)
        +  -p, --profile string   ~/.databrickscfg profile
        +  -t, --target string    bundle target to use (if applicable)
         
        -[mcp_servers.github-logfood]
        -args = ["aitools", "mcp", "serve", "--profile", "logfood", "--service", "system.ai.github", "--agent", "codex"]
        -command = "[CLI]"
        -[mcp_servers.github-logfood.env]
        -DATABRICKS_CONFIG_FILE = "[TEST_TMP_DIR]/.databrickscfg"
        +Use "databricks aitools [command] --help" for more information about a command.
         
        -=== State records the binding, without the time and CLI version
         
        ->>> jq .mcp | del(.servers[].installed_at, .servers[].cli_version) home/.databricks/aitools/skills/.state.json
        -{
        -  "servers": [
        -    {
        -      "name": "genie_one_mcp-logfood",
        -      "service": "system.ai.genie_one_mcp",
        -      "profile": "logfood",
        -      "host": "[DATABRICKS_URL]",
        -      "workspace_id": "[WORKSPACE_ID]",
        -      "sign_in_required": false,
        -      "command": "[CLI]",
        -      "config_file": "[TEST_TMP_DIR]/.databrickscfg",
        -      "agents": {
        -        "claude-code": {
        -          "path": "[TEST_TMP_DIR]/home/.claude.json",
        -          "scope": "user"
        -        },
        -        "codex": {
        -          "path": "[TEST_TMP_DIR]/home/.codex/config.toml",
        -          "scope": "user"
        -        }
        -      }
        -    },
        -    {
        -      "name": "github-logfood",
        -      "service": "system.ai.github",
        -      "profile": "logfood",
        -      "host": "[DATABRICKS_URL]",
        -      "workspace_id": "[WORKSPACE_ID]",
        -      "sign_in_required": true,
        -      "command": "[CLI]",
        -      "config_file": "[TEST_TMP_DIR]/.databrickscfg",
        -      "agents": {
        -        "claude-code": {
        -          "path": "[TEST_TMP_DIR]/home/.claude.json",
        -          "scope": "user"
        -        },
        -        "codex": {
        -          "path": "[TEST_TMP_DIR]/home/.codex/config.toml",
        -          "scope": "user"
        -        }
        -      }
        -    }
        -  ]
        -}
        +Exit code: 1
        
    acceptance_test.go:1152: 
        LOG.create-genie_one_mcp-logfood: {
        LOG.create-genie_one_mcp-logfood:   "comment": "Ask questions about your data",
        LOG.create-genie_one_mcp-logfood:   "created_by": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "effective_owner": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "metastore_id": "7a0ca292-fe32-5afd-7a0c-a292fe325c15",
        LOG.create-genie_one_mcp-logfood:   "name": "mcp-services/system.ai.genie_one_mcp",
        LOG.create-genie_one_mcp-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-genie_one_mcp-logfood: }
    acceptance_test.go:1152: 
        LOG.create-github-logfood: {
        LOG.create-github-logfood:   "comment": "Repositories, issues, pull requests",
        LOG.create-github-logfood:   "config": {
        LOG.create-github-logfood:     "source_connection": {
        LOG.create-github-logfood:       "name": "connections/github",
        LOG.create-github-logfood:       "options": {
        LOG.create-github-logfood:         "oauth_provider": "OAUTH_PROVIDER_GITHUB_MCP"
        LOG.create-github-logfood:       }
        LOG.create-github-logfood:     }
        LOG.create-github-logfood:   },
... (6 more lines truncated, raise DATABRICKS_CLI_TEST_MAX_LOG to see more)

latest (v1.26.0):

acceptance_test.go:1374: Diff:
        --- cmd/aitools/mcp/add-two-agents/output.txt
        +++ /tmp/TestAcceptcmdaitoolsmcpadd-two-agents4231477997/002/output.txt
        @@ -2,157 +2,28 @@
         === Two services for two agents, as JSON
         
         >>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp,system.ai.github --agents claude-code,codex --output json
        -{
        -  "profile": "logfood",
        -  "host": "[DATABRICKS_URL]",
        -  "scope": "global",
        -  "servers": [
        -    {
        -      "name": "genie_one_mcp-logfood",
        -      "service": "system.ai.genie_one_mcp",
        -      "sign_in_required": false,
        -      "agents": [
        -        {
        -          "name": "claude-code",
        -          "status": "installed",
        -          "path": "[TEST_TMP_DIR]/home/.claude.json"
        -        },
        -        {
        -          "name": "codex",
        -          "status": "installed",
        -          "path": "[TEST_TMP_DIR]/home/.codex/config.toml"
        -        }
        -      ]
        -    },
        -    {
        -      "name": "github-logfood",
        -      "service": "system.ai.github",
        -      "sign_in_required": true,
        -      "agents": [
        -        {
        -          "name": "claude-code",
        -          "status": "installed",
        -          "path": "[TEST_TMP_DIR]/home/.claude.json"
        -        },
        -        {
        -          "name": "codex",
        -          "status": "installed",
        -          "path": "[TEST_TMP_DIR]/home/.codex/config.toml"
        -        }
        -      ]
        -    }
        -  ],
        -  "error_category": "TYPE_UNSPECIFIED"
        -}
        +Error: unknown flag: --names
         
        -=== Claude Code config keeps the entries it already had and gets ours
        +Usage:
        +  databricks aitools [command]
         
        ->>> cat home/.claude.json
        -{
        -  "mcpServers": {
        -    "my-server": {
        -      "type": "stdio",
        -      "command": "my-server",
        -      "args": []
        -    },
        -    "genie_one_mcp-logfood": {
        -      "args": [
        -        "aitools",
        -        "mcp",
        -        "serve",
        -        "--profile",
        -        "logfood",
        -        "--service",
        -        "system.ai.genie_one_mcp",
        -        "--agent",
        -        "claude-code"
        -      ],
        -      "command": "[CLI]",
        -      "env": {
        -        "DATABRICKS_CONFIG_FILE": "[TEST_TMP_DIR]/.databrickscfg"
        -      },
        -      "type": "stdio"
        -    },
        -    "github-logfood": {
        -      "args": [
        -        "aitools",
        -        "mcp",
        -        "serve",
        -        "--profile",
        -        "logfood",
        -        "--service",
        -        "system.ai.github",
        -        "--agent",
        -        "claude-code"
        -      ],
        -      "command": "[CLI]",
        -      "env": {
        -        "DATABRICKS_CONFIG_FILE": "[TEST_TMP_DIR]/.databrickscfg"
        -      },
        -      "type": "stdio"
        -    }
        -  }
        -}
        +Available Commands:
        +  install     Install Databricks skills and plugins for coding agents
        +  list        List installed skills and plugins
        +  uninstall   Uninstall Databricks skills and plugins
        +  update      Update installed skills and plugins
        +  version     Show installed skills and plugins version
         
        -=== Codex config
        +Flags:
        +  -h, --help   help for aitools
         
        ->>> cat home/.codex/config.toml
        -[mcp_servers.genie_one_mcp-logfood]
        -args = ["aitools", "mcp", "serve", "--profile", "logfood", "--service", "system.ai.genie_one_mcp", "--agent", "codex"]
        -command = "[CLI]"
        -[mcp_servers.genie_one_mcp-logfood.env]
        -DATABRICKS_CONFIG_FILE = "[TEST_TMP_DIR]/.databrickscfg"
        +Global Flags:
        +      --debug            enable debug logging
        +  -o, --output type      output type: text or json (default text)
        +  -p, --profile string   ~/.databrickscfg profile
        +  -t, --target string    bundle target to use (if applicable)
         
        -[mcp_servers.github-logfood]
        -args = ["aitools", "mcp", "serve", "--profile", "logfood", "--service", "system.ai.github", "--agent", "codex"]
        -command = "[CLI]"
        -[mcp_servers.github-logfood.env]
        -DATABRICKS_CONFIG_FILE = "[TEST_TMP_DIR]/.databrickscfg"
        +Use "databricks aitools [command] --help" for more information about a command.
         
        -=== State records the binding, without the time and CLI version
         
        ->>> jq .mcp | del(.servers[].installed_at, .servers[].cli_version) home/.databricks/aitools/skills/.state.json
        -{
        -  "servers": [
        -    {
        -      "name": "genie_one_mcp-logfood",
        -      "service": "system.ai.genie_one_mcp",
        -      "profile": "logfood",
        -      "host": "[DATABRICKS_URL]",
        -      "workspace_id": "[WORKSPACE_ID]",
        -      "sign_in_required": false,
        -      "command": "[CLI]",
        -      "config_file": "[TEST_TMP_DIR]/.databrickscfg",
        -      "agents": {
        -        "claude-code": {
        -          "path": "[TEST_TMP_DIR]/home/.claude.json",
        -          "scope": "user"
        -        },
        -        "codex": {
        -          "path": "[TEST_TMP_DIR]/home/.codex/config.toml",
        -          "scope": "user"
        -        }
        -      }
        -    },
        -    {
        -      "name": "github-logfood",
        -      "service": "system.ai.github",
        -      "profile": "logfood",
        -      "host": "[DATABRICKS_URL]",
        -      "workspace_id": "[WORKSPACE_ID]",
        -      "sign_in_required": true,
        -      "command": "[CLI]",
        -      "config_file": "[TEST_TMP_DIR]/.databrickscfg",
        -      "agents": {
        -        "claude-code": {
        -          "path": "[TEST_TMP_DIR]/home/.claude.json",
        -          "scope": "user"
        -        },
        -        "codex": {
        -          "path": "[TEST_TMP_DIR]/home/.codex/config.toml",
        -          "scope": "user"
        -        }
        -      }
        -    }
        -  ]
        -}
        +Exit code: 1
        
    acceptance_test.go:1152: 
        LOG.create-genie_one_mcp-logfood: {
        LOG.create-genie_one_mcp-logfood:   "comment": "Ask questions about your data",
        LOG.create-genie_one_mcp-logfood:   "created_by": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "effective_owner": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "metastore_id": "7a0ca29a-d110-42ec-7a0c-a29ad11043c8",
        LOG.create-genie_one_mcp-logfood:   "name": "mcp-services/system.ai.genie_one_mcp",
        LOG.create-genie_one_mcp-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-genie_one_mcp-logfood: }
    acceptance_test.go:1152: 
        LOG.create-github-logfood: {
        LOG.create-github-logfood:   "comment": "Repositories, issues, pull requests",
        LOG.create-github-logfood:   "config": {
        LOG.create-github-logfood:     "source_connection": {
        LOG.create-github-logfood:       "name": "connections/github",
        LOG.create-github-logfood:       "options": {
        LOG.create-github-logfood:         "oauth_provider": "OAUTH_PROVIDER_GITHUB_MCP"
        LOG.create-github-logfood:       }
        LOG.create-github-logfood:     }
        LOG.create-github-logfood:   },
... (6 more lines truncated, raise DATABRICKS_CLI_TEST_MAX_LOG to see more)
TestAccept/cmd/aitools/mcp/add-two-profiles: branch ✅ | main (8e70c8b) ❌ | latest (v1.26.0) ❌

main (8e70c8b):

acceptance_test.go:1374: Diff:
        --- cmd/aitools/mcp/add-two-profiles/output.txt
        +++ /tmp/TestAcceptcmdaitoolsmcpadd-two-profiles3921740966/002/output.txt
        @@ -2,52 +2,28 @@
         === The same service for a second profile adds an entry and renames nothing
         
         >>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp --agents claude-code
        -Profile logfood ([DATABRICKS_URL])
        -Agents (named with --agents): claude-code
        +Error: unknown flag: --names
         
        -genie_one_mcp-logfood  system.ai.genie_one_mcp
        -  claude-code  installed  [TEST_TMP_DIR]/home/.claude.json
        +Usage:
        +  databricks aitools [command]
         
        ->>> [CLI] aitools mcp add --profile dogfood --names system.ai.genie_one_mcp --agents claude-code --output json
        -{
        -  "profile": "dogfood",
        -  "servers": [
        -    {
        -      "name": "genie_one_mcp-dogfood",
        -      "agents": [
        -        {
        -          "name": "claude-code",
        -          "status": "installed"
        -        }
        -      ]
        -    }
        -  ]
        -}
        +Available Commands:
        +  install     Install Databricks skills and plugins for coding agents
        +  list        List installed skills and plugins
        +  uninstall   Uninstall Databricks skills and plugins
        +  update      Update installed skills and plugins
        +  version     Show installed skills and plugins version
         
        -=== Both entries are in the Claude Code config
        +Flags:
        +  -h, --help   help for aitools
         
        ->>> jq .mcpServers | keys home/.claude.json
        -[
        -  "genie_one_mcp-dogfood",
        -  "genie_one_mcp-logfood"
        -]
        +Global Flags:
        +      --debug            enable debug logging
        +  -o, --output type      output type: text or json (default text)
        +  -p, --profile string   ~/.databrickscfg profile
        +  -t, --target string    bundle target to use (if applicable)
         
        -=== State holds one entry per profile
        +Use "databricks aitools [command] --help" for more information about a command.
         
        ->>> jq .mcp | [.servers[] | {name, profile, agents: (.agents | keys)}] home/.databricks/aitools/skills/.state.json
        -[
        -  {
        -    "name": "genie_one_mcp-logfood",
        -    "profile": "logfood",
        -    "agents": [
        -      "claude-code"
        -    ]
        -  },
        -  {
        -    "name": "genie_one_mcp-dogfood",
        -    "profile": "dogfood",
        -    "agents": [
        -      "claude-code"
        -    ]
        -  }
        -]
        +
        +Exit code: 1
        
    acceptance_test.go:1152: 
        LOG.create-genie_one_mcp-dogfood: {
        LOG.create-genie_one_mcp-dogfood:   "comment": "Ask questions about your data",
        LOG.create-genie_one_mcp-dogfood:   "created_by": "tester@databricks.com",
        LOG.create-genie_one_mcp-dogfood:   "effective_owner": "tester@databricks.com",
        LOG.create-genie_one_mcp-dogfood:   "metastore_id": "7a0ca293-1a17-9c96-7a0c-a2931a179deb",
        LOG.create-genie_one_mcp-dogfood:   "name": "mcp-services/system.ai.genie_one_mcp",
        LOG.create-genie_one_mcp-dogfood:   "updated_by": "tester@databricks.com"
        LOG.create-genie_one_mcp-dogfood: }
    acceptance_test.go:1152: 
        LOG.create-genie_one_mcp-logfood: {
        LOG.create-genie_one_mcp-logfood:   "comment": "Ask questions about your data",
        LOG.create-genie_one_mcp-logfood:   "created_by": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "effective_owner": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "metastore_id": "7a0ca293-18b9-ae64-7a0c-a29318b9af68",
        LOG.create-genie_one_mcp-logfood:   "name": "mcp-services/system.ai.genie_one_mcp",
        LOG.create-genie_one_mcp-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-genie_one_mcp-logfood: }

latest (v1.26.0):

acceptance_test.go:1374: Diff:
        --- cmd/aitools/mcp/add-two-profiles/output.txt
        +++ /tmp/TestAcceptcmdaitoolsmcpadd-two-profiles706782298/002/output.txt
        @@ -2,52 +2,28 @@
         === The same service for a second profile adds an entry and renames nothing
         
         >>> [CLI] aitools mcp add --profile logfood --names system.ai.genie_one_mcp --agents claude-code
        -Profile logfood ([DATABRICKS_URL])
        -Agents (named with --agents): claude-code
        +Error: unknown flag: --names
         
        -genie_one_mcp-logfood  system.ai.genie_one_mcp
        -  claude-code  installed  [TEST_TMP_DIR]/home/.claude.json
        +Usage:
        +  databricks aitools [command]
         
        ->>> [CLI] aitools mcp add --profile dogfood --names system.ai.genie_one_mcp --agents claude-code --output json
        -{
        -  "profile": "dogfood",
        -  "servers": [
        -    {
        -      "name": "genie_one_mcp-dogfood",
        -      "agents": [
        -        {
        -          "name": "claude-code",
        -          "status": "installed"
        -        }
        -      ]
        -    }
        -  ]
        -}
        +Available Commands:
        +  install     Install Databricks skills and plugins for coding agents
        +  list        List installed skills and plugins
        +  uninstall   Uninstall Databricks skills and plugins
        +  update      Update installed skills and plugins
        +  version     Show installed skills and plugins version
         
        -=== Both entries are in the Claude Code config
        +Flags:
        +  -h, --help   help for aitools
         
        ->>> jq .mcpServers | keys home/.claude.json
        -[
        -  "genie_one_mcp-dogfood",
        -  "genie_one_mcp-logfood"
        -]
        +Global Flags:
        +      --debug            enable debug logging
        +  -o, --output type      output type: text or json (default text)
        +  -p, --profile string   ~/.databrickscfg profile
        +  -t, --target string    bundle target to use (if applicable)
         
        -=== State holds one entry per profile
        +Use "databricks aitools [command] --help" for more information about a command.
         
        ->>> jq .mcp | [.servers[] | {name, profile, agents: (.agents | keys)}] home/.databricks/aitools/skills/.state.json
        -[
        -  {
        -    "name": "genie_one_mcp-logfood",
        -    "profile": "logfood",
        -    "agents": [
        -      "claude-code"
        -    ]
        -  },
        -  {
        -    "name": "genie_one_mcp-dogfood",
        -    "profile": "dogfood",
        -    "agents": [
        -      "claude-code"
        -    ]
        -  }
        -]
        +
        +Exit code: 1
        
    acceptance_test.go:1152: 
        LOG.create-genie_one_mcp-dogfood: {
        LOG.create-genie_one_mcp-dogfood:   "comment": "Ask questions about your data",
        LOG.create-genie_one_mcp-dogfood:   "created_by": "tester@databricks.com",
        LOG.create-genie_one_mcp-dogfood:   "effective_owner": "tester@databricks.com",
        LOG.create-genie_one_mcp-dogfood:   "metastore_id": "7a0ca29a-b83f-0444-7a0c-a29ab83f0566",
        LOG.create-genie_one_mcp-dogfood:   "name": "mcp-services/system.ai.genie_one_mcp",
        LOG.create-genie_one_mcp-dogfood:   "updated_by": "tester@databricks.com"
        LOG.create-genie_one_mcp-dogfood: }
    acceptance_test.go:1152: 
        LOG.create-genie_one_mcp-logfood: {
        LOG.create-genie_one_mcp-logfood:   "comment": "Ask questions about your data",
        LOG.create-genie_one_mcp-logfood:   "created_by": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "effective_owner": "tester@databricks.com",
        LOG.create-genie_one_mcp-logfood:   "metastore_id": "7a0ca29a-b721-05d1-7a0c-a29ab7210730",
        LOG.create-genie_one_mcp-logfood:   "name": "mcp-services/system.ai.genie_one_mcp",
        LOG.create-genie_one_mcp-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-genie_one_mcp-logfood: }

9 more failure details omitted, see the full report in the run.

@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Integration test report

Commit: 4a8c08c

Run: 43449421001

Env 💚​RECOVERED ✅​pass 🙈​skip Time
💚​ aws linux-2core-8gb 1 270 29 11:37
💚​ aws-windows-latest-4core-16gb 1 272 27 8:32
💚​ azure linux-2core-8gb 1 269 29 10:24
💚​ azure-windows-latest-4core-16gb 1 271 27 8:23
💚​ gcp linux-2core-8gb 1 270 29 12:06
💚​ gcp-windows-latest-4core-16gb 1 272 27 8:54
Test Name aws linux-2core-8gb aws-windows-latest-4core-16gb azure linux-2core-8gb azure-windows-latest-4core-16gb gcp linux-2core-8gb gcp-windows-latest-4core-16gb
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
Top 9 slowest tests (at least 2 minutes):
duration env testname
3:36 azure-windows-latest-4core-16gb TestAccept
3:35 gcp-windows-latest-4core-16gb TestAccept
3:35 aws-windows-latest-4core-16gb TestAccept
2:53 azure-windows-latest-4core-16gb TestFilerWorkspaceFilesExtensionsRead
2:40 aws-windows-latest-4core-16gb TestFilerRecursiveDelete/workspace_files
2:30 aws linux-2core-8gb TestFilerWorkspaceNotebook/pyNb.py
2:01 azure linux-2core-8gb TestFilerWorkspaceNotebook/scalaJupyterNb.ipynb
2:01 azure-windows-latest-4core-16gb TestImportDirDoesNotOverwrite
2:00 gcp linux-2core-8gb TestFilerWorkspaceFilesExtensionsReadDir

@simonfaltum
simonfaltum force-pushed the aitools-mcp/06b-serve branch from eec9d1a to 5b1d279 Compare November 12, 2026 14:37
@simonfaltum
simonfaltum force-pushed the aitools-mcp/06b-serve branch from 5b1d279 to 9187003 Compare November 12, 2026 17:10
@simonfaltum
simonfaltum force-pushed the aitools-mcp/06b-serve branch from 9187003 to 48fac6f Compare November 13, 2026 00:15
@simonfaltum
simonfaltum force-pushed the aitools-mcp/07-login branch 2 times, most recently from 0aa7987 to a08bc42 Compare November 13, 2026 07:16
simonfaltum and others added 17 commits November 13, 2026 14:48
Provider sign-in for the external MCP services (GitHub, Slack and others).
login opens the sign-in page, prints its URL first and polls the caller's
credential until it is ACTIVE. logout deletes the credential and treats a
missing one as success. add gains --login and --no-login, and list gains
--check, which mints a token per profile, reads sign-in state and checks
the recorded binary.

The testserver serves the user-credentials GET, POST and DELETE routes.
A POST completes a sign-in, which the acceptance scripts use in place of
the browser.

Co-authored-by: Isaac <no-reply@databricks.com>
…nce cases

mcp add now refuses an agent named with --agents whose config directory
is missing. These two cases add for claude-code in a fresh HOME, so they
create the directory first. Their goldens do not change.

Co-authored-by: Isaac <no-reply@databricks.com>
The note ends with a period since "Tidy MCP discovery comments, notes
and error mapping" on the discovery branch. The stack now includes that
commit, so the golden shows the period. Nothing else in the output
changes.

Co-authored-by: Isaac <no-reply@databricks.com>
Card 03 moved MCP records into the `mcp` section of
~/.databricks/aitools/skills/.state.json. The list-check and
login-flips-active scripts read or edited the old path. The login
golden changes only in the echoed jq command.

Co-authored-by: Isaac <no-reply@databricks.com>
The sign-in acceptance tests stubbed the service GET with [[Server]] entries, but `add` also lists the services
of system.ai, and the stubs do not answer the list. Create the services in the fake workspace from the scripts,
as the other MCP acceptance tests do.

The sign-in state is kept per workspace, and tests without stubs share one fake workspace per token. Give each
sign-in test its own token, so that one test's sign-in cannot end another test's wait.

Co-authored-by: Isaac <no-reply@databricks.com>
The add help lists the new --login and --no-login flags. The list output is the one the list rewrite produces: profiles
name their config file when their servers are pinned to one, the JSON carries each server's host and status, and
an unchanged agent entry is installed even when the recorded binary is gone, which the binary check reports.

Co-authored-by: Isaac <no-reply@databricks.com>
Refactor without behavior change: SignIn takes the sign-in URL the caller builds, and no longer
takes the host and workspace ID separately; the URL is built where it is printed and recorded.
binaryStatus resolves the recorded command with binaryPathFor, so it shares the lookup seam.
The full-name check, the agent-reached check, and the unexported token field of the list
profile also reuse the existing helpers.

Co-authored-by: Isaac <no-reply@databricks.com>
login and logout check the profile binding before any request, as add and serve do, so a
profile whose host or workspace changed is refused with the same message, and a missing
profile gets add's wording. login names the profile's current workspace in the sign-in URL.

logout looks the service up first: a service the profile cannot see is an error instead of
'not signed in'. A service whose source connection was deleted is not found, as Discover drops it.
Failures from the API are described as add describes them, so a rejected credential is
AUTH_FAILED with the login command.

The fake answers a sign-in to a service that does not exist with NOT_FOUND.

Co-authored-by: Isaac <no-reply@databricks.com>
…ttempt

A FAILED credential from an earlier sign-in is no longer the result of a new one: FAILED counts
only once the state has changed since the attempt started. The wait is cancelled after the
timeout, which also cuts off a poll that is still in flight, so the command ends at the timeout
rather than when the SDK's retries of that poll end.

Co-authored-by: Isaac <no-reply@databricks.com>
add --login attempts each installed sign-in server, so one failed sign-in no longer leaves the
later servers unsigned. Each server that failed gets the login command, as a server that was not
signed in by a plain add does. The sign-in failures are joined with the agent failures, so the
exit status and the JSON error both report them. topLevelFailure keeps the failures that are
not per-agent when a run error joins both; install and remove never join them, so they are unchanged.

Co-authored-by: Isaac <no-reply@databricks.com>
…s own file

Each profile group is checked with its own config file, in the context and in the process
environment the token store reads, and the environment is restored afterwards. Two groups of
one profile name in two files no longer share the ambient file's token store.

A token that cannot be minted, or a sign-in that cannot be read, is a warning that names the
profile or server and says why, and the result stays unknown. A service the profile can no
longer see is a warning with unknown sign-in, not a sign-in that is needed.

Co-authored-by: Isaac <no-reply@databricks.com>
The sign-in hints shell-quote the service and profile they print, and share one login command.
The sign-in notice names the profile only when the page is about to open, so a login that is
already signed in does not print it. The sign-in error messages start lowercase, as the package's
other errors do. list's --check comments and command docs no longer cite the design document,
and the list command documents that only --check makes network calls.

Co-authored-by: Isaac <no-reply@databricks.com>
…wait longer for the background sign-in

Git Bash on Windows rewrites the /api/ path of the credential POSTs unless MSYS_NO_PATHCONV is set.
The login acceptance test waits for the background command's sign-in line with a retry budget of
30 seconds, instead of the default 2.5 seconds.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
The SDK puts a resource name into the request path unescaped. A service name
with a '#' or '?' cut the path short, so logout of such a name sent DELETE to
the service itself. The service part of each request is now escaped, and
PathSafeName reports the names that change a path.
A service name with "/", "?", "#", "%" or a control character is refused
before any request, with the same message as a name without three parts.

Co-authored-by: Isaac <no-reply@databricks.com>
The endpoint already escapes the name. This refuses the same names as login
and logout before serve reads the state, so the proxy never starts for them.

Co-authored-by: Isaac <no-reply@databricks.com>
simonfaltum and others added 28 commits November 13, 2026 14:49
ConfigFileCommand is the one place a pinned config file enters a hint. On Windows the
file was printed with backslashes, which broke the golden outputs and the rule that hints
use forward slashes.

Co-authored-by: Isaac <no-reply@databricks.com>
…d backgrounds a child

The default opener swaps the output streams of pkg/browser for each open, so two opens that
overlap (sign-in can start one while add --login starts another) could leave the streams swapped.
A mutex now covers the swap and the open.

A custom BROWSER command ran with piped stdout and stderr that were copied to stderr before Wait.
A command that starts a child and returns, such as one that backgrounds the browser, kept the pipes
open, so the copy never ended and the call hung. The command's output now goes to stderr directly,
as the streams of the inherited output do, and stdin is inherited as it is for Open.

Co-authored-by: Isaac <no-reply@databricks.com>
… failure

Serve and Bind passed an empty pinned file to the auth error, so the login command for a server pinned
to a config file was bare and logged in to the file the shell reads. A new helper, PinnedFile, gives
the file for an entry pinned to one, and the sign-in command uses it too.

Co-authored-by: Isaac <no-reply@databricks.com>
…-check warning

The rejected-token warning for an unpinned profile gave a bare login command, which writes the file
DATABRICKS_CONFIG_FILE names. The file is now derived from the caller's context before the check
sets the environment to the profile's file, as the drift hints already do.

Co-authored-by: Isaac <no-reply@databricks.com>
…r see

Logout looked up the service before it deleted the credential, so a credential for a service whose
source connection was deleted or unshared could not be removed. The credential is deleted first. The
service is looked up only when there is no credential, to tell a name that is not a service from a
caller who is not signed in.

Co-authored-by: Isaac <no-reply@databricks.com>
…t fix

The hint after a failed sign-in in add --login was printed for every failure except a few, so a
refused sign-in (PERMISSION_DENIED) or a cancelled command got a login hint that would not fix it,
and the hint was printed in JSON mode, unlike the other hints. The timeout and failed errors already
name the login command, so the hint is removed.

Co-authored-by: Isaac <no-reply@databricks.com>
… no longer shows

A profile whose binding drifted is not checked, and its text listing gave no sign of that. The text now
says the check was skipped and why, only when --check ran. A server that never needed sign-in gets no
sign-in value when its service cannot be read, and the warning is the only report; a server that did
need sign-in stays unknown.

Co-authored-by: Isaac <no-reply@databricks.com>
…opener-failure test

TestSignInPollsUntilActive read a slice the opener goroutine wrote, with no synchronization. The URL now
arrives on a buffered channel. TestSignInReportsOpenerFailure depended on the poll interval winning
against the opener goroutine. The credential now stays NOT_FOUND until the failure is printed, so the
wait cannot end first.

Co-authored-by: Isaac <no-reply@databricks.com>
… rule

The doc said such names are refused, while the library escapes the names it reads from state.
The commands refuse them before any request, and the library escapes the others.

Co-authored-by: Isaac <no-reply@databricks.com>
… sign-in already active

login and logout set the profile in the JSON document only after the state is read, so a failure to read
it left the profile out. The profile is now set once it is resolved.

A login when the credential is already ACTIVE printed 'Signed in' and the sign-in URL, although no page
was opened. SignIn now reports that the caller was already signed in. The command says 'Already signed
in' and leaves the URL out of the document, and add --login says the same.

Co-authored-by: Isaac <no-reply@databricks.com>
…the stream and rate limiter sources

Doc comments for runMcpSignIn, mcpLogin and mcpLogout say what each step records. The state checks in
SignIn are switches. mcpLoginCommand moves to the top of its file. The comments about pkg/browser's
streams and the SDK's rate limiter link to the source they describe.

Co-authored-by: Isaac <no-reply@databricks.com>
The exhaustive linter requires every ProvisioningInfoState value in a switch. The states the
sign-in does not act on are listed with the wait.

Co-authored-by: Isaac <no-reply@databricks.com>
The overlapping-opens test relied on an empty PATH to stop the default
opener, which does not hold on Windows, where pkg/browser uses
ShellExecute. The launcher is now a variable that the tests replace, so
no browser opens on any OS. The stream restore in the test helper runs
in a defer, and the test with a backgrounded child waits for that child.

Co-authored-by: Isaac <no-reply@databricks.com>
The default opener sent the launcher's error output to io.Discard. exec
then copies that output through a pipe, so Run waited for every process
still holding the pipe, including the browser the launcher started. The
opener held its mutex for that whole time, so a second open, such as
the second sign-in of add --login, waited too and printed nothing.
The error output now goes to os.DevNull, a file that exec passes
through directly. The test stubs the launcher with a command that
leaves a child running.

Co-authored-by: Isaac <no-reply@databricks.com>
The test checks that the command's stdout reaches the process's stderr
and stays off stdout. The doc said that a command holding its output
does not hold up Wait. That is true, but for the reason that the output
is not piped, so the doc now says that.

Co-authored-by: Isaac <no-reply@databricks.com>
A poll of the credential that failed with a server error or a network
failure ended the sign-in, while the user may still be on the provider's
page. Those failures are now polled again until the wait ends. Any other
failed poll, such as a refused credential, still ends it at once.

The timeout is now SignInTimeoutError, which carries the failure of the
last poll that failed, and the timeout message names it. A poll that
reads the credential clears it.

Co-authored-by: Isaac <no-reply@databricks.com>
The JSON of login and of add --login named the URL whenever the sign-in
was not already active, including when it failed before the page was
printed, such as when the credential cannot be read. A URL that was
never shown is not a way to finish the sign-in. SignIn now reports
whether the URL was printed, and the URL is named only then.

Co-authored-by: Isaac <no-reply@databricks.com>
The text listing said 'sign-in needed' without the login command that
add prints for the same state. The server line now has a line below it
with the login command, pinned to the config file the server is pinned
to, as the other hints are.

Co-authored-by: Isaac <no-reply@databricks.com>
…kens failed

Any error from minting a profile's token marked the token failed, so
an unreachable host, such as an offline laptop, was told to log in
again. A token is now failed only when the credential is refused, and
unknown otherwise, with the warning kept.

The checks of a profile also ran on the SDK's five-minute retry budget,
one profile after another, so one host that did not answer held the
listing for minutes. Each profile's checks now end after the same
five-second bound that cmd/auth gives profile validation, and the SDK's
request and retry budgets for the profile are bounded too, since
resolving the profile does not take the check's context.

Co-authored-by: Isaac <no-reply@databricks.com>
A profile with a drifted server was not checked at all, so a server that
still matched its profile, added after the host changed, was reported as
not matching it. Each server is now skipped on its own status, and says
why in the text and in its JSON check: drifted, when the profile no longer
matches the server, or unknown, when the server could not be read. A
profile with no server to check is not contacted.

Co-authored-by: Isaac <no-reply@databricks.com>
…ument

The document of add --login had no sign-in per server. The successful,
already signed in and skipped sign-ins were not visible at all, and a
failure appeared only in the top-level error, which joins the failures
of every server. Each server now has a sign_in object with the state
that login reports, and the error category and message when the sign-in
failed. It is absent without --login, and for a server that reached no
agent, where no sign-in was attempted.

Co-authored-by: Isaac <no-reply@databricks.com>
The profile carried its own copy of the token check, which every server
of the profile repeats in its check. The text now reads the token from
the checked servers, and the profile keeps no copy. No output changes.

Co-authored-by: Isaac <no-reply@databricks.com>
…date_file.py

The setup posts of the list-check and login-flips-active scripts sent
their output to /dev/null, where a failure would not show. They write
LOG files, as the other aitools scripts do. The list-check script edited
the state with inline jq; it now uses update_file.py, with the binary path
JSON-escaped so the replacement matches on Windows too. Both servers of
that scenario record the binary, so both now report it missing.

Co-authored-by: Isaac <no-reply@databricks.com>
The two login hint tests and the two browser stdout tests are table
tests now. The doc of signInHint describes the sentence it returns, not
a command. openDefault and openWithCommand have doc comments, and the
long comments of the sign-in wait and of signInStatus are shorter. No
behavior changes.

Co-authored-by: Isaac <no-reply@databricks.com>
The helper returns the timeout error for the test's later assertions.
The one test that only needs the error to be a timeout discards it
explicitly, which the errcheck linter accepts.

Co-authored-by: Isaac <no-reply@databricks.com>
The time limit covered every request of a profile together: its token check
plus two requests per sign-in server. A profile with several sign-in servers
on a slow but healthy workspace ran out of time and reported its later
servers as unknown. Each request now gets the limit on its own, so a host
that never answers still ends the profile's checks.

Co-authored-by: Isaac <no-reply@databricks.com>
A plain error is now a transient token failure, so the guard tests that
expect the proxy to stop use a rejected token, and serveProxy takes the
pinned config file in the tests added for the transient case.

Co-authored-by: Isaac <no-reply@databricks.com>
Main classifies a StateError as STATE_INVALID, so a state file that does not
parse reports that category in the JSON sign-in output rather than
UNCATEGORIZED.

Co-authored-by: Isaac <no-reply@databricks.com>

This branch has not been deployed

No deployments
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.

2 participants