dotagents-cli 0.8.2__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.
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/CHANGELOG.md +20 -1
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/PKG-INFO +5 -2
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/guide/commands.md +12 -5
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/guide/overlays.md +49 -1
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/pyproject.toml +3 -2
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/AGENTS.md +48 -11
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/__init__.py +1 -1
- dotagents_cli-0.8.3/src/dotagents/_mcp_gateway.py +116 -0
- dotagents_cli-0.8.3/src/dotagents/_mcp_gateway_clients.py +385 -0
- dotagents_cli-0.8.3/src/dotagents/_mcp_gateway_config.py +168 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_overlays.py +1 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_pyvenv.py +1 -1
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/cli/__init__.py +14 -1
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/cli/build_pyz.py +3 -2
- dotagents_cli-0.8.3/src/dotagents/cli/mcp.py +36 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_audit_leak.py +1 -1
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_cli_main.py +1 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_cmds_discovery.py +5 -3
- dotagents_cli-0.8.3/tests/test_mcp.py +368 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tools/pyz_smoke.sh +1 -1
- dotagents_cli-0.8.2/tests/test_mcp.py +0 -60
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/.gitignore +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/LICENSE +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/README.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/agents.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/cli.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/commands.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/context.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/env.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/fs.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/hooks.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/merge.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/overlays.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/reference.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/resources.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/scope.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/skills.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/sources.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/api/wrappers.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/changelog.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/guide/authoring.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/guide/install.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/guide/private-sync.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/docs/index.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/install.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/mkdocs.yml +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/__main__.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_agents.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_context.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_env.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_fs.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_hooks.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_ignore.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_launchers.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_merge.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/cmds/findings.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/cmds/launch.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/hooks/preinvocation_antigravity_context.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/hooks/pretooluse_codex_env.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/hooks/sessionstart_codex_context.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/templates/AGENTS.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_overlay/dotagents/templates/PROJECT.md +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_pipwheel.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_resources.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_scope.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_skills.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_sources.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/_wrappers.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/cli/_common.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/cli/about.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/cli/context.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/cli/env.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/cli/init.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/cli/overlays.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/cli/path.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/src/dotagents/py.typed +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/_helpers.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/_shell.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/conftest.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_about.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_agents.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_api_header.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_build_pyz.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_cloud_setup.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_context.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_env.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_env_cli_scope.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_env_format.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_env_into.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_env_regressions.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_env_safety.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_findings_cmd.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_fix_adapters.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_fix_cli.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_fix_env.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_fix_hooks.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_fix_init.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_fix_overlays.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_fix_packaging.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_fix_scope_context.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_fix_skills_adapters.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_git_ssl_revoke.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_hooks.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_ignore.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_init_base.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_last_mile.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_launch_cmd.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_launchers.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_local_additions.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_no_internal_labels.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_overlay_libs.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_overlays_cli.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_overlays_command.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_path_timeout_frontmatter.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_pipwheel.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_powershell_env_hook.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_pyvenv_core.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_quoting_injection_safety.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_scope.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_sources.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_sources_uri.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_wire_hooks.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tests/test_wrappers.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tools/audit.py +0 -0
- {dotagents_cli-0.8.2 → dotagents_cli-0.8.3}/tools/cloud-setup.sh +0 -0
|
@@ -7,6 +7,24 @@ 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
|
+
|
|
10
28
|
## [0.8.2] - 2026-10-25
|
|
11
29
|
|
|
12
30
|
### Fixed
|
|
@@ -1567,7 +1585,8 @@ Patch release: the PATH/POSIX-conversion fix above (the only change since 0.3.0)
|
|
|
1567
1585
|
affected module sources (`dotagents.cli`, `duho.presets`) to extracted temp files
|
|
1568
1586
|
before dispatch; a no-op for a plain install.
|
|
1569
1587
|
|
|
1570
|
-
[Unreleased]: https://github.com/jose-pr/dotagents/compare/v0.8.
|
|
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
|
|
1571
1590
|
[0.8.2]: https://github.com/jose-pr/dotagents/compare/v0.8.1...v0.8.2
|
|
1572
1591
|
[0.8.1]: https://github.com/jose-pr/dotagents/compare/v0.8.0...v0.8.1
|
|
1573
1592
|
[0.8.0]: https://github.com/jose-pr/dotagents/compare/v0.7.6...v0.8.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: dotagents-cli
|
|
3
|
-
Version: 0.8.
|
|
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.
|
|
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:
|
|
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
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
@@ -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.
|
|
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.**
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
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`),
|
|
181
|
-
`pyproject.toml`; `--extras` vendors each requirement at its floor
|
|
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
|
|
@@ -742,7 +779,7 @@ interpreter; layered project > user > system through a `.pth`. `.pyvenv/` holds
|
|
|
742
779
|
agents' venvs only (skills, overlay helpers, setup scripts), never a project's own
|
|
743
780
|
code. `AGENTS_NO_PYVENV=1` (or `true`/`yes`/`on`) turns it all off.
|
|
744
781
|
|
|
745
|
-
- `BASE_REQUIREMENTS = ("duho>=0.7.
|
|
782
|
+
- `BASE_REQUIREMENTS = ("duho>=0.7.9,<0.8",)` — always in the user venv; dotagents'
|
|
746
783
|
own duho range. `REQUIREMENTS_FILE = "requirements.txt"` (at an overlay's root),
|
|
747
784
|
`PYVENV_DIR = ".pyvenv"`, `STATE_FILE = ".dotagents-venv.json"` (in the venv:
|
|
748
785
|
`{"digest", "requirements", "pth"}`), `PTH_FILE = "dotagents-layers.pth"` (in its
|
|
@@ -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
|