dotagents-cli 0.8.1__tar.gz → 0.8.3__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 (125) hide show
  1. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/CHANGELOG.md +42 -1
  2. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/PKG-INFO +5 -2
  3. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/guide/commands.md +12 -5
  4. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/guide/overlays.md +49 -1
  5. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/guide/private-sync.md +17 -10
  6. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/pyproject.toml +3 -2
  7. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/AGENTS.md +53 -13
  8. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/__init__.py +1 -1
  9. dotagents_cli-0.8.3/src/dotagents/_mcp_gateway.py +116 -0
  10. dotagents_cli-0.8.3/src/dotagents/_mcp_gateway_clients.py +385 -0
  11. dotagents_cli-0.8.3/src/dotagents/_mcp_gateway_config.py +168 -0
  12. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/cmds/findings.py +21 -0
  13. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_overlays.py +5 -3
  14. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_pyvenv.py +1 -1
  15. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_scope.py +25 -2
  16. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_wrappers.py +34 -23
  17. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/cli/__init__.py +14 -1
  18. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/cli/build_pyz.py +3 -2
  19. dotagents_cli-0.8.3/src/dotagents/cli/mcp.py +36 -0
  20. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_audit_leak.py +1 -1
  21. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_cli_main.py +1 -0
  22. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_cmds_discovery.py +5 -3
  23. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_findings_cmd.py +14 -0
  24. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_fix_scope_context.py +27 -0
  25. dotagents_cli-0.8.3/tests/test_mcp.py +368 -0
  26. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_wrappers.py +29 -3
  27. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tools/pyz_smoke.sh +1 -1
  28. dotagents_cli-0.8.1/tests/test_mcp.py +0 -60
  29. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/.gitignore +0 -0
  30. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/LICENSE +0 -0
  31. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/README.md +0 -0
  32. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/agents.md +0 -0
  33. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/cli.md +0 -0
  34. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/commands.md +0 -0
  35. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/context.md +0 -0
  36. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/env.md +0 -0
  37. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/fs.md +0 -0
  38. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/hooks.md +0 -0
  39. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/merge.md +0 -0
  40. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/overlays.md +0 -0
  41. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/reference.md +0 -0
  42. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/resources.md +0 -0
  43. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/scope.md +0 -0
  44. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/skills.md +0 -0
  45. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/sources.md +0 -0
  46. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/api/wrappers.md +0 -0
  47. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/changelog.md +0 -0
  48. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/guide/authoring.md +0 -0
  49. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/guide/install.md +0 -0
  50. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/docs/index.md +0 -0
  51. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/install.py +0 -0
  52. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/mkdocs.yml +0 -0
  53. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/__main__.py +0 -0
  54. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_agents.py +0 -0
  55. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_context.py +0 -0
  56. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_env.py +0 -0
  57. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_fs.py +0 -0
  58. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_hooks.py +0 -0
  59. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_ignore.py +0 -0
  60. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_launchers.py +0 -0
  61. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_merge.py +0 -0
  62. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/cmds/launch.py +0 -0
  63. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/hooks/preinvocation_antigravity_context.py +0 -0
  64. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/hooks/pretooluse_codex_env.py +0 -0
  65. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/hooks/sessionstart_codex_context.py +0 -0
  66. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/templates/AGENTS.md +0 -0
  67. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/templates/PROJECT.md +0 -0
  68. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_pipwheel.py +0 -0
  69. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_resources.py +0 -0
  70. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_skills.py +0 -0
  71. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/_sources.py +0 -0
  72. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/cli/_common.py +0 -0
  73. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/cli/about.py +0 -0
  74. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/cli/context.py +0 -0
  75. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/cli/env.py +0 -0
  76. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/cli/init.py +0 -0
  77. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/cli/overlays.py +0 -0
  78. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/cli/path.py +0 -0
  79. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/src/dotagents/py.typed +0 -0
  80. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/_helpers.py +0 -0
  81. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/_shell.py +0 -0
  82. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/conftest.py +0 -0
  83. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_about.py +0 -0
  84. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_agents.py +0 -0
  85. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_api_header.py +0 -0
  86. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_build_pyz.py +0 -0
  87. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_cloud_setup.py +0 -0
  88. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_context.py +0 -0
  89. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_env.py +0 -0
  90. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_env_cli_scope.py +0 -0
  91. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_env_format.py +0 -0
  92. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_env_into.py +0 -0
  93. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_env_regressions.py +0 -0
  94. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_env_safety.py +0 -0
  95. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_fix_adapters.py +0 -0
  96. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_fix_cli.py +0 -0
  97. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_fix_env.py +0 -0
  98. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_fix_hooks.py +0 -0
  99. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_fix_init.py +0 -0
  100. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_fix_overlays.py +0 -0
  101. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_fix_packaging.py +0 -0
  102. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_fix_skills_adapters.py +0 -0
  103. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_git_ssl_revoke.py +0 -0
  104. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_hooks.py +0 -0
  105. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_ignore.py +0 -0
  106. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_init_base.py +0 -0
  107. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_last_mile.py +0 -0
  108. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_launch_cmd.py +0 -0
  109. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_launchers.py +0 -0
  110. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_local_additions.py +0 -0
  111. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_no_internal_labels.py +0 -0
  112. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_overlay_libs.py +0 -0
  113. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_overlays_cli.py +0 -0
  114. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_overlays_command.py +0 -0
  115. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_path_timeout_frontmatter.py +0 -0
  116. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_pipwheel.py +0 -0
  117. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_powershell_env_hook.py +0 -0
  118. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_pyvenv_core.py +0 -0
  119. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_quoting_injection_safety.py +0 -0
  120. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_scope.py +0 -0
  121. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_sources.py +0 -0
  122. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_sources_uri.py +0 -0
  123. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tests/test_wire_hooks.py +0 -0
  124. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tools/audit.py +0 -0
  125. {dotagents_cli-0.8.1 → dotagents_cli-0.8.3}/tools/cloud-setup.sh +0 -0
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.3] - 2026-10-30
11
+
12
+ ### Added
13
+
14
+ - `dotagents mcp` starts the stdio MCP gateway and can forward tools from
15
+ overlay-declared stdio and Streamable HTTP servers. Forwarded names default
16
+ to `<skill-or-server-name>.<tool>`; `--prefix` accepts a `{name}` template.
17
+ `dotagents.mcp.refresh` reloads active overlay declarations and reconnects
18
+ servers without restarting dotagents.
19
+ Overlays declare servers and skill mappings in `overlay.toml` under `[mcp]`;
20
+ Streamable HTTP requires the `mcp-http` extra, and credentials are read from
21
+ named environment variables.
22
+
23
+ ### Changed
24
+
25
+ - Requires duho 0.7.9 (its `ToolProvider` serves the forwarded tools), also in
26
+ the scope venvs and the `.pyz`.
27
+
28
+ ## [0.8.2] - 2026-10-25
29
+
30
+ ### Fixed
31
+
32
+ - `findings` (and `init` / `overlays`) run from inside a project's `.agents`, such as
33
+ `<project>/.agents/findings`, target that project. They used to write a second
34
+ `.agents` inside the store. Deeper than a top-level directory of the user store,
35
+ where no project can be named, they refuse instead of writing there.
36
+ - `findings` keeps a hand-written `INDEX.md`: before the first regeneration it is
37
+ copied to `INDEX.md.handwritten`, with a warning.
38
+ - The routing note above overlay paths no longer suggests `dotagents env` for
39
+ reading an overlay root (it prints the whole environment, secrets included); it
40
+ says to read just the variable.
41
+
42
+ - The `sh` wrapper `init` writes into `<store>/bin/dotagents` works from another
43
+ system that shares the store, such as WSL reading a Windows store. It ran the
44
+ interpreter recorded by `init`, a Windows path there; where that interpreter
45
+ does not run it now takes the store's agents' venv for that OS and machine
46
+ (`.pyvenv/<version>-<os>-<arch>`), else `python3`, and converts a recorded
47
+ `C:/...` pyz path with `wslpath`.
48
+
10
49
  ## [0.8.1] - 2026-10-21
