toolfuncs 1.0.0__tar.gz → 1.2.0__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.
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/PKG-INFO +52 -9
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/README.md +51 -8
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/docs/contract.md +8 -2
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/pyproject.toml +1 -1
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/__main__.py +37 -5
- toolfuncs-1.2.0/src/toolfuncs/_catalog.py +447 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/_contract.py +8 -3
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/_mcp.py +37 -3
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/_packages.py +11 -2
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/_tools.py +1 -1
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/agent_hooks.py +36 -5
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/sdk.py +12 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/tests/test_agent_hooks.py +21 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/tests/test_packages.py +71 -0
- toolfuncs-1.2.0/tests/test_path_mcp.py +306 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/uv.lock +1 -1
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/.github/workflows/release.yml +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/.github/workflows/test.yml +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/.gitignore +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/.python-version +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/docs/migrating-to-1.0.md +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/__init__.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/_descriptions.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/_doctor.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/_errors.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/_import_path.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/_runtime.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/_setup.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/src/toolfuncs/py.typed +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/tests/__init__.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/tests/conftest.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/tests/test_contract.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/tests/test_doctor.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/tests/test_import_path.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/tests/test_managed_runtime.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/tests/test_mcp.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/tests/test_sdk_cli.py +0 -0
- {toolfuncs-1.0.0 → toolfuncs-1.2.0}/tests/test_tools.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: toolfuncs
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Typed Python functions with native Python, CLI, and MCP interfaces
|
|
5
5
|
Project-URL: Repository, https://github.com/nimashoghi/toolfuncs
|
|
6
6
|
Project-URL: Issues, https://github.com/nimashoghi/toolfuncs/issues
|
|
@@ -185,24 +185,55 @@ Function commands inherit help and error presentation from their parent app. All
|
|
|
185
185
|
|
|
186
186
|
## Use MCP
|
|
187
187
|
|
|
188
|
-
Register
|
|
188
|
+
Register one server to expose the effective PATH catalog. Scripts and opted-in package executables remain separate tool families:
|
|
189
189
|
|
|
190
190
|
```json
|
|
191
191
|
{
|
|
192
192
|
"mcpServers": {
|
|
193
|
-
"
|
|
193
|
+
"toolfuncs": {
|
|
194
194
|
"command": "/absolute/path/to/toolfuncs",
|
|
195
|
-
"args": ["mcp"
|
|
195
|
+
"args": ["mcp"]
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
```
|
|
200
200
|
|
|
201
|
-
`toolfuncs mcp
|
|
201
|
+
`toolfuncs mcp` captures PATH and starts one persistent child per family, using each source's isolated dependency environment. Children inherit the launch directory and environment. Startup is bounded to 120 seconds, including dependency preparation, with at most eight families starting concurrently. Configure the client's startup timeout to at least 180 seconds for cold installations. Failed families appear in `catalog` with an error; healthy families remain callable. `toolfuncs doctor` loads all families and exits nonzero if any are unavailable. It does not invoke their operations. Use `toolfuncs doctor SOURCE` for the full source documentation and help checks.
|
|
202
|
+
|
|
203
|
+
The server's operation catalog remains fixed until restart. A crashed child becomes unavailable and is not restarted automatically. Interrupted operations are never replayed, since their side effects may already have occurred. Ordinary function exceptions remain tool errors and leave the family available. Shutdown closes the children. Installing tools, changing PATH, or updating source/dependencies requires restarting the server.
|
|
204
|
+
|
|
205
|
+
`toolfuncs mcp SOURCE` remains available to serve one source or opted-in package command. Use `--prepared` only when the selected interpreter already contains every selected source's dependencies. Toolfuncs does not edit client configuration.
|
|
206
|
+
|
|
207
|
+
### Progressive discovery
|
|
208
|
+
|
|
209
|
+
The hook advertises families. Inspect one group at a time, then one selected operation:
|
|
210
|
+
|
|
211
|
+
```console
|
|
212
|
+
toolfuncs catalog
|
|
213
|
+
toolfuncs commands weather-report
|
|
214
|
+
toolfuncs commands weather-report --group '["records"]'
|
|
215
|
+
toolfuncs inspect-operation weather-report '["records", "show"]'
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The generic MCP server exposes these same three discovery functions as `catalog`, `commands`, and `inspect_operation`. Group views contain only immediate child names and short descriptions. Selected operation views contain the original input/output schemas, documentation, canonical CLI argv, MCP name, and a Python attribute when it is verifiably exported by the source module. Do not reconstruct Python access from an MCP name: registered aliases and nested CLI groups need not match Python attributes.
|
|
219
|
+
|
|
220
|
+
Python uses the same bound functions:
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from toolfuncs.sdk import ToolCatalog
|
|
224
|
+
|
|
225
|
+
async with ToolCatalog() as catalog:
|
|
226
|
+
families = catalog.catalog() # static discovery, no child startup
|
|
227
|
+
await catalog.start(["weather-report"])
|
|
228
|
+
children = catalog.commands("weather-report")
|
|
229
|
+
detail = catalog.inspect_operation("weather-report", ["records", "show"])
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
In Codex Code Mode, use toolfuncs discovery to select an operation, resolve its runtime name from `ALL_TOOLS`, and invoke `tools[name](arguments)`. Retain deferred MCP exposure; native tool search is unnecessary. In Claude Code, keep the server's tools deferred (`"alwaysLoad": false` on supported versions), load the selected tool with `ToolSearch` query `select:mcp__toolfuncs__NAME`, then call it normally. Claude still exposes operation names initially. These context policies belong to the client; generic MCP clients may expose all schemas. For the agent hook's client-specific guidance, set `TOOLFUNCS_MCP_SERVER=toolfuncs` in the host environment when that server is registered.
|
|
202
233
|
|
|
203
234
|
For programmatic use, `server = app.to_mcp()` returns a FastMCP server. Sync functions use FastMCP's thread dispatch and async functions are awaited. FastMCP owns request handling and cancellation. MCP calls do not share live state with an IPython kernel. The function implementation is shared.
|
|
204
235
|
|
|
205
|
-
A nested CLI command such as `records show` becomes `records__show`;
|
|
236
|
+
A nested CLI command such as `records show` becomes `records__show`; the PATH server prefixes the family, for example `weather_report__records__show`. Hyphens become underscores. Aliases remain CLI aliases. Collisions and names exceeding 64 characters fail explicitly. Every successful MCP result has structured content `{"result": value}`, including scalar and null results, plus JSON text content. Exceptions become MCP tool errors.
|
|
206
237
|
|
|
207
238
|
Use stderr for logs and diagnostics. Stdout belongs to the CLI result or MCP protocol; an operation that prints to stdout can corrupt that stream. Durable background tasks, push notifications, remote hosting, resources and prompts are outside the 1.0 interface.
|
|
208
239
|
|
|
@@ -234,7 +265,7 @@ Declare `toolfuncs>=1,<2` in PEP 723 dependencies to make the required major ver
|
|
|
234
265
|
|
|
235
266
|
The description, either `[tool.toolfuncs].description` or a package module's first docstring line, is the line agents read in `toolfuncs list` and session-start catalogs when they choose a tool. Write one sentence that says what the tool does for its caller. Keep implementation details, compatibility notes and contract caveats out of it; put them in later docstring text or in command docstrings.
|
|
236
267
|
|
|
237
|
-
Descriptions are static by default. A
|
|
268
|
+
Descriptions are static by default. A script whose available domain surface changes at runtime may opt into a last-known catalog description with `dynamic = true`:
|
|
238
269
|
|
|
239
270
|
```toml
|
|
240
271
|
[tool.toolfuncs]
|
|
@@ -262,6 +293,15 @@ Only functions registered on `app` become CLI commands. Imported functions and `
|
|
|
262
293
|
|
|
263
294
|
No `if __name__ == "__main__":` block is required. The operating system passes the source path to the shebang interpreter, and toolfuncs imports the source under its declared Python name before invoking its module-level app. Consequently, `weather-report ...` is the supported CLI while `python weather-report ...` merely defines the module and exits, or fails if its dependencies are not already installed.
|
|
264
295
|
|
|
296
|
+
Packaged tools opt in with an additional matching entry point:
|
|
297
|
+
|
|
298
|
+
```toml
|
|
299
|
+
[project.entry-points."toolfuncs.dynamic_description"]
|
|
300
|
+
my-tool = "my_package.tools:app"
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Keep the matching `project.scripts` and `project.entry-points.toolfuncs` declarations. Publish descriptions through the same `toolsdk.publish_description` API. Discovery reads the current user's published line on each call and falls back to the module docstring when no valid line exists; it does not import the tool or access its service.
|
|
304
|
+
|
|
265
305
|
## Package-backed implementations
|
|
266
306
|
|
|
267
307
|
A toolfunc remains one script even when most of its implementation lives in a package:
|
|
@@ -332,9 +372,12 @@ Setup does not discover, copy, register, link, synchronize, or remove tools. The
|
|
|
332
372
|
|
|
333
373
|
```console
|
|
334
374
|
toolfuncs SOURCE [ARGS...]
|
|
335
|
-
toolfuncs doctor SOURCE
|
|
336
|
-
toolfuncs mcp [--prepared] SOURCE
|
|
375
|
+
toolfuncs doctor [--prepared] [SOURCE]
|
|
376
|
+
toolfuncs mcp [--prepared] [SOURCE]
|
|
337
377
|
toolfuncs list
|
|
378
|
+
toolfuncs catalog
|
|
379
|
+
toolfuncs commands FAMILY [--group JSON_PATH]
|
|
380
|
+
toolfuncs inspect-operation FAMILY JSON_PATH
|
|
338
381
|
toolfuncs setup [--bin-dir DIRECTORY]
|
|
339
382
|
```
|
|
340
383
|
|
|
@@ -167,24 +167,55 @@ Function commands inherit help and error presentation from their parent app. All
|
|
|
167
167
|
|
|
168
168
|
## Use MCP
|
|
169
169
|
|
|
170
|
-
Register
|
|
170
|
+
Register one server to expose the effective PATH catalog. Scripts and opted-in package executables remain separate tool families:
|
|
171
171
|
|
|
172
172
|
```json
|
|
173
173
|
{
|
|
174
174
|
"mcpServers": {
|
|
175
|
-
"
|
|
175
|
+
"toolfuncs": {
|
|
176
176
|
"command": "/absolute/path/to/toolfuncs",
|
|
177
|
-
"args": ["mcp"
|
|
177
|
+
"args": ["mcp"]
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
```
|
|
182
182
|
|
|
183
|
-
`toolfuncs mcp
|
|
183
|
+
`toolfuncs mcp` captures PATH and starts one persistent child per family, using each source's isolated dependency environment. Children inherit the launch directory and environment. Startup is bounded to 120 seconds, including dependency preparation, with at most eight families starting concurrently. Configure the client's startup timeout to at least 180 seconds for cold installations. Failed families appear in `catalog` with an error; healthy families remain callable. `toolfuncs doctor` loads all families and exits nonzero if any are unavailable. It does not invoke their operations. Use `toolfuncs doctor SOURCE` for the full source documentation and help checks.
|
|
184
|
+
|
|
185
|
+
The server's operation catalog remains fixed until restart. A crashed child becomes unavailable and is not restarted automatically. Interrupted operations are never replayed, since their side effects may already have occurred. Ordinary function exceptions remain tool errors and leave the family available. Shutdown closes the children. Installing tools, changing PATH, or updating source/dependencies requires restarting the server.
|
|
186
|
+
|
|
187
|
+
`toolfuncs mcp SOURCE` remains available to serve one source or opted-in package command. Use `--prepared` only when the selected interpreter already contains every selected source's dependencies. Toolfuncs does not edit client configuration.
|
|
188
|
+
|
|
189
|
+
### Progressive discovery
|
|
190
|
+
|
|
191
|
+
The hook advertises families. Inspect one group at a time, then one selected operation:
|
|
192
|
+
|
|
193
|
+
```console
|
|
194
|
+
toolfuncs catalog
|
|
195
|
+
toolfuncs commands weather-report
|
|
196
|
+
toolfuncs commands weather-report --group '["records"]'
|
|
197
|
+
toolfuncs inspect-operation weather-report '["records", "show"]'
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The generic MCP server exposes these same three discovery functions as `catalog`, `commands`, and `inspect_operation`. Group views contain only immediate child names and short descriptions. Selected operation views contain the original input/output schemas, documentation, canonical CLI argv, MCP name, and a Python attribute when it is verifiably exported by the source module. Do not reconstruct Python access from an MCP name: registered aliases and nested CLI groups need not match Python attributes.
|
|
201
|
+
|
|
202
|
+
Python uses the same bound functions:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
from toolfuncs.sdk import ToolCatalog
|
|
206
|
+
|
|
207
|
+
async with ToolCatalog() as catalog:
|
|
208
|
+
families = catalog.catalog() # static discovery, no child startup
|
|
209
|
+
await catalog.start(["weather-report"])
|
|
210
|
+
children = catalog.commands("weather-report")
|
|
211
|
+
detail = catalog.inspect_operation("weather-report", ["records", "show"])
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
In Codex Code Mode, use toolfuncs discovery to select an operation, resolve its runtime name from `ALL_TOOLS`, and invoke `tools[name](arguments)`. Retain deferred MCP exposure; native tool search is unnecessary. In Claude Code, keep the server's tools deferred (`"alwaysLoad": false` on supported versions), load the selected tool with `ToolSearch` query `select:mcp__toolfuncs__NAME`, then call it normally. Claude still exposes operation names initially. These context policies belong to the client; generic MCP clients may expose all schemas. For the agent hook's client-specific guidance, set `TOOLFUNCS_MCP_SERVER=toolfuncs` in the host environment when that server is registered.
|
|
184
215
|
|
|
185
216
|
For programmatic use, `server = app.to_mcp()` returns a FastMCP server. Sync functions use FastMCP's thread dispatch and async functions are awaited. FastMCP owns request handling and cancellation. MCP calls do not share live state with an IPython kernel. The function implementation is shared.
|
|
186
217
|
|
|
187
|
-
A nested CLI command such as `records show` becomes `records__show`;
|
|
218
|
+
A nested CLI command such as `records show` becomes `records__show`; the PATH server prefixes the family, for example `weather_report__records__show`. Hyphens become underscores. Aliases remain CLI aliases. Collisions and names exceeding 64 characters fail explicitly. Every successful MCP result has structured content `{"result": value}`, including scalar and null results, plus JSON text content. Exceptions become MCP tool errors.
|
|
188
219
|
|
|
189
220
|
Use stderr for logs and diagnostics. Stdout belongs to the CLI result or MCP protocol; an operation that prints to stdout can corrupt that stream. Durable background tasks, push notifications, remote hosting, resources and prompts are outside the 1.0 interface.
|
|
190
221
|
|
|
@@ -216,7 +247,7 @@ Declare `toolfuncs>=1,<2` in PEP 723 dependencies to make the required major ver
|
|
|
216
247
|
|
|
217
248
|
The description, either `[tool.toolfuncs].description` or a package module's first docstring line, is the line agents read in `toolfuncs list` and session-start catalogs when they choose a tool. Write one sentence that says what the tool does for its caller. Keep implementation details, compatibility notes and contract caveats out of it; put them in later docstring text or in command docstrings.
|
|
218
249
|
|
|
219
|
-
Descriptions are static by default. A
|
|
250
|
+
Descriptions are static by default. A script whose available domain surface changes at runtime may opt into a last-known catalog description with `dynamic = true`:
|
|
220
251
|
|
|
221
252
|
```toml
|
|
222
253
|
[tool.toolfuncs]
|
|
@@ -244,6 +275,15 @@ Only functions registered on `app` become CLI commands. Imported functions and `
|
|
|
244
275
|
|
|
245
276
|
No `if __name__ == "__main__":` block is required. The operating system passes the source path to the shebang interpreter, and toolfuncs imports the source under its declared Python name before invoking its module-level app. Consequently, `weather-report ...` is the supported CLI while `python weather-report ...` merely defines the module and exits, or fails if its dependencies are not already installed.
|
|
246
277
|
|
|
278
|
+
Packaged tools opt in with an additional matching entry point:
|
|
279
|
+
|
|
280
|
+
```toml
|
|
281
|
+
[project.entry-points."toolfuncs.dynamic_description"]
|
|
282
|
+
my-tool = "my_package.tools:app"
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Keep the matching `project.scripts` and `project.entry-points.toolfuncs` declarations. Publish descriptions through the same `toolsdk.publish_description` API. Discovery reads the current user's published line on each call and falls back to the module docstring when no valid line exists; it does not import the tool or access its service.
|
|
286
|
+
|
|
247
287
|
## Package-backed implementations
|
|
248
288
|
|
|
249
289
|
A toolfunc remains one script even when most of its implementation lives in a package:
|
|
@@ -314,9 +354,12 @@ Setup does not discover, copy, register, link, synchronize, or remove tools. The
|
|
|
314
354
|
|
|
315
355
|
```console
|
|
316
356
|
toolfuncs SOURCE [ARGS...]
|
|
317
|
-
toolfuncs doctor SOURCE
|
|
318
|
-
toolfuncs mcp [--prepared] SOURCE
|
|
357
|
+
toolfuncs doctor [--prepared] [SOURCE]
|
|
358
|
+
toolfuncs mcp [--prepared] [SOURCE]
|
|
319
359
|
toolfuncs list
|
|
360
|
+
toolfuncs catalog
|
|
361
|
+
toolfuncs commands FAMILY [--group JSON_PATH]
|
|
362
|
+
toolfuncs inspect-operation FAMILY JSON_PATH
|
|
320
363
|
toolfuncs setup [--bin-dir DIRECTORY]
|
|
321
364
|
```
|
|
322
365
|
|
|
@@ -8,6 +8,8 @@ Toolfuncs projects one explicitly registered set of typed Python functions into
|
|
|
8
8
|
|
|
9
9
|
An installed distribution opts a console script into discovery by declaring the same command name and `module:app` value in its `console_scripts` and `toolfuncs` entry-point groups. Authors normally express these as `[project.scripts]` and `[project.entry-points.toolfuncs]` in `pyproject.toml`. The module contains the same module-level `app` and decorated typed functions used by script tools. There is no hand-maintained launcher: normal installers generate it.
|
|
10
10
|
|
|
11
|
+
A packaged tool may also declare that same name and value in the `toolfuncs.dynamic_description` entry-point group (`[project.entry-points."toolfuncs.dynamic_description"]`). Discovery then uses its valid published one-line description, with the module docstring as fallback, just as a script with `dynamic = true` does. The dynamic entry must match both ordinary declarations; it does not register an additional command. Discovery remains local and never imports the module. Published state is scoped to the current user's state directory and CLI identity, not to a package generation.
|
|
12
|
+
|
|
11
13
|
The public executable's kebab-case name determines its snake-case facade name, with the same reserved-name checks and PATH precedence as scripts. uv-launch's versioned installation records locate the actual executable and its metadata roots, capturing a single retained generation. The actual executable must have one distribution owner across all RECORD files, a matching strong checksum, and paired console-script and toolfuncs entry points. Discovery trusts the installer to implement those entry points. It does not parse generated Python or shell bodies or authenticate package metadata. Discovery statically reads the installed module's first docstring line and requires inspectable Python source.
|
|
12
14
|
|
|
13
15
|
Metadata in the current interpreter is also supported, including additive native dependency preparation. Unmanaged foreign environments are not inferred from launcher text; install those commands through uv-launch. Discovery never imports target packages, starts a subprocess, installs dependencies, or queries the network. `toolfuncs.sdk.discover(path=None)` returns the same `Catalog` as CLI listing, with `tools` and `invalid_tools`.
|
|
@@ -53,7 +55,11 @@ Cyclopts tokenizes and binds CLI arguments. Its central converter uses Pydantic'
|
|
|
53
55
|
|
|
54
56
|
## MCP interface
|
|
55
57
|
|
|
56
|
-
`toolfuncs mcp SOURCE` prepares dependencies and serves one source or opted-in package executable over stdio.
|
|
58
|
+
`toolfuncs mcp SOURCE` prepares dependencies and serves one source or opted-in package executable over stdio. Omitting SOURCE serves the effective PATH catalog with one isolated persistent child per family. `--prepared` uses the current interpreter without dependency preparation. `app.to_mcp(name=...)` produces a FastMCP server for programmatic use.
|
|
59
|
+
|
|
60
|
+
The PATH server snapshots discovery and operation schemas at startup, retains healthy families when another fails, and records failed families in its `catalog` discovery result. Each native tool name is prefixed by its family Python name and `__`; the complete name must be unique and at most 64 characters. There is no automatic refresh, reconnect or failed-call replay. Child processes inherit the server's launch directory and environment. They are closed when the server exits.
|
|
61
|
+
|
|
62
|
+
`catalog`, `commands`, and `inspect_operation` use the same implementations in MCP, the management CLI and `toolfuncs.sdk.ToolCatalog`. Each group listing exposes immediate children only. One selected operation exposes its authoritative contract. The Python catalog is an async context manager; `await catalog.start()` loads all families, or a supplied sequence selects families. Static `sdk.discover()` and `toolfuncs list` remain import-free. Native domain calls still use the original functions through `toolfuncs.NAME`; discovery does not turn Python domain calls into RPC.
|
|
57
63
|
|
|
58
64
|
Canonical command paths become tool names by replacing hyphens with underscores and joining path segments with `__`. A root default operation uses its function name. Names must contain 1-64 ASCII letters, digits, underscores or hyphens. CLI aliases are deduplicated; name collisions fail.
|
|
59
65
|
|
|
@@ -116,7 +122,7 @@ Successful default CLI operations render one Python value through Rich on an int
|
|
|
116
122
|
|
|
117
123
|
Setup does not discover, install, copy, link, register, synchronize, or remove tool sources. Adding and removing tools are ordinary filesystem operations in directories already on `PATH`.
|
|
118
124
|
|
|
119
|
-
The public management commands
|
|
125
|
+
The public management commands include `toolfuncs mcp [--prepared] [SOURCE]`, `toolfuncs doctor [--prepared] [SOURCE]`, `toolfuncs list`, `toolfuncs catalog`, `toolfuncs commands FAMILY [--group JSON_PATH]`, `toolfuncs inspect-operation FAMILY JSON_PATH`, and `toolfuncs setup [--bin-dir DIRECTORY]`. Doctor without SOURCE checks aggregate loading and name validity; doctor with SOURCE also checks its authoring documentation and generated help. The explicit-source execution form is `toolfuncs SOURCE [ARGS...]`; it is not a universal name dispatcher. Tools on `PATH` are invoked directly by their own CLI names. There is no generic operation-call command.
|
|
120
126
|
|
|
121
127
|
## Compatibility
|
|
122
128
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import asyncio
|
|
5
6
|
import json
|
|
6
7
|
import sys
|
|
7
8
|
from collections.abc import Iterator, Sequence
|
|
@@ -31,9 +32,12 @@ Usage:
|
|
|
31
32
|
toolfuncs --version
|
|
32
33
|
toolfuncs SOURCE [ARGS...]
|
|
33
34
|
toolfuncs run-prepared SOURCE [ARGS...]
|
|
34
|
-
toolfuncs doctor [--prepared] SOURCE
|
|
35
|
-
toolfuncs mcp [--prepared] SOURCE
|
|
35
|
+
toolfuncs doctor [--prepared] [SOURCE]
|
|
36
|
+
toolfuncs mcp [--prepared] [SOURCE]
|
|
36
37
|
toolfuncs list
|
|
38
|
+
toolfuncs catalog
|
|
39
|
+
toolfuncs commands FAMILY [--group JSON_PATH]
|
|
40
|
+
toolfuncs inspect-operation FAMILY JSON_PATH
|
|
37
41
|
toolfuncs setup [--bin-dir DIRECTORY]
|
|
38
42
|
|
|
39
43
|
A toolfunc is an executable Python script whose first line is exactly:
|
|
@@ -53,8 +57,12 @@ run-prepared and doctor --prepared use the current interpreter without resolving
|
|
|
53
57
|
or installing dependencies. Installers must prepare the script's declared runtime
|
|
54
58
|
first. They still validate the source and its Python-version requirement.
|
|
55
59
|
|
|
56
|
-
mcp serves
|
|
57
|
-
|
|
60
|
+
mcp serves native tools from PATH over stdio, or one selected SOURCE. Register it
|
|
61
|
+
once with your client. Each family runs in its own persistent process. Restart
|
|
62
|
+
the server after installing or changing tools; there is no automatic reload.
|
|
63
|
+
Discovery proceeds from catalog to commands to inspect-operation. These commands
|
|
64
|
+
share their implementation with the Python ToolCatalog and native MCP discovery.
|
|
65
|
+
doctor without SOURCE checks all discovered families and reports partial failures.
|
|
58
66
|
"""
|
|
59
67
|
|
|
60
68
|
|
|
@@ -108,11 +116,27 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
108
116
|
_print_json(_catalog_payload(catalog))
|
|
109
117
|
_warn_invalid_tools(catalog.invalid_tools)
|
|
110
118
|
return 0
|
|
119
|
+
if command in {"catalog", "commands", "inspect-operation"}:
|
|
120
|
+
from ._catalog import run_discovery
|
|
121
|
+
|
|
122
|
+
return asyncio.run(run_discovery(command, tokens))
|
|
111
123
|
if command in {"doctor", "__doctor-source"}:
|
|
112
124
|
prepared = command == "__doctor-source"
|
|
113
125
|
if command == "doctor" and tokens[:1] == ["--prepared"]:
|
|
114
126
|
prepared = True
|
|
115
127
|
tokens.pop(0)
|
|
128
|
+
if not tokens and command == "doctor":
|
|
129
|
+
from ._catalog import doctor_catalog
|
|
130
|
+
|
|
131
|
+
families = asyncio.run(doctor_catalog(prepared=prepared))
|
|
132
|
+
healthy = all(family.status == "ready" for family in families)
|
|
133
|
+
_print_json(
|
|
134
|
+
{
|
|
135
|
+
"status": "success" if healthy else "error",
|
|
136
|
+
"families": [family.model_dump(mode="json") for family in families],
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
return 0 if healthy else 1
|
|
116
140
|
if len(tokens) != 1:
|
|
117
141
|
raise SystemExit("usage: toolfuncs doctor SOURCE")
|
|
118
142
|
if not prepared and Path(tokens[0]).is_file():
|
|
@@ -136,6 +160,11 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
136
160
|
if tokens[:1] == ["--prepared"]:
|
|
137
161
|
prepared = True
|
|
138
162
|
tokens.pop(0)
|
|
163
|
+
if not tokens and command == "mcp":
|
|
164
|
+
from ._catalog import create_path_server
|
|
165
|
+
|
|
166
|
+
create_path_server(prepared=prepared).run(transport="stdio", show_banner=False)
|
|
167
|
+
return 0
|
|
139
168
|
if len(tokens) != 1:
|
|
140
169
|
raise SystemExit("usage: toolfuncs mcp [--prepared] SOURCE")
|
|
141
170
|
source = Path(tokens[0])
|
|
@@ -144,7 +173,10 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
144
173
|
raise AssertionError("exec returned")
|
|
145
174
|
# Import diagnostics must not be mistaken for JSON-RPC protocol frames.
|
|
146
175
|
with redirect_stdout(sys.stderr), _tool_program_name(source):
|
|
147
|
-
|
|
176
|
+
from ._mcp import create_server
|
|
177
|
+
|
|
178
|
+
module = _load_prepared_source(source)
|
|
179
|
+
server = create_server(_application(module), name=source.name, source_module=module)
|
|
148
180
|
server.run(transport="stdio", show_banner=False)
|
|
149
181
|
return 0
|
|
150
182
|
if command == "setup":
|