Skip to content

Add aitools mcp serve, the stdio proxy for managed MCP servers - #7550

Draft
simonfaltum wants to merge 32 commits into
aitools-mcp/06a-bridgefrom
aitools-mcp/06b-serve
Draft

simonfaltum wants to merge 32 commits into
aitools-mcp/06a-bridgefrom
aitools-mcp/06b-serve

Conversation

@simonfaltum

@simonfaltum simonfaltum commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

This pull request is part of a stack that adds the hidden databricks aitools mcp command group. It builds on #7540 and adds aitools mcp serve, the stdio command that each installed MCP entry runs.

Changes

  • aitools mcp serve --profile <p> --service <full name> [--agent <name>], hidden. It runs the binding checks, then bridges stdin and stdout to the service endpoint with the profile's credentials.
  • Stdout carries only JSON-RPC, so --output and a log file on stdout are refused.
  • Binding and auth failures exit 2. mcp add still exits 1 on a binding failure.
  • A rejected token fails that request with the login hint. Later requests fail at once, and serve prints one final line and exits 2.
  • A transient token failure (a network error, a timeout, a 5xx from the token endpoint) fails only that request, with no login hint, since a login would not fix it. Serve keeps running and the next request asks for a token again.
  • libs/aitools/mcp/proxy: Authenticate may return ErrSkipRequest to skip the session DELETE.
  • cmd/root: ExitStatusError, which ExitStatus honors. Execute closes the log file and restores the default logger and standard log output on return, also after a panic.
  • libs/testserver: the fake MCP endpoint answers 404 for unknown services and matches names without regard to case.

Why

Each entry that mcp add writes runs aitools mcp serve. It binds to one profile before it reads stdin, so a profile that points elsewhere at start fails with a message instead of sending a token to another host.

Decisions to review

  • Only a rejected token stops the proxy. A rejection is one that a new login fixes: a 400 or 401 from the token endpoint with invalid_grant or invalid_client, or the CLI's U2M client reporting an invalid refresh token. Everything else fails only its request, including network errors, timeouts, 5xx responses, and anything the check cannot classify. The reason is a laptop that wakes before the network is back: stopping there leaves the agent with a dead server until the entry restarts. The cost is that an unclassified error keeps failing requests instead of stopping serve.
  • Known limitation: the binding is checked only at start. A profile re-pointed to another host during a session, for example by databricks auth login --profile P --host B, keeps sending its token to the host serve was bound to. The command's doc comment says so.
  • A log file that is also stderr is allowed, so --log-file stdout works on a Unix terminal. Windows refuses it even in a terminal, because a console is not a regular file there.
  • If the client cancels the request that hit a rejected token before the bridge logs it, the login hint is lost. The final line still says the proxy stopped.
  • An unknown --agent is accepted and left out of the User-Agent, since a later release may write agents this binary does not know.

Tests

  • go test -race on cmd/aitools, cmd/root, libs/aitools and libs/testserver, also through the repo's gotestsum runner.
  • cmd/root: the stdout refusal and check, including a closed stderr; logger restore after a panic and a normal return.
  • cmd/aitools: a rejected token stops the proxy with exit 2, the hint, and no later token request. A transient failure fails only its request, and the next request succeeds while serve runs to stdin close (exit 0). TestCredentialRejected checks each error shape.
  • Acceptance serve-passthrough checks each stdout line is one JSON-RPC message; serve-binding-errors checks exit 2 and the stderr text.

Not tested: a real token refresh end to end, because the fake cannot fail one. The typed U2M case has no exported constructor, so the tests do not reach it. Windows: only go vet and a test build ran, so the console behavior is from reading the code.

This pull request and its description were written by Isaac.

@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Commit: 10450c9ed | Workflow run

Regression test report

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

12 tests: regression=12 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/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-installed1183708881/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": "7a0ca28a-63ea-5f2b-7a0c-a28a63ea6049",
        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-installed403882537/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": "7a0ca28b-4533-fa72-7a0c-a28b4533fb98",
        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-config3549267729/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": "7a0ca28a-aef2-7957-7a0c-a28aaef27a93",
        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-config554936917/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": "7a0ca28b-84c8-98d5-7a0c-a28b84c899f3",
        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-text1463399618/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 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 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": "7a0ca28a-ad3c-d1fb-7a0c-a28aad3cd336",
        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": "7a0ca28a-af5d-3dfb-7a0c-a28aaf5d3f5f",
        LOG.create-github-logfood:   "name": "mcp-services/system.ai.github",
        LOG.create-github-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-github-logfood: }
FAIL

latest (v1.26.0):

acceptance_test.go:1374: Diff:
        --- cmd/aitools/mcp/add-text/output.txt
        +++ /tmp/TestAcceptcmdaitoolsmcpadd-text2942564782/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 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 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": "7a0ca28b-8389-02d6-7a0c-a28b8389042e",
        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": "7a0ca28b-8564-707b-7a0c-a28b856471ac",
        LOG.create-github-logfood:   "name": "mcp-services/system.ai.github",
        LOG.create-github-logfood:   "updated_by": "tester@databricks.com"
        LOG.create-github-logfood: }
FAIL
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-agents3808353927/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": "7a0ca28a-9950-4506-7a0c-a28a9950463e",
        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-agents419983047/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": "7a0ca28b-7b65-5a88-7a0c-a28b7b655ba4",
        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-profiles2699421804/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": "7a0ca28a-a5dc-d2e6-7a0c-a28aa5dcd3f3",
        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": "7a0ca28a-a3fc-5662-7a0c-a28aa3fc57b4",
        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-profiles2392194586/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": "7a0ca28b-7b90-e82c-7a0c-a28b7b90e906",
        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": "7a0ca28b-7978-86d8-7a0c-a28b797887f8",
        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: }

7 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: 10450c9

Run: 43449424524

Env 💚​RECOVERED ✅​pass 🙈​skip Time
💚​ aws linux-2core-8gb 1 270 27 12:25
💚​ aws-windows-latest-4core-16gb 1 272 25 8:14
💚​ azure linux-2core-8gb 1 269 27 10:34
💚​ azure-windows-latest-4core-16gb 1 271 25 7:56
💚​ gcp linux-2core-8gb 1 270 27 11:45
💚​ gcp-windows-latest-4core-16gb 1 272 25 9:13
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:34 azure-windows-latest-4core-16gb TestAccept
3:34 aws-windows-latest-4core-16gb TestAccept
3:33 gcp-windows-latest-4core-16gb TestAccept
2:31 aws linux-2core-8gb TestFilerWorkspaceFilesExtensionsReadDir
2:23 gcp-windows-latest-4core-16gb TestFilerWorkspaceFilesExtensionsReadDir
2:16 azure linux-2core-8gb TestLock
2:10 gcp linux-2core-8gb TestImportDirDoesNotOverwrite
2:10 azure-windows-latest-4core-16gb TestFilerRecursiveDelete/workspace_files
2:06 aws linux-2core-8gb TestFilerRecursiveDelete/workspace_files

simonfaltum and others added 18 commits November 13, 2026 14:48
`databricks aitools mcp serve --profile <p> --service <full name>` is the
command each installed MCP entry runs. It binds to the profile, refusing to
start when the profile is gone, re-pointed at another host, or cannot mint
a token (exit 2 with the message on stderr), then bridges newline-delimited
JSON-RPC on stdin/stdout to the service endpoint.

- Binding runs before stdin is read; the bound config is set on the command
  context so the exit-time telemetry upload authenticates with it.
- The proxy sets the User-Agent extras aitools-mcp/proxy and
  aitools-mcp-client/<agent>, plus the workspace routing header, and appends
  the bound workspace and profile to the initialize instructions.
- A token refresh failure mid-session answers that request with an error
  naming the profile, then exits 2.
- The root command skips the stale-version warning for serve, and the exit
  status now comes from root.ExitStatus through main.
- Fake /ai-gateway/mcp-services/{name} endpoint in testserver, with
  acceptance tests for stdout purity and binding errors.

Co-authored-by: Isaac <no-reply@databricks.com>
Stdout carries the MCP protocol for serve, so an explicit --log-file stdout
would mix log lines into it. serve now fails at flag validation, as it does
for --output, and the binding-errors scenario covers both flags.

Co-authored-by: Isaac <no-reply@databricks.com>
macOS wc pads its count with spaces, so the golden differed from Linux.
Strip the padding the way acceptance/ssh/bulk-transfer does.

Co-authored-by: Isaac <no-reply@databricks.com>
Card 03 moved MCP records into the `mcp` section of
~/.databricks/aitools/skills/.state.json. serve-binding-errors wrote a
v1 file at the old path, so the bind found no record and the proxy
started. The fixture now writes the new file and pins its record to the
test's DATABRICKS_CONFIG_FILE, as `add` does; otherwise card 03 reports
a config-file mismatch before the host change.

Golden changes, all from card 03:
- The PROFILE_NOT_FOUND text names the config path as resolved (absolute)
  and has one space before "or remove", not three.
- The PROFILE_HOST_CHANGED text and the no-record notice have no trailing
  period.

Co-authored-by: Isaac <no-reply@databricks.com>
The proxy bridge now takes an http.RoundTripper. A nil one means
http.DefaultTransport, whose dialer waits 30 s. serve passes a transport
that keeps the profile's proxy and TLS settings, as cmd/apps/logs.go does,
and dials with a 10 s connect timeout.

Co-authored-by: Isaac <no-reply@databricks.com>
Card 03 and 04 changed the binding messages. A profile with no state
record now says the server needs the profile and how to set it up. A host
change names the server in the remove hint, not the profile.

Co-authored-by: Isaac <no-reply@databricks.com>
ExitStatus honors an error with an ExitStatus() int method, found with errors.AsType, so
serve no longer wraps its own status error. The method is not named ExitCode because os/exec's
ExitError has that method, and a child process's code would become the CLI's exit status.
mcp.BindError renames ExitCode to ExitStatus to match. The refresh failure is the AUTH_FAILED
BindError, so it exits 2 as the binding failures do.

The failed-execution log lines and the telemetry exit code now come from the same status, so the
log no longer records exit_code 1 for an exit 2.

Co-authored-by: Isaac <no-reply@databricks.com>
A command annotated with root.StdoutCarriesProtocol gets its log destination checked before the log
file is opened. A log file that is stdout, by name or by identity (/dev/stdout), is refused, so the
protocol on stdout is never shared with a log line. The stale-version warning is skipped for such
commands, because an agent shows the server's stderr on every start.

aitools mcp serve carries the annotation. Its own check for --log-file stdout and the match by
command name are removed.

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

A refresh failure stops the proxy only when it is a real token failure. A request whose own
context ended (the client cancelled it, or shutdown began) fails that request alone, and the
session DELETE at shutdown is best effort, so its auth failure is only logged.

An unknown --agent no longer stops serve: it only feeds the User-Agent, and an entry written by a
later release may name an agent this binary does not know. Unknown names are left out of the
User-Agent and logged at debug.

The first SIGINT or SIGTERM cancels the context and releases the signal handler before the context
reports done, so a second signal during shutdown ends the process.

The proxy ignores the profile's HTTPTransport: BoundConfig never sets it. The transport keeps the
connect timeout and InsecureSkipVerify.

Co-authored-by: Isaac <no-reply@databricks.com>
serve-binding-errors records the config file as the binary spells it (pwd -W on Git Bash), so the
binding check matches on Windows. The unknown --agent case moves to serve-passthrough, which shows
that such an agent is served and logged at debug. The out.* files are goldens; the stdout and stderr
captures now go to LOG.* files. The last binding case checks a log file on stdout at debug level.

The testserver's MCP endpoint names the JSON-RPC version once and documents its helpers. The proxy
test's runOptions is documented.

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

A log file is refused for a protocol command when it is the process's stdout. The check now
compares the opened file with os.Stdout: os.Stat of /dev/stdout does not match stdout on macOS
when stdout is a pipe. The check runs after the file is opened and before the first log line.

The log file is closed when Execute returns, so a test or an embedding caller does not keep it
open (Windows cannot remove an open file).

ExitStatus honors only the named type ExitStatusError, which serve returns for binding and auth
failures. mcp.BindError no longer carries an exit status, so `mcp add` exits 1 in both output
modes, as its SPEC says. An error with an ExitStatus method of its own no longer sets the status.

Co-authored-by: Isaac <no-reply@databricks.com>
The check ran after initializeContext had installed the stdout logger, so a refused command
still wrote its telemetry debug line to stdout. It now runs inside initializeContext, after the
log file opens and before the default logger is set, and it closes the refused file.

The log file is also recorded on the context here, so Execute closes it after the last log line.

Co-authored-by: Isaac <no-reply@databricks.com>
…the service name

- A token that cannot be refreshed mid-session is printed once: the bridge logs it on the request that
  needed it, and serve exits 2 without printing it again. The session DELETE after that failure is
  sent without a credential, so it does not log the refresh error a second time.
- A state file that cannot be read is a binding failure (exit 2).
- --service must be a full three-part name, with the message naming the flag.
- An unknown --agent is reported on stderr once, not at debug level.
- The bound profile is no longer set as the command's config: serve logs no telemetry yet, so the
  setting had no effect. Card 10a adds it with the telemetry it serves.
- The signal context is released by the same stop that cancels the refresh guard, so a second
  signal during a refresh-failure shutdown ends the process.
- serveTransport sets InsecureSkipVerify on the clone's TLS config instead of replacing it.
- The fake MCP endpoint answers a client response (an id with no method) with 202 and no body.
- The second-signal test asserts the child was ended by the signal, not by a panic or exit.

Co-authored-by: Isaac <no-reply@databricks.com>
writesToStdout read the process's os.Stdout and os.Stderr. Under `go test -json`
the testing package sets os.Stderr to os.Stdout, so the stdout cases were taken
for stderr and the check passed the log file through. Take the two streams as
parameters, and give the tests pipes and a separate stderr of their own.
Production behavior is unchanged.

Co-authored-by: Isaac <no-reply@databricks.com>
On Windows os.SameFile reports any two pipes or character devices as the same
file, because their file ids are zero. With stdout piped, a null device or a
named-pipe log file was refused as stdout. Compare by identity only for
regular files there; elsewhere nothing changes. stdout itself is matched by
pointer, so the stdout keyword is still refused on every OS.

Co-authored-by: Isaac <no-reply@databricks.com>
When stdout and stderr are one file, as on a terminal, a log file opened as
/dev/stderr or /dev/tty was refused as stdout, while --log-file stderr was
allowed. The protocol already shares that file with the diagnostics, so the
check cannot help there: a destination that is the same file as stderr is not
refused. Other destinations are compared as before.

Co-authored-by: Isaac <no-reply@databricks.com>
Execute closed the log file through the context of the command that ran. On a
panic the command's context was never returned, so the file stayed open, and
the default logger kept writing to it after Execute returned, or closed. Execute
now keeps a slot in its own context that initializeContext fills, closes the
file in a defer (which also runs on a panic), and restores the default logger
that was in place before the command.

Co-authored-by: Isaac <no-reply@databricks.com>
A caller whose credential is known to be unusable has no reason to send the
session DELETE at shutdown: it would fail and log the failure again. Authenticate
can now return ErrSkipRequest for the DELETE. The bridge then sends and logs
nothing for it. Any other request that returns the sentinel is an error like
any other.

Co-authored-by: Isaac <no-reply@databricks.com>
simonfaltum and others added 14 commits November 13, 2026 14:48
After a token refresh failure, every later request asked for a token again
and logged the login hint again, a request cancelled before its failure was
logged left nothing on stderr, and a stdin error after the failure exited 1
and hid the auth status. Now the first failing request carries the hint, with
the restart step; later requests fail at once with a short error that does not
repeat it; the session DELETE is skipped (proxy.ErrSkipRequest); and serve
prints one final line and exits 2, whatever the bridge returned.

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

The fake MCP endpoint answered any service name, so a wrong service name or path
in the proxy's command still passed. A name with no service now gets 404, as
the gateway answers a name that has none, and the tests and the serve
acceptance case seed the service they use.

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

The stale-version skip for a command whose stdout carries a protocol had no
test. Move the decision into staleVersionSkipped, which takes the build info,
and test it. In initializeContext the refused log file no longer needs a close:
Execute closes the file it opened.

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

The flag names of serve are the constants that add writes into agent configs,
so an entry binds what add wrote; a test parses that argv with serve's flags.
The dialer helper existed only for a test of a constant, so it is inlined and
the test dropped. The binding status doc no longer cites a list command that is
not in this tree.

Co-authored-by: Isaac <no-reply@databricks.com>
The serve cases ran the command without printing it, so the flags under test
did not appear in the goldens. Wrap each invocation in trace.

Co-authored-by: Isaac <no-reply@databricks.com>
slog.SetDefault redirects the standard log package to the new handler, so
after Execute closed the log file, log.Printf still wrote to it. Execute
now saves and restores log.Writer() and log.Flags() with the default logger.

Co-authored-by: Isaac <no-reply@databricks.com>
The stdout check ran for every protocol command, including the default
stderr log, and a failed Stat returned an error that stopped the command,
so `2>&-` made `serve` fail. A stat failure now means the stream is not
stdout, and writesToStdout returns a bool.

Co-authored-by: Isaac <no-reply@databricks.com>
Unity Catalog names are case-insensitive, and the fake's listing already
matches them that way. The MCP endpoint looked the name up exactly, so a
service addressed in another case got 404.

Co-authored-by: Isaac <no-reply@databricks.com>
jq -s also accepts pretty-printed or several-per-line JSON, so the
passthrough test did not check that each stdout line is one message. The
filter now reads each line with fromjson. The binding-errors steps send
stderr to the script output, so each traced command, its stderr and its
exit code appear in order in the golden.

Co-authored-by: Isaac <no-reply@databricks.com>
No behavior change. The refresh-failure tests share one fake endpoint and
one Authenticate, and the exit-with-status error is built by one helper.
Stale-version skipping is one predicate, the refresh-failure messages are
constants in the tests, and the two no-body endpoint tests are one table.
Comments are trimmed to their reasons, test comments start with the test
name, and the silent_err.go import is back to its original form.

Co-authored-by: Isaac <no-reply@databricks.com>
…ndows console case

serve checks its binding only at start. A profile re-pointed to another
host during the session keeps receiving the endpoint of the bound host, so
that is documented on the command. The refresh hint's restart is needed
because a new spawn re-runs the binding checks. The stdout check treats a
Windows console as a file that is not stdout, so `--log-file stdout` is
refused there even in a terminal.

Co-authored-by: Isaac <no-reply@databricks.com>
depguard forbids the standard log package outside a few generators. The
import is needed here because slog.SetDefault redirects that package, and
libs/log has no way to restore it. The exception is annotated where it is
used.

Co-authored-by: Isaac <no-reply@databricks.com>
Serve stopped on any token refresh failure. A network error, a timeout or a
5xx from the token endpoint is not a credential the user must replace, so
serve now fails only the request that needed the token, with the login hint,
and keeps serving. The next request asks for a token again.

A failure stops serve only when the token is rejected: the token endpoint
answers 400 or 401 with invalid_grant or invalid_client, or the U2M client
reports an invalid refresh token. Anything else is transient, including
errors that cannot be classified.

Co-authored-by: Isaac <no-reply@databricks.com>
A failure that is not a rejection, such as a network blip, says nothing
about the credential, so the request's error no longer tells the user to
log in. It says the next request tries again.

Co-authored-by: Isaac <no-reply@databricks.com>
@simonfaltum
simonfaltum force-pushed the aitools-mcp/06a-bridge branch from 6634449 to 5c42df0 Compare November 13, 2026 15:05
@simonfaltum
simonfaltum force-pushed the aitools-mcp/06b-serve branch from 7064bbc to 10450c9 Compare November 13, 2026 15:05

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