thinqos 3.3.2__tar.gz → 3.3.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. {thinqos-3.3.2 → thinqos-3.3.4}/CHANGELOG.md +14 -0
  2. {thinqos-3.3.2 → thinqos-3.3.4}/PKG-INFO +12 -7
  3. {thinqos-3.3.2 → thinqos-3.3.4}/README.md +11 -6
  4. {thinqos-3.3.2 → thinqos-3.3.4}/pyproject.toml +1 -1
  5. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/cli.py +47 -34
  6. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_install.py +61 -1
  7. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_5100_key_prompt_links.py +1 -1
  8. {thinqos-3.3.2 → thinqos-3.3.4}/.gitignore +0 -0
  9. {thinqos-3.3.2 → thinqos-3.3.4}/scripts/sync_cognitive_contract.py +0 -0
  10. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/__init__.py +0 -0
  11. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/__main__.py +0 -0
  12. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/accountscope.py +0 -0
  13. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/adapters/__init__.py +0 -0
  14. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/adapters/claude_code.py +0 -0
  15. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/adapters/cline.py +0 -0
  16. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/adapters/cline_tasks.py +0 -0
  17. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/adapters/codex.py +0 -0
  18. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/adapters/grok_build.py +0 -0
  19. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/adapters/vscode_chat_store.py +0 -0
  20. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/adapters/vscode_copilot.py +0 -0
  21. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/attribution.py +0 -0
  22. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/capture_history_policy.py +0 -0
  23. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/client.py +0 -0
  24. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/cline_sweep.py +0 -0
  25. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/connections.py +0 -0
  26. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/denylist.py +0 -0
  27. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/discovery.py +0 -0
  28. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/filelock.py +0 -0
  29. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/hook.py +0 -0
  30. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/importer.py +0 -0
  31. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/incremental.py +0 -0
  32. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/install_marker.py +0 -0
  33. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/installs.py +0 -0
  34. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/logsetup.py +0 -0
  35. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/mcp_transport.py +0 -0
  36. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/primecache.py +0 -0
  37. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/process.py +0 -0
  38. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/questionrecall.py +0 -0
  39. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/recovery.py +0 -0
  40. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/scheduler.py +0 -0
  41. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/secretstore.py +0 -0
  42. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/selfupdate.py +0 -0
  43. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/server_revision.py +0 -0
  44. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/shim.py +0 -0
  45. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/state.py +0 -0
  46. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/taskstate.py +0 -0
  47. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/tls.py +0 -0
  48. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/tool_turns.py +0 -0
  49. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/vscode_chat_sweep.py +0 -0
  50. {thinqos-3.3.2 → thinqos-3.3.4}/src/thinqos_capture/writeredirect.py +0 -0
  51. {thinqos-3.3.2 → thinqos-3.3.4}/tests/conftest.py +0 -0
  52. {thinqos-3.3.2 → thinqos-3.3.4}/tests/fixtures/claude_code_session.jsonl +0 -0
  53. {thinqos-3.3.2 → thinqos-3.3.4}/tests/fixtures/codex_session.jsonl +0 -0
  54. {thinqos-3.3.2 → thinqos-3.3.4}/tests/fixtures/grok_build_summary.json +0 -0
  55. {thinqos-3.3.2 → thinqos-3.3.4}/tests/fixtures/grok_build_updates.jsonl +0 -0
  56. {thinqos-3.3.2 → thinqos-3.3.4}/tests/fixtures/tos_6776_recovery_failures.json +0 -0
  57. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_attribution.py +0 -0
  58. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_capture_completeness.py +0 -0
  59. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_capture_incremental.py +0 -0
  60. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_claude_code_adapter.py +0 -0
  61. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_cli_drain.py +0 -0
  62. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_cli_install_hook.py +0 -0
  63. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_cli_list_forget.py +0 -0
  64. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_cli_run.py +0 -0
  65. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_client_limits.py +0 -0
  66. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_client_version_header.py +0 -0
  67. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_cline_adapter.py +0 -0
  68. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_cline_mcp_registration.py +0 -0
  69. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_cline_sweep.py +0 -0
  70. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_cline_tasks.py +0 -0
  71. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_codex_adapter.py +0 -0
  72. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_codex_capture_staleness.py +0 -0
  73. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_codex_hook_runtime_contract.py +0 -0
  74. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_codex_hook_trust.py +0 -0
  75. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_codex_plugin_setup.py +0 -0
  76. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_codex_rollout_memory.py +0 -0
  77. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_doctor.py +0 -0
  78. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_doctor_queue.py +0 -0
  79. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_drain_partition_split.py +0 -0
  80. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_drain_poison.py +0 -0
  81. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_drain_timeout.py +0 -0
  82. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_env_key_remnant.py +0 -0
  83. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_grok_build_adapter.py +0 -0
  84. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_hook.py +0 -0
  85. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_hook_autodrain.py +0 -0
  86. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_hook_prime.py +0 -0
  87. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_hook_secret_resolution.py +0 -0
  88. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_hook_self_update.py +0 -0
  89. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_hook_stdin.py +0 -0
  90. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_hook_task_state.py +0 -0
  91. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_importer.py +0 -0
  92. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_incremental.py +0 -0
  93. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_installer_hardening.py +0 -0
  94. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_liveness.py +0 -0
  95. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_logsetup.py +0 -0
  96. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_mcp_transport.py +0 -0
  97. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_no_retired_tool_names.py +0 -0
  98. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_plugin_autoupdate.py +0 -0
  99. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_plugin_mode.py +0 -0
  100. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_prime_cache.py +0 -0
  101. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_process.py +0 -0
  102. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_queue_policy.py +0 -0
  103. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_scheduler.py +0 -0
  104. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_secretstore.py +0 -0
  105. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_selfupdate.py +0 -0
  106. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_shim.py +0 -0
  107. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_state.py +0 -0
  108. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_state_migration.py +0 -0
  109. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_taskstate.py +0 -0
  110. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tls_cloud_proxy.py +0 -0
  111. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_token_economy.py +0 -0
  112. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tool_turns.py +0 -0
  113. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_2840_completion.py +0 -0
  114. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_2840_repair_round1.py +0 -0
  115. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_2840_repair_round2.py +0 -0
  116. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_4483_account_scope.py +0 -0
  117. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_4485_capture_history_policy.py +0 -0
  118. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_4485_doctor_update_state.py +0 -0
  119. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_4485_prime_refresh_coalescing.py +0 -0
  120. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_4486_question_recall.py +0 -0
  121. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_4486_refresh_retry.py +0 -0
  122. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_4487_install_marker.py +0 -0
  123. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_5043_connections.py +0 -0
  124. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_5043_discovery.py +0 -0
  125. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_5043_install_precedence.py +0 -0
  126. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_5102_selfheal.py +0 -0
  127. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_5226_installs_registry.py +0 -0
  128. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_5369_mcp_revision.py +0 -0
  129. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_6776_recovery.py +0 -0
  130. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_6807_plugin_install.py +0 -0
  131. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_7410_prime_refusals.py +0 -0
  132. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_tos_7686_self_repair.py +0 -0
  133. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_verify.py +0 -0
  134. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_vscode_chat_store.py +0 -0
  135. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_vscode_copilot_adapter.py +0 -0
  136. {thinqos-3.3.2 → thinqos-3.3.4}/tests/test_writeredirect.py +0 -0
  137. {thinqos-3.3.2 → thinqos-3.3.4}/uv.lock +0 -0
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.3.4](https://github.com/AI4Outcomes/thinqos/compare/tools/thinqos-v3.3.3...tools/thinqos-v3.3.4) (2026-10-10)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * follow migrated Copilot MCP configuration (TOS-7796) ([#8401](https://github.com/AI4Outcomes/thinqos/issues/8401)) ([e845518](https://github.com/AI4Outcomes/thinqos/commit/e845518d4fd2194805a6a46085ebdf9b71a136e3))
9
+
10
+ ## [3.3.3](https://github.com/AI4Outcomes/thinqos/compare/tools/thinqos-v3.3.2...tools/thinqos-v3.3.3) (2026-10-09)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * release batch af78bc6f (TOS-7763, TOS-7758, TOS-7768) ([#8361](https://github.com/AI4Outcomes/thinqos/issues/8361)) ([2d27f78](https://github.com/AI4Outcomes/thinqos/commit/2d27f78d697c61c8bda2c8beedb244034b451df4))
16
+
3
17
  ## [3.3.2](https://github.com/AI4Outcomes/thinqos/compare/tools/thinqos-v3.3.1...tools/thinqos-v3.3.2) (2026-10-09)
4
18
 
5
19
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: thinqos
3
- Version: 3.3.2
3
+ Version: 3.3.4
4
4
  Summary: Connect Claude Code, Codex, Grok Build, VS Code, and Cline to your thinqOS Mind.
5
5
  Project-URL: Homepage, https://thinqos.com
6
6
  Project-URL: Documentation, https://thinqos.com/mcp-memory-server
@@ -109,7 +109,7 @@ the Claude Code or Codex *extensions*, which are wired via `--client claude` /
109
109
  uvx --link-mode=copy thinqos install --client vscode --pat tq_xxxx...
110
110
  ```
111
111
 
112
- This registers thinqOS as an MCP server in VS Code's own `User/mcp.json` AND
112
+ This registers thinqOS in the active VS Code/Copilot MCP configuration AND
113
113
  wires capture/reflexive-retrieve/resume hooks into the **workspace**
114
114
  `.github/copilot/settings.local.json` for the Copilot agent host. Note `settings.local.json`, NOT
115
115
  `settings.json`: the shared file is git-tracked in every AI4O repo, and
@@ -446,7 +446,10 @@ reads workspace hook sources including `.claude/settings.local.json`; thinqOS
446
446
  writes its managed capture/incremental/reflexive-retrieve/resume hooks to the
447
447
  Copilot-only `.github/copilot/settings.local.json` so they cannot double-fire
448
448
  inside Claude Code. It registers the remote thinqOS MCP server in VS Code's
449
- native user-level `mcp.json`:
449
+ user-level `~/.copilot/mcp-config.json` (`mcpServers.thinqos`) once that
450
+ Copilot config exists, honoring `COPILOT_HOME`. This follows the newer VS Code
451
+ migration rather than recreating the legacy entry and repeating its prompt.
452
+ Older hosts without that config continue to use native `mcp.json`:
450
453
 
451
454
  - macOS: `~/Library/Application Support/Code/User/mcp.json`
452
455
  - Windows: `%APPDATA%\Code\User\mcp.json`
@@ -461,12 +464,14 @@ to the same capture path Claude uses.
461
464
  Because the wiring lives in the workspace `.github/copilot/settings.local.json`, run
462
465
  `install --client vscode` from the repo root you want connected (the installer
463
466
  walks up for `.git` to find the workspace root). It is safe to re-run: managed
464
- hooks are detected and migrated, and unrelated MCP servers already in
465
- `mcp.json` (e.g. `github/github-mcp-server`) are preserved.
467
+ hooks are detected and migrated, and unrelated MCP servers and existing
468
+ custom tool allowlists are preserved. `VSCODE_MCP_PATH` explicitly overrides
469
+ the registration file; name it `mcp-config.json` for the Copilot format.
466
470
 
467
471
  `uninstall --client vscode` removes the workspace capture hooks and the
468
- `servers.thinqos` entry from `User/mcp.json`, leaving any other MCP servers you
469
- have registered untouched.
472
+ `thinqos` entry from the active MCP configuration, leaving any other MCP
473
+ servers you have registered untouched. Doctor and background sync use the
474
+ same path and schema.
470
475
 
471
476
  For a one-time backfill of past VS Code sessions in the current workspace:
472
477
 
@@ -89,7 +89,7 @@ the Claude Code or Codex *extensions*, which are wired via `--client claude` /
89
89
  uvx --link-mode=copy thinqos install --client vscode --pat tq_xxxx...
90
90
  ```
91
91
 
92
- This registers thinqOS as an MCP server in VS Code's own `User/mcp.json` AND
92
+ This registers thinqOS in the active VS Code/Copilot MCP configuration AND
93
93
  wires capture/reflexive-retrieve/resume hooks into the **workspace**
94
94
  `.github/copilot/settings.local.json` for the Copilot agent host. Note `settings.local.json`, NOT
95
95
  `settings.json`: the shared file is git-tracked in every AI4O repo, and
@@ -426,7 +426,10 @@ reads workspace hook sources including `.claude/settings.local.json`; thinqOS
426
426
  writes its managed capture/incremental/reflexive-retrieve/resume hooks to the
427
427
  Copilot-only `.github/copilot/settings.local.json` so they cannot double-fire
428
428
  inside Claude Code. It registers the remote thinqOS MCP server in VS Code's
429
- native user-level `mcp.json`:
429
+ user-level `~/.copilot/mcp-config.json` (`mcpServers.thinqos`) once that
430
+ Copilot config exists, honoring `COPILOT_HOME`. This follows the newer VS Code
431
+ migration rather than recreating the legacy entry and repeating its prompt.
432
+ Older hosts without that config continue to use native `mcp.json`:
430
433
 
431
434
  - macOS: `~/Library/Application Support/Code/User/mcp.json`
432
435
  - Windows: `%APPDATA%\Code\User\mcp.json`
@@ -441,12 +444,14 @@ to the same capture path Claude uses.
441
444
  Because the wiring lives in the workspace `.github/copilot/settings.local.json`, run
442
445
  `install --client vscode` from the repo root you want connected (the installer
443
446
  walks up for `.git` to find the workspace root). It is safe to re-run: managed
444
- hooks are detected and migrated, and unrelated MCP servers already in
445
- `mcp.json` (e.g. `github/github-mcp-server`) are preserved.
447
+ hooks are detected and migrated, and unrelated MCP servers and existing
448
+ custom tool allowlists are preserved. `VSCODE_MCP_PATH` explicitly overrides
449
+ the registration file; name it `mcp-config.json` for the Copilot format.
446
450
 
447
451
  `uninstall --client vscode` removes the workspace capture hooks and the
448
- `servers.thinqos` entry from `User/mcp.json`, leaving any other MCP servers you
449
- have registered untouched.
452
+ `thinqos` entry from the active MCP configuration, leaving any other MCP
453
+ servers you have registered untouched. Doctor and background sync use the
454
+ same path and schema.
450
455
 
451
456
  For a one-time backfill of past VS Code sessions in the current workspace:
452
457
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "thinqos"
3
- version = "3.3.2"
3
+ version = "3.3.4"
4
4
  description = "Connect Claude Code, Codex, Grok Build, VS Code, and Cline to your thinqOS Mind."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -1806,7 +1806,7 @@ def doctor_cmd(
1806
1806
  )
1807
1807
  click.echo(
1808
1808
  f" Otherwise, get a fresh key from {base_url}"
1809
- "/settings?section=expenses and run "
1809
+ "/api-keys and run "
1810
1810
  f"`{CLI_NAME} install --repair`."
1811
1811
  )
1812
1812
  check_statuses["server"] = "fail"
@@ -2309,19 +2309,28 @@ def _grok_agents_path() -> Path:
2309
2309
 
2310
2310
 
2311
2311
  def _vscode_mcp_path() -> Path:
2312
- """User-level VS Code MCP registration file (native ``mcp.json``).
2313
-
2314
- This is distinct from the Claude Code / Codex extensions that also run
2315
- inside VS Code - those are handled by ``--client claude`` / ``--client
2316
- codex`` and write into ``~/.claude`` / ``~/.codex``. This path is VS
2317
- Code's OWN "Agents" surface (Copilot Chat / Copilot CLI agent mode),
2318
- which reads MCP servers from this file, not from either coding client's
2319
- config. Capture hooks for this client live in the WORKSPACE
2320
- ``.claude/settings.local.json`` (see ``_config_path_for("vscode")``).
2312
+ """Use the Copilot agent host's config after VS Code has migrated it.
2313
+
2314
+ Older hosts use ``User/mcp.json``. Once ``mcp-config.json`` exists,
2315
+ install, sync, doctor and uninstall must follow that destination rather
2316
+ than resurrecting the legacy registration and its migration prompt.
2317
+ ``VSCODE_MCP_PATH`` remains an explicit override for either format.
2321
2318
  """
2322
2319
  override = os.environ.get("VSCODE_MCP_PATH")
2323
2320
  if override:
2324
2321
  return Path(override)
2322
+ copilot_config = Path(os.environ.get("COPILOT_HOME") or (Path.home() / ".copilot"))
2323
+ copilot_config /= "mcp-config.json"
2324
+ if copilot_config.exists():
2325
+ return copilot_config
2326
+ return _vscode_legacy_mcp_path()
2327
+
2328
+
2329
+ def _vscode_mcp_container(path: Path) -> str:
2330
+ return "mcpServers" if path.name == "mcp-config.json" else "servers"
2331
+
2332
+
2333
+ def _vscode_legacy_mcp_path() -> Path:
2325
2334
  home = Path.home()
2326
2335
  if sys.platform == "win32":
2327
2336
  # %APPDATA%\Code\User\mcp.json
@@ -2426,15 +2435,11 @@ def _detect_cline_mcp() -> dict[str, str | bool]:
2426
2435
 
2427
2436
 
2428
2437
  def _detect_vscode_mcp() -> dict[str, str | bool]:
2429
- """Return non-secret VS Code native MCP registration diagnostics.
2430
-
2431
- VS Code's schema keys servers under ``servers`` (not Claude's
2432
- ``mcpServers``); each entry is otherwise the same ``type``/``url``/
2433
- ``headers`` shape.
2434
- """
2438
+ """Return non-secret diagnostics from the active VS Code MCP format."""
2435
2439
  try:
2436
- cfg = json.loads(_vscode_mcp_path().read_text(encoding="utf-8"))
2437
- server = _thinqos_server_entry(cfg, "servers")
2440
+ path = _vscode_mcp_path()
2441
+ cfg = json.loads(path.read_text(encoding="utf-8"))
2442
+ server = _thinqos_server_entry(cfg, _vscode_mcp_container(path))
2438
2443
  if server is None:
2439
2444
  return {"present": False}
2440
2445
  headers = server.get("headers", {})
@@ -5462,7 +5467,10 @@ def _registration_key_absent(client: str) -> bool:
5462
5467
  return False
5463
5468
  if not isinstance(config, dict):
5464
5469
  return False
5465
- container = config.get(_REGISTRATION_CONTAINERS[client])
5470
+ container_key = (
5471
+ _vscode_mcp_container(path) if client == "vscode" else _REGISTRATION_CONTAINERS[client]
5472
+ )
5473
+ container = config.get(container_key)
5466
5474
  if container is None:
5467
5475
  return True
5468
5476
  return isinstance(container, dict) and "thinqos" not in container
@@ -6522,7 +6530,7 @@ def _remove_vscode_mcp_registration(*, dry_run: bool) -> str:
6522
6530
  return "unreadable - preserved"
6523
6531
  if not isinstance(config, dict):
6524
6532
  return "unexpected shape - preserved"
6525
- servers = config.get("servers")
6533
+ servers = config.get(_vscode_mcp_container(path))
6526
6534
  if not isinstance(servers, dict) or "thinqos" not in servers:
6527
6535
  return "not present"
6528
6536
  if not dry_run:
@@ -6555,14 +6563,10 @@ def _remove_cline_mcp_registration(*, dry_run: bool) -> str:
6555
6563
 
6556
6564
 
6557
6565
  def _ensure_vscode_mcp_registration(token: str, base_url: str) -> str:
6558
- """Write/refresh the ``thinqos`` entry in VS Code's native ``mcp.json``.
6559
-
6560
- Safe to re-run on a file that already has unrelated servers registered
6561
- (e.g. ``github/github-mcp-server``) - only the ``servers.thinqos`` key is
6562
- ever touched. Creates the file (and parent dirs) if VS Code has not
6563
- created one yet, since - unlike Claude/Codex/Grok, whose config
6564
- directories only exist once that client has run at least once - a fresh
6565
- VS Code install may not have written ``User/mcp.json`` yet.
6566
+ """Merge thinqOS into the active VS Code/Copilot MCP registration file.
6567
+
6568
+ Preserve unrelated servers and custom options, including Copilot's
6569
+ tool allowlist. Older hosts without a migrated config retain mcp.json.
6566
6570
  """