11
50
 
12
51
  ### Added
@@ -1546,7 +1585,9 @@ Patch release: the PATH/POSIX-conversion fix above (the only change since 0.3.0)
1546
1585
  affected module sources (`dotagents.cli`, `duho.presets`) to extracted temp files
1547
1586
  before dispatch; a no-op for a plain install.
1548
1587
 
1549
- [Unreleased]: https://github.com/jose-pr/dotagents/compare/v0.8.1...HEAD
1588
+ [Unreleased]: https://github.com/jose-pr/dotagents/compare/v0.8.3...HEAD
1589
+ [0.8.3]: https://github.com/jose-pr/dotagents/compare/v0.8.2...v0.8.3
1590
+ [0.8.2]: https://github.com/jose-pr/dotagents/compare/v0.8.1...v0.8.2
1550
1591
  [0.8.1]: https://github.com/jose-pr/dotagents/compare/v0.8.0...v0.8.1
1551
1592
  [0.8.0]: https://github.com/jose-pr/dotagents/compare/v0.7.6...v0.8.0
1552
1593
  [0.7.6]: https://github.com/jose-pr/dotagents/compare/v0.7.5...v0.7.6
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dotagents-cli
3
- Version: 0.8.1
3
+ Version: 0.8.3
4
4
  Summary: The dotagents CLI: install and manage a portable ~/.agents config for AI coding agents
5
5
  Project-URL: Homepage, https://github.com/jose-pr/dotagents/
6
6
  Project-URL: Repository, https://github.com/jose-pr/dotagents
@@ -24,7 +24,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
24
  Classifier: Typing :: Typed
25
25
  Requires-Python: >=3.9
26
26
  Requires-Dist: distlib<0.5,>=0.4.0
27
- Requires-Dist: duho<0.8,>=0.7.5
27
+ Requires-Dist: duho<0.8,>=0.7.9
28
28
  Requires-Dist: pathapi<0.2,>=0.1.0
29
29
  Requires-Dist: tomli>=1.1.0; python_version < '3.11'
30
30
  Provides-Extra: dev
@@ -33,12 +33,15 @@ Requires-Dist: hatchling>=1.27; extra == 'dev'
33
33
  Requires-Dist: pathapi-uri<0.2,>=0.1.0; extra == 'dev'
34
34
  Requires-Dist: pytest; extra == 'dev'
35
35
  Requires-Dist: pyyaml; extra == 'dev'
36
+ Requires-Dist: syshttp<0.2,>=0.1.0; extra == 'dev'
36
37
  Provides-Extra: docs
37
38
  Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
38
39
  Requires-Dist: mkdocs>=1.5; extra == 'docs'
39
40
  Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
40
41
  Provides-Extra: http
41
42
  Requires-Dist: pathapi-uri<0.2,>=0.1.0; extra == 'http'
43
+ Provides-Extra: mcp-http
44
+ Requires-Dist: syshttp<0.2,>=0.1.0; extra == 'mcp-http'
42
45
  Provides-Extra: repo
43
46
  Requires-Dist: pathapi-uri<0.2,>=0.1.0; extra == 'repo'
44
47
  Requires-Dist: repopath[uri]<0.2,>=0.1.0; extra == 'repo'
@@ -27,10 +27,11 @@ otherwise.
27
27
  | `path` | Print the bin dirs `env` puts on `PATH` (or, with `--lib`, the lib dirs it puts on `PYTHONPATH`). |
28
28
  | `build-pyz` | Build the self-contained `dotagents.pyz` zipapp (from a source checkout). |
29
29
  | `about` | `dotagents-cli <version>`, then the packages bundled in the `.pyz` (or installed beside a plain install) with their versions. |
30
+ | `mcp [--prefix TEMPLATE]` | Serve dotagents command tools and active overlay MCP tools over stdio. |
30
31
  | `findings` | Per-scope findings queue: `add` / `list` / `show` / `done` / `reopen` / `remove` / `index` / `path`. |
31
32
  | `launch` | Start an agent's CLI with the `env` exported and the `context` handed over; everything after `--` goes to the agent. |
32
33
 
33
- That table is the **whole** shipped surface: six built-in commands and two bundled
34
+ That table is the **whole** shipped surface: eight built-in commands and two bundled
34
35
  command modules, `findings` and `launch` (discovered from the package itself). Every
35
36
  other command is **discovered** from a directory of command modules. The sources, in
36
37
  order — a later one overrides a same-named command from an earlier one:
@@ -61,14 +62,20 @@ subcommand, no registration needed; see
61
62
 
62
63
  ## As tools for an agent
63
64
 
64
- Every command above except `env`, and every discovered one, is also an **MCP tool**.
65
- Start `dotagents` with `DOTAGENTS_MCP=stdio` in its environment and it serves that
66
- command set over stdio instead of running a command:
65
+ Every command above except `env` and `mcp`, and every discovered one, is also an
66
+ **MCP tool**. `dotagents mcp` serves that command set over stdio; starting
67
+ `dotagents` with `DOTAGENTS_MCP=stdio` in its environment is the same server:
67
68
 
68
69
  ```sh
69
- DOTAGENTS_MCP=stdio dotagents # register this as a stdio MCP server in your agent
70
+ dotagents mcp # register this as a stdio MCP server in your agent
70
71
  ```
71
72
 
73
+ The same server forwards the tools of the MCP servers that active overlays declare
74
+ (see [Overlays → MCP servers](overlays.md#mcp-servers)), named
75
+ `<skill-or-server-name>.<tool>`; `--prefix 'dotagents.{name}'` puts them under
76
+ `dotagents.`. Calling `dotagents.mcp.refresh` re-reads those declarations without a
77
+ restart and sends `notifications/tools/list_changed`.
78
+
72
79
  - One tool per leaf command, named with dots: `dotagents.findings.add`,
73
80
  `dotagents.overlays.list`, and for an overlay's commands `dotagents.<command>`.
74
81
  - A tool's input schema is generated from the command's fields; its result is what
@@ -205,7 +205,8 @@ contract is under [Commands → env](commands.md#env).
205
205
 
206
206
  The manifest is read by the `overlays` command and the context assembler, with
207
207
  `tomllib` (Python 3.11+) or `tomli` when either is installed, and otherwise with a
208
- small built-in reader that covers these keys. Keys:
208
+ small built-in reader that covers these keys (all but `[mcp]`, which needs one of
209
+ the two). Keys:
209
210
 
210
211
  | Key | Meaning |
211
212
  | --- | --- |
@@ -217,11 +218,58 @@ small built-in reader that covers these keys. Keys:
217
218
  | `priority` | Merge order (lower sorts earlier; unprioritized default is 500). |
218
219
  | `setup_timeout` | Seconds the setup script may run before it is stopped (default 300; `0` = no limit). |
219
220
  | `[scripts]` | Launchers on `PATH`: `name = "path/in/overlay.py"` adds or replaces one, `name = false` drops the one for a `bin/<name>.py`. See [Authoring → Commands on PATH](authoring.md#commands-on-path-binpy-and-scripts). |
221
+ | `[mcp]` | `[[mcp.servers]]` and `[[mcp.skills]]` declare MCP endpoints and skills that expose them. See [MCP servers](#mcp-servers). |
220
222
 
221
223
  `overlay.toml` is the only manifest. A leading `---` frontmatter block in a
222
224
  `CONTEXT.md` (the `priority:` / `scope:` / `dependencies:` an older overlay format
223
225
  kept there) is not read, and `context` leaves it out of what it assembles.
224
226
 
227
+ ## MCP servers
228
+
229
+ `dotagents mcp` forwards the tools of the MCP servers an overlay declares. Each
230
+ `[[mcp.servers]]` entry has a `name`, a `transport` and an optional `timeout` in
231
+ seconds (default 60):
232
+
233
+ | `transport` | Keys |
234
+ | --- | --- |
235
+ | `stdio` | `command`, `args`; `env` sets values (not secrets: they sit in the manifest); `env_passthrough` names variables passed on from the environment. |
236
+ | `streamable-http` | `url` (no credentials, no fragment); `headers_env` maps a header name to the variable holding its value. Needs `dotagents-cli[mcp-http]`. |
237
+
238
+ ```toml
239
+ [mcp]
240
+
241
+ [[mcp.servers]]
242
+ name = "builder"
243
+ transport = "stdio"
244
+ command = "uvx"
245
+ args = ["builder-mcp"]
246
+ env = { BUILDER_MCP = "stdio" }
247
+ env_passthrough = ["BUILDER_TOKEN"]
248
+ timeout = 60
249
+
250
+ [[mcp.servers]]
251
+ name = "docs"
252
+ transport = "streamable-http"
253
+ url = "https://docs.example/mcp"
254
+ headers_env = { Authorization = "DOCS_TOKEN" }
255
+ timeout = 30
256
+
257
+ [[mcp.skills]]
258
+ name = "code-search"
259
+ server = "builder"
260
+ ```
261
+
262
+ A `[[mcp.skills]]` entry names a skill of the overlay (`skills/code-search/SKILL.md`
263
+ must exist) and the server behind it; that server's tools are then listed as
264
+ `code-search.<tool>` instead of `builder.<tool>`, once per skill when it backs
265
+ several. `dotagents mcp --prefix 'dotagents.{name}'` puts every forwarded name under
266
+ `dotagents.`.
267
+
268
+ A stdio server gets `PATH`, `HOME`, `USERPROFILE`, `SYSTEMROOT`, `TEMP` and `TMP`,
269
+ the variables in `env_passthrough`, and `env`; nothing else of the environment. An
270
+ invalid `[mcp]` table in any active overlay stops `dotagents mcp` from starting; a
271
+ server that fails to start is left out with a warning. Only tools are forwarded.
272
+
225
273
  ## Skills
226
274
 
227
275
  An overlay may ship `skills/<skill-name>/` directories. Publishing symlinks each (or
@@ -11,13 +11,19 @@ overlay is what makes them exist; a plain dotagents has no private-sync surface
11
11
 
12
12
  ## The idea
13
13
 
14
- Your global config store **is** a private git repo. Its root is the per-user config;
15
- a `projects/<name>/` tree inside it holds each project's private payload. For a
16
- checked-out project, its `.agents` directory is a **symlink** into the store (the
17
- project's `.gitignore` already excludes `.agents` per the Leakage rule, so the link
14
+ Your global config store **is** a private git repo, and its `main` branch is the
15
+ per-user config. Each project's private payload is a **branch** of it, named after
16
+ the project, and the project's `.agents` directory is a git worktree on that branch
17
+ (the project's `.gitignore` already excludes `.agents` per the Leakage rule, so it
18
18
  never lands in the public repo). `<name>` defaults to the project's basename, so a
19
- local checkout and a cloud checkout of the same project resolve to the same store
20
- entry.
19
+ local checkout and a cloud checkout of the same project resolve to the same branch.
20
+
21
+ That branch can live elsewhere: `link-project --store-repo self` keeps it on the
22
+ project's own repository (branch `agents`), and `--store-repo <url>` on any other,
23
+ such as a team account's (cloned into `.agents`); `--store-branch` names the branch.
24
+ The choice is recorded in the config repo, so every machine links the project the
25
+ same way. A store kept as a `projects/<name>/` directory, symlinked into the
26
+ project, still works and is kept by a project that already has one.
21
27
 
22
28
  The store's installed overlays can travel two ways:
23
29
 
@@ -39,11 +45,12 @@ dotagents overlays add private-sync -g \
39
45
  --repo "https://github.com/jose-pr/dotagents.git@repo#overlays" # commands + kb + hooks
40
46
 
41
47
  # link-project / sync-project come FROM that overlay -- run `overlays add` first.
42
- dotagents link-project . # symlink this project's .agents into its store
48
+ dotagents link-project . # check this project's store branch out at .agents
43
49
  # (an existing real .agents/ is adopted on the
44
- # first link; --copy mirrors it as a real dir
45
- # for no-symlink systems)
46
- dotagents sync-project -m "msg" # git pull --rebase / commit / push the store
50
+ # first link)
51
+ dotagents link-project . --store-repo self # ...on the project's own repo
52
+ dotagents link-project . --store-repo <team-repo-url> # ...on another repo
53
+ dotagents sync-project --project . -m "msg" # commit / push the project's branch, then the config
47
54
  dotagents sync-project --remote <url> -m init # one-command bootstrap
48
55
  ```
49
56
 
@@ -35,7 +35,7 @@ classifiers = [
35
35
  # `build-pyz` vendors the floor of each of these (src/dotagents/cli/build_pyz.py);
36
36
  # raise a floor and move its pin there in the same change.
37
37
  dependencies = [
38
- "duho>=0.7.5,<0.8",
38
+ "duho>=0.7.9,<0.8",
39
39
  "pathapi>=0.1.0,<0.2",
40
40
  # The launchers of overlays' [scripts]: ScriptMaker writes the Windows .exe.
41
41
  "distlib>=0.4.0,<0.5",
@@ -51,13 +51,14 @@ dependencies = [
51
51
  uri = ["pathapi-uri>=0.1.0,<0.2"]
52
52
  # http(s) is part of `uri` now; kept so `dotagents-cli[http]` still installs.
53
53
  http = ["pathapi-uri>=0.1.0,<0.2"]
54
+ mcp-http = ["syshttp>=0.1.0,<0.2"]
54
55
  sftp = ["pathapi-uri>=0.1.0,<0.2", "sftppath[paramiko]>=0.1.0,<0.2"]
55
56
  s3 = ["pathapi-uri>=0.1.0,<0.2", "objpath[s3]>=0.1.0,<0.2"]
56
57
  # github:, gitlab: and repo: (a repository's tree at a ref).
57
58
  repo = ["pathapi-uri>=0.1.0,<0.2", "repopath[uri]>=0.1.0,<0.2"]
58
59
  # YAML overlay-repo registries (dotagents.yaml / *.yml).
59
60
  yaml = ["pyyaml"]
60
- dev = ["build", "hatchling>=1.27", "pytest", "pyyaml", "pathapi-uri>=0.1.0,<0.2"]
61
+ dev = ["build", "hatchling>=1.27", "pytest", "pyyaml", "pathapi-uri>=0.1.0,<0.2", "syshttp>=0.1.0,<0.2"]
61
62
  docs = [
62
63
  "mkdocs>=1.5",
63
64
  "mkdocs-material>=9.5",
@@ -43,17 +43,35 @@ and an error saying a command is required, and exits 2.
43
43
  | `findings add\|list\|show\|done\|reopen\|remove\|index\|path [-g] [--dir D]` | The findings queue at `<store>/findings/` (`--dir` overrides): one markdown file per finding, `done` moves it to `processed/` with a required resolution, `INDEX.md` regenerated on every change. The frontmatter is valid YAML (a value is double-quoted when it must be); `index --requote` fixes values written bare in existing files, touching nothing else. |
44
44
  | `about [--json]` | `dotagents-cli <version>`, then `<distribution> <version>` per bundled (`.pyz`) or installed package. |
45
45
  | `build-pyz [--out P] [--python SHEBANG] [--duho-version V] [--pathapi-version V] [--extras a,b] [--with-pip]` | Build a zipapp from a source checkout; errors elsewhere. `--with-pip` vendors the pinned pip wheel (`_pipwheel`) as `dotagents/_vendor/`. |
46
+ | `mcp [--prefix TEMPLATE]` | Serve command and active overlay MCP tools over stdio; `{name}` in the template is the skill or server name. |
46
47
 
47
- **Every command but `env` is also an MCP tool.** With `DOTAGENTS_MCP=stdio` in its
48
- environment, `dotagents` serves the command set — built-in, bundled and discovered —
49
- over stdio instead of running one: a tool per leaf command, named with dots
48
+ **Every command but `env` and `mcp` is also an MCP tool.** `dotagents mcp` (or
49
+ `DOTAGENTS_MCP=stdio dotagents`, the same server) serves the command set — built-in,
50
+ bundled and discovered — over stdio: a tool per leaf command, named with dots
50
51
  (`dotagents.findings.add`, `dotagents.overlays.list`), its input schema generated from
51
52
  the command's fields, its result the command's output. `env` is not served: it prints
52
- resolved, possibly secret values. The set is fixed when the server starts, for the
53
- scope it starts in. With `AGENT_HELP=1`, `--help` prints one
53
+ resolved, possibly secret values. The command set is fixed when the server starts, for
54
+ the scope it starts in. With `AGENT_HELP=1`, `--help` prints one
54
55
  JSON document (`duho/agent-help@1`) for the command tree it is asked about, in place
55
56
  of the human text.
56
57
 
58
+ The same server forwards the tools of the MCP servers active overlays declare in
59
+ `overlay.toml` (`[[mcp.servers]]`, `[[mcp.skills]]`; keys in
60
+ `docs/guide/overlays.md`). Forwarded names are `<skill-or-server-name>.<tool>`, or
61
+ `--prefix`'s template with `{name}` filled in, plus `.<tool>`. Gotchas:
62
+
63
+ - An invalid `[mcp]` table in any active overlay stops the server from starting
64
+ (exit 2, the reason on stderr).
65
+ - Overlay servers start on the first `tools/list`; one that fails to start, or a tool
66
+ whose name collides with one already listed, is left out with a warning.
67
+ - A stdio server's environment is `PATH`, `HOME`, `USERPROFILE`, `SYSTEMROOT`,
68
+ `TEMP`, `TMP`, the variables named in `env_passthrough`, then `env`. An HTTP
69
+ server's credentials come only from the variables `headers_env` names.
70
+ - Streamable HTTP needs the `mcp-http` extra (`syshttp`).
71
+ - `dotagents.mcp.refresh` re-reads the declarations, closes every connection and
72
+ sends `notifications/tools/list_changed`; the next `tools/list` reconnects.
73
+ - Only tools are forwarded: not resources, prompts, or a server's own requests.
74
+
57
75
  Scope flags: `-g` / `--global` selects the user store; `--agents-dir D` overrides the
58
76
  store the command resolves to (the project store for `init` / `overlays` / `findings`
59
77
  without `-g`, the user store otherwise). For `env`, `context` and `launch`, `-g` means
@@ -152,7 +170,7 @@ The package's own data; library code, importing nothing from `dotagents.cli`.
152
170
  install. The archive also holds the vendored duho and pathapi; `env`
153
171
  exports it as `AGENTS_PYLIB`.
154
172
 
155
- ## Commands — `dotagents.cli.init`, `dotagents.cli.overlays`, `dotagents.cli.context`, `dotagents.cli.env`, `dotagents.cli.path`, `dotagents.cli.about`, `dotagents.cli.build_pyz`
173
+ ## Commands — `dotagents.cli.init`, `dotagents.cli.overlays`, `dotagents.cli.context`, `dotagents.cli.env`, `dotagents.cli.path`, `dotagents.cli.about`, `dotagents.cli.build_pyz`, `dotagents.cli.mcp`
156
174
 
157
175
  One `duho` command class per command above; fields are the flags, `__call__()`
158
176
  returns the exit code.
@@ -176,10 +194,28 @@ returns the exit code.
176
194
  `bundle_manifest() -> Optional[dict]`, `installed_packages() -> dict[str, str]`,
177
195
  `packages() -> tuple[dict[str, str], Optional[dict]]` (the bundle's packages, else
178
196
  the installed ones; the CLI itself in neither).
197
+ - `mcp.Mcp` — `prefix: Optional[str]`; exits 2 when the template uses a placeholder
198
+ other than `{name}` or does not format to a tool-name segment.
179
199
  - `build_pyz`: `EXTRA_REQUIREMENTS` — `{extra: (requirement, ...)}`, what each
180
- dotagents-cli extra installs (`uri`, `http`, `sftp`, `s3`, `repo`), kept equal to
181
- `pyproject.toml`; `--extras` vendors each requirement at its floor and refuses a
182
- name not in it.
200
+ dotagents-cli extra installs (`uri`, `http`, `mcp-http`, `sftp`, `s3`, `repo`),
201
+ kept equal to `pyproject.toml`; `--extras` vendors each requirement at its floor
202
+ and refuses a name not in it.
203
+
204
+ ## `dotagents._mcp_gateway`, `dotagents._mcp_gateway_config`, `dotagents._mcp_gateway_clients`
205
+
206
+ - `_mcp_gateway.Gateway(notify, load_specs, name_prefix=None)` — the
207
+ `duho.mcp.ToolProvider` the root's `_mcp_tools_` (`cli._mcp_tools`) builds:
208
+ `list_tools()` (`dotagents.mcp.refresh` and the forwarded tools; servers start on
209
+ the first call), `call_tool(name, arguments)` (`None` for a name it does not
210
+ forward), `close()` (every connection). `load_specs()` runs in the
211
+ constructor, so a `GatewayConfigError` ends `duho.mcp.serve` with status 2 before
212
+ the first request. duho's server answers everything else (`-32602` for an unknown
213
+ tool or bad arguments, bad arguments a tool result from protocol `2025-11-25` on).
214
+ - `_mcp_gateway_config.discover(scope) -> list` — the validated server declarations
215
+ of the scope's active overlays; raises `GatewayConfigError` (a `ValueError`) on
216
+ the first invalid one, or on a `[[mcp.skills]]` entry without
217
+ `skills/<name>/SKILL.md`.
218
+ - `_mcp_gateway_clients` — the stdio and Streamable HTTP clients; private.
183
219
 
184
220
  ## `dotagents._scope`
185
221
 
@@ -276,7 +312,8 @@ returns the exit code.
276
312
  `setup_timeout`, `scripts` (the `[scripts]` table as written: name -> path
277
313
  relative to the overlay, `/`-separated, plain file-name characters, no `..`,
278
314
  or `false`; any other entry is dropped with a warning); a missing or invalid
279
- manifest gives empty contributions.
315
+ manifest gives empty contributions. `[mcp]` is read by
316
+ `_mcp_gateway_config.discover`, not here.
280
317
  - `scripts() -> dict[str, str]` — the scripts that get a launcher
281
318
  (`dotagents._launchers`): each top-level `bin/<name>.py` whose stem is a
282
319
  `SCRIPT_NAME_RE` name (so not `_private.py`, not `__init__.py`), then
@@ -722,8 +759,11 @@ objects `{"matcher"?: str, "hooks": [{"type": "command", "command": str, ...}]}`
722
759
 
723
760
  ## `dotagents._wrappers`
724
761
 
725
- - `POSIX_TEMPLATE`, `POSIX_TEMPLATE_REL` (pyz path relative to the wrapper),
726
- `POSIX_TEMPLATE_MODULE` (`"<python>" -m dotagents`) — the `sh` wrapper bodies.
762
+ - `POSIX_TEMPLATE` — the `sh` wrapper body, for a `.pyz` (absolute, or relative
763
+ to the wrapper) or `-m dotagents`. It runs the recorded interpreter; where that
764
+ does not run (a Windows store read from WSL) it takes the store's agents' venv
765
+ for this OS and machine (`.pyvenv/*-<os>-<arch>/bin/python`), else `python3`,
766
+ and turns a recorded `C:/...` pyz path into a local one with `wslpath`.
727
767
  - `write_wrappers(bin_dir, pyz_path, python=None, *, relative=False) -> list[Path]` —
728
768
  `dotagents` (sh) and `dotagents.cmd`, both always, running the `.pyz`.
729
769
  - `write_module_wrappers(bin_dir, python=None) -> list[Path]` — both forms running
@@ -739,7 +779,7 @@ interpreter; layered project > user > system through a `.pth`. `.pyvenv/` holds
739
779
  agents' venvs only (skills, overlay helpers, setup scripts), never a project's own
740
780
  code. `AGENTS_NO_PYVENV=1` (or `true`/`yes`/`on`) turns it all off.
741
781
 
742
- - `BASE_REQUIREMENTS = ("duho>=0.7.5,<0.8",)` — always in the user venv; dotagents'
782
+ - `BASE_REQUIREMENTS = ("duho>=0.7.9,<0.8",)` — always in the user venv; dotagents'
743
783
  own duho range. `REQUIREMENTS_FILE = "requirements.txt"` (at an overlay's root),
744
784
  `PYVENV_DIR = ".pyvenv"`, `STATE_FILE = ".dotagents-venv.json"` (in the venv:
745
785
  `{"digest", "requirements", "pth"}`), `PTH_FILE = "dotagents-layers.pth"` (in its
@@ -1,3 +1,3 @@
1
1
  """dotagents: installable CLI for the dotagents agent-config payload."""
2
2
 
3
- __version__ = "0.8.1"
3
+ __version__ = "0.8.3"
@@ -0,0 +1,116 @@
1
+ """Forward the tools of overlay-declared MCP servers through duho's MCP server."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+
7
+ from dotagents._mcp_gateway_clients import (
8
+ _HTTPClient,
9
+ _StdioClient,
10
+ _prefix_tools,
11
+ _server_error,
12
+ )
13
+ from dotagents._mcp_gateway_config import GatewayConfigError, discover # noqa: F401 (re-exported)
14
+
15
+ _LOG = logging.getLogger("dotagents.mcp")
16
+ _REFRESH_TOOL = "dotagents.mcp.refresh"
17
+ _REFRESH_SPEC = {
18
+ "name": _REFRESH_TOOL,
19
+ "description": "Reload active overlay MCP declarations and reconnect their servers.",
20
+ "inputSchema": {"type": "object", "properties": {}, "additionalProperties": False},
21
+ }
22
+
23
+
24
+ def _text_result(text, is_error=False):
25
+ result = {"content": [{"type": "text", "text": text}]}
26
+ if is_error:
27
+ result["isError"] = True
28
+ return result
29
+
30
+
31
+ class Gateway:
32
+ """A `duho.mcp.ToolProvider` over the servers `load_specs()` declares.
33
+
34
+ Servers start on the first `list_tools`; one that fails to start, or a tool
35
+ whose name is already taken, is left out with a warning.
36
+ """
37
+
38
+ def __init__(self, notify, load_specs, name_prefix=None):
39
+ self._notify = notify
40
+ self._load_specs = load_specs
41
+ self._name_prefix = name_prefix
42
+ self._specs = load_specs()
43
+ self._clients = []
44
+ self._tool_map = {}
45
+ self._tools = []
46
+ self._connected = False
47
+
48
+ def _connect(self):
49
+ self._connected = True
50
+ for spec in self._specs:
51
+ client = None
52
+ try:
53
+ client = _StdioClient(spec) if spec.transport == "stdio" else _HTTPClient(spec)
54
+ client.start()
55
+ candidates = [
56
+ pair
57
+ for expose_as in spec.skills or [spec.name]
58
+ for pair in _prefix_tools(
59
+ spec, client, spec.prefix(expose_as, self._name_prefix)
60
+ )
61
+ ]
62
+ except Exception as exc:
63
+ if client is not None:
64
+ client.close()
65
+ reason = _server_error(exc, spec)[:500]
66
+ _LOG.warning("MCP server %s.%s unavailable (%s)", spec.overlay, spec.name, reason)
67
+ continue
68
+ for tool, remote_name in candidates:
69
+ name = tool["name"]
70
+ if name == _REFRESH_TOOL or name in self._tool_map:
71
+ _LOG.warning("MCP server %s.%s has a colliding tool name", spec.overlay, spec.name)
72
+ continue
73
+ self._tool_map[name] = (client, remote_name)
74
+ self._tools.append(tool)
75
+ self._clients.append(client)
76
+
77
+ def list_tools(self):
78
+ if not self._connected:
79
+ self._connect()
80
+ return [_REFRESH_SPEC] + self._tools
81
+
82
+ def call_tool(self, name, arguments):
83
+ if name == _REFRESH_TOOL:
84
+ return self._refresh()
85
+ target = self._tool_map.get(name)
86
+ if target is None:
87
+ return None
88
+ client, remote_name = target
89
+ try:
90
+ value = client.call(remote_name, arguments)
91
+ except Exception as exc:
92
+ return _text_result(_server_error(exc, client.spec), is_error=True)
93
+ if not isinstance(value, dict) or "content" not in value:
94
+ return _text_result("tool returned an invalid result", is_error=True)
95
+ return value
96
+
97
+ def _refresh(self):
98
+ try:
99
+ specs = self._load_specs()
100
+ except Exception as exc:
101
+ return _text_result("Could not reload MCP configuration: %s" % exc, is_error=True)
102
+ self.close()
103
+ self._specs = specs
104
+ self._notify("notifications/tools/list_changed")
105
+ return _text_result(
106
+ "Reloaded %d declared MCP server(s); request tools/list to reconnect them."
107
+ % len(specs)
108
+ )
109
+
110
+ def close(self):
111
+ for client in self._clients:
112
+ client.close()
113
+ self._clients = []
114
+ self._tool_map = {}
115
+ self._tools = []
116
+ self._connected = False