6567
6571
  path = _vscode_mcp_path()
6568
6572
  config: dict[str, Any] = {}
@@ -6574,19 +6578,28 @@ def _ensure_vscode_mcp_registration(token: str, base_url: str) -> str:
6574
6578
  if not isinstance(parsed, dict):
6575
6579
  return "unexpected shape - preserved"
6576
6580
  config = parsed
6577
- servers = config.setdefault("servers", {})
6581
+ container_key = _vscode_mcp_container(path)
6582
+ servers = config.setdefault(container_key, {})
6578
6583
  if not isinstance(servers, dict):
6579
- return "unexpected servers shape - preserved"
6584
+ return f"unexpected {container_key} shape - preserved"
6580
6585
  endpoint = _mcp_endpoint(base_url)
6581
- desired = {
6586
+ desired: dict[str, Any] = {
6582
6587
  "type": "http",
6583
6588
  "url": endpoint,
6584
6589
  "headers": {"Authorization": f"Bearer {token}"},
6585
6590
  }
6586
6591
  existing = servers.get("thinqos")
6587
- if existing == desired:
6592
+ merged = dict(existing) if isinstance(existing, dict) else {}
6593
+ headers = merged.get("headers")
6594
+ merged_headers = dict(headers) if isinstance(headers, dict) else {}
6595
+ merged_headers.update(desired["headers"])
6596
+ desired["headers"] = merged_headers
6597
+ merged.update(desired)
6598
+ if container_key == "mcpServers":
6599
+ merged.setdefault("tools", ["*"])
6600
+ if existing == merged:
6588
6601
  return "already up to date"
6589
- servers["thinqos"] = desired
6602
+ servers["thinqos"] = merged
6590
6603
  path.parent.mkdir(parents=True, exist_ok=True)
6591
6604
  tmp = path.with_suffix(path.suffix + ".tmp")
6592
6605
  tmp.write_text(json.dumps(config, indent=2), encoding="utf-8")
@@ -7089,7 +7102,7 @@ def _prompt_for_pasted_token(base_url: str) -> str:
7089
7102
  """
7090
7103
  click.echo("")
7091
7104
  click.echo("No thinqOS API key found on this machine yet.")
7092
- click.echo(f" 1. Open {base_url}/settings?section=expenses (sign in if asked).")
7105
+ click.echo(f" 1. Open {base_url}/api-keys (sign in if asked).")
7093
7106
  click.echo(" 2. Click Create Key and copy the key it shows you.")
7094
7107
  if base_url == DEFAULT_BASE_URL:
7095
7108
  click.echo(
@@ -61,6 +61,7 @@ def _no_real_uv(monkeypatch, tmp_path) -> None:
61
61
  monkeypatch.setenv("GROK_AGENTS_PATH", str(tmp_path / "grok" / "AGENTS.md"))
62
62
  # Never touch the real VS Code User/mcp.json.
63
63
  monkeypatch.setenv("VSCODE_MCP_PATH", str(tmp_path / "vscode_mcp.json"))
64
+ monkeypatch.setenv("COPILOT_HOME", str(tmp_path / "copilot"))
64
65
  # Never write workspace capture hooks into the real repo's
65
66
  # .claude/settings.json - pin the resolved workspace root under tmp_path.
66
67
  vscode_workspace = tmp_path / "vscode_workspace"
@@ -635,6 +636,65 @@ def test_uninstall_vscode_dry_run_preserves_registration(tmp_path: Path, monkeyp
635
636
  assert vscode_mcp.read_text() == original
636
637
 
637
638
 
639
+ @pytest.mark.parametrize("existing", [False, True])
640
+ def test_vscode_follows_migrated_copilot_config(
641
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, existing: bool
642
+ ) -> None:
643
+ from thinqos_capture import cli
644
+
645
+ monkeypatch.delenv("VSCODE_MCP_PATH")
646
+ monkeypatch.setattr(cli.Path, "home", lambda: tmp_path)
647
+ monkeypatch.setattr(cli.sys, "platform", "darwin")
648
+ migrated = tmp_path / "copilot" / "mcp-config.json"
649
+ migrated.parent.mkdir()
650
+ custom = {"type": "http", "url": "https://old.example/mcp", "tools": ["recall"]}
651
+ servers = {"commons": {"type": "http", "url": "https://example.com/mcp"}}
652
+ if existing:
653
+ servers["thinqos"] = custom
654
+ migrated.write_text(json.dumps({"mcpServers": servers}), encoding="utf-8")
655
+ legacy = cli._vscode_legacy_mcp_path()
656
+ legacy.parent.mkdir(parents=True)
657
+ legacy.write_text('{"inputs": [], "servers": {}}', encoding="utf-8")
658
+ before = legacy.read_bytes()
659
+
660
+ assert cli._vscode_mcp_path() == migrated
661
+ assert cli._registration_key_absent("vscode") is (not existing)
662
+ assert cli._sync_client("vscode", "tq_test", "https://app.thinqos.com", dry_run=False) == (
663
+ "updated" if existing else "added"
664
+ )
665
+ config = json.loads(migrated.read_text(encoding="utf-8"))
666
+ assert config["mcpServers"]["commons"] == servers["commons"]
667
+ assert config["mcpServers"]["thinqos"]["tools"] == (["recall"] if existing else ["*"])
668
+ assert "servers" not in config
669
+ assert cli._detect_vscode_mcp()["present"] is True
670
+ assert cli._registration_key_absent("vscode") is False
671
+ assert cli._sync_client("vscode", "tq_test", "https://app.thinqos.com", dry_run=False) == (
672
+ "already up to date"
673
+ )
674
+ assert cli._remove_vscode_mcp_registration(dry_run=True) == "would remove"
675
+ assert cli._detect_vscode_mcp()["present"] is True
676
+ assert cli._remove_vscode_mcp_registration(dry_run=False) == "removed"
677
+ assert json.loads(migrated.read_text(encoding="utf-8"))["mcpServers"] == {
678
+ "commons": servers["commons"]
679
+ }
680
+ assert legacy.read_bytes() == before
681
+
682
+
683
+ def test_vscode_preserves_malformed_migrated_config(
684
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
685
+ ) -> None:
686
+ from thinqos_capture import cli
687
+
688
+ migrated = tmp_path / "mcp-config.json"
689
+ migrated.write_text('{"mcpServers": []}', encoding="utf-8")
690
+ monkeypatch.setenv("VSCODE_MCP_PATH", str(migrated))
691
+ assert cli._ensure_vscode_mcp_registration("tq_test", "https://app.thinqos.com") == (
692
+ "unexpected mcpServers shape - preserved"
693
+ )
694
+ assert migrated.read_text(encoding="utf-8") == '{"mcpServers": []}'
695
+ assert cli._registration_key_absent("vscode") is False
696
+
697
+
638
698
  def test_vscode_mcp_path_prefers_override_env_var(
639
699
  tmp_path: Path, monkeypatch: pytest.MonkeyPatch
640
700
  ) -> None:
@@ -2458,7 +2518,7 @@ def test_install_with_no_token_sources_prompts_and_accepts_pasted_pat(
2458
2518
  )
2459
2519
 
2460
2520
  assert result.exit_code == 0, result.output
2461
- assert "Open https://app.thinqos.com/settings?section=expenses" in result.output
2521
+ assert "Open https://app.thinqos.com/api-keys" in result.output
2462
2522
  assert "Paste your thinqOS API key" in result.output
2463
2523
  assert read_ingest_key(tmp_path / "state") == "tq_pasted_abc123"
2464
2524
 
@@ -17,7 +17,7 @@ def test_prompt_links_directly_to_key_page(
17
17
  monkeypatch.setattr(cli.click, "prompt", lambda *a, **k: "")
18
18
  cli._prompt_for_pasted_token("https://thinqos.heartwoodguild.com")
19
19
  out = capsys.readouterr().out
20
- assert "https://thinqos.heartwoodguild.com/settings?section=expenses" in out
20
+ assert "https://thinqos.heartwoodguild.com/api-keys" in out
21
21
  assert "Create Key" in out
22
22
  assert "/connect " not in out
23
23
  assert "Settings -> API Keys" not in out
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes