diff --git a/docs/tools/index.md b/docs/tools/index.md index af7ac5df..45dd54c3 100644 --- a/docs/tools/index.md +++ b/docs/tools/index.md @@ -8,13 +8,23 @@ differ in exactly one way: only the CLI accepts filesystem paths. ## Setup -Install one of the two; the second includes the first: +Install `osw` as a uv tool. The `mcp` extra adds the server to the same install: ```bash uv tool install osw # the `osw` command uv tool install "osw[mcp]" # the same, plus the `osw-mcp` server ``` +uv writes both console scripts to its tool bin directory, `~/.local/bin` by +default. Run `uv tool update-shell` once if that directory is not on your PATH +yet, and `uv tool upgrade osw` to update them later. Quote the argument: +`osw[mcp]` contains brackets, which zsh reads as a glob pattern and then refuses +to run. + +Without the `mcp` extra, uv still writes `osw-mcp` to the bin directory, but +that copy raises `ModuleNotFoundError` at startup. Install `osw[mcp]` before you +register the server with a client. +
Other ways to install @@ -24,13 +34,14 @@ uv add "osw[mcp]" # as a dependency of the current uv project uvx --from "osw[mcp]" osw-mcp # run the server without installing it ``` -`uvx` is what the registration examples further down use, so the server needs -no install of its own. +`uvx` needs no install of its own. +[Which command to register](mcp.md#which-command-to-register-for-the-mcp-server) +explains when to prefer it over the installed command.
-`osw[mcp]` is also part of `osw[all]`. The other extras are listed in the -[Get Started guide](../get-started.md#optional-extras). +`osw[mcp]` includes `osw[wikitext]`, and is itself part of `osw[all]`. The other +extras are listed in the [Get Started guide](../get-started.md#optional-extras). ## In this section diff --git a/docs/tools/mcp.md b/docs/tools/mcp.md index c022c234..13fc4bf1 100644 --- a/docs/tools/mcp.md +++ b/docs/tools/mcp.md @@ -8,6 +8,11 @@ introspect category schemas, read entities and every page slot, create/update and delete entities, and read and write file pages as text. The transport is stdio; SSE and HTTP are not supported. +A registered server runs the `osw-mcp` console script, and there are two ways to +reach it: let `uvx` fetch it for each server, or install `osw[mcp]` once and +register the command directly. See +[Which command to register for the MCP server](#which-command-to-register-for-the-mcp-server). + **No filesystem access:** no MCP tool takes or returns a local path. File content moves inline as text (`get_file_info`, `read_file_text`, `write_file_text`), and everything path-based lives in the CLI instead @@ -42,6 +47,36 @@ Notes: for every project, or `-s project` to write a shared `.mcp.json`. - List what is registered with `claude mcp list`. +### Which command to register for the MCP server + +- **To pin the `osw` version per server, register the `uvx` command**, as in the + example above and in every other example on this page. The package + specification is part of the entry, so one server can name a version or a + checkout that the others do not use. `uvx` needs no install of its own. The + cost is that changing the version means editing every entry that should + change. Note that `uvx` reuses an installed `osw` tool whenever that + installation satisfies the specification, so an unpinned `osw[mcp]` runs the + installed version if there is one. Write an explicit version, for example + `osw[mcp]==2.6.2`, when a server must be independent of it. + +- **To let all servers share one `osw` version, register the installed + `osw-mcp` command.** Install `osw[mcp]` as a uv tool ([Setup](index.md#setup)), + then register that command in place of the whole `uvx --from ...` invocation: + + ```bash + claude mcp add osw-dev \ + -e OSW_DOMAIN=wiki-dev.open-semantic-lab.org \ + -e OSW_CRED_FILEPATH=/abs/path/to/accounts.pwd.yaml \ + -- osw-mcp + ``` + + In JSON that entry is `"command": "osw-mcp"` with an empty `args` array. + `uv tool upgrade osw` then changes the version for every server at once. The + cost is that `osw-mcp` has to be on the PATH of the client, and that no + server can keep an older version. + +Everything else about an entry, `env` included, is the same either way. + ## Registering a server A server entry can carry its settings in two ways: @@ -165,21 +200,50 @@ Why the MCP server is shaped the way it is, and how that differs from the CLI: ## Notes for developers -To try an unreleased branch against a real client, point `uvx` at the checkout -instead of at PyPI. Everything else about the registration stays the same: +To try an unreleased branch against a real client, run it from the checkout. The +same two options apply as above. + +Point `uvx` at the checkout instead of at PyPI. Everything else about the +registration stays the same: ```bash uvx --reinstall --from "/abs/path/to/osw-python[mcp]" osw-mcp ``` -`--reinstall` is what picks up your latest edits, since `uvx` caches the wheel -it builds. In a JSON `args` array, a Windows path needs forward slashes or +`--reinstall` is what makes `uvx` use your latest edits, since it caches the +wheel it builds. In a JSON `args` array, a Windows path needs forward slashes or doubled backslashes. -Prefer that over an editable install for the server. `create_or_update_entity` -and `export_entity_jsonld` call `fetch_schema`, which regenerates -`src/osw/model/entity.py` inside the installed package: `uvx` builds a -non-editable wheel, so the write lands in the uv cache, while under -`pip install -e` or `uv sync` it lands in your working tree. The read tools +Or install the checkout as an editable uv tool. The server then registers as +plain `osw-mcp`, no client config contains the checkout path, and edits take +effect at the next server start without a reinstall: + +```bash +cd /path/to/osw-python +uv tool install --reinstall --editable ".[mcp]" +``` + +`osw` and `osw-mcp` then import from `src/` in that checkout. Four consequences: + +- Both commands follow the checked-out branch, so switching branches changes + what a registered server runs. +- Run the install command again after a change to `pyproject.toml`. uv + re-resolves from the checkout and installs the difference. +- Returning to a released version needs `--reinstall`. Without it uv finds the + requirement satisfied, keeps the editable environment and rewrites only the + tool receipt, so receipt and environment disagree. +- On Windows, an install that has to replace the tool environment fails with + `os error 5` while an `osw-mcp` server from it is running, because the running + process locks the tool's `Scripts` directory. Stop the MCP clients first. + +One difference decides between `uvx` and the editable install. +`create_or_update_entity` and +`export_entity_jsonld` call `fetch_schema`, which regenerates +`src/osw/model/entity.py` inside the installed package. `uvx` builds a +non-editable wheel, so the regenerated file is written into the uv cache. Under +an editable install, as under `pip install -e` or `uv sync`, it is written into +your working tree, where git tracks it. Note that `export_entity_jsonld` is +declared read-only and still triggers this. The remaining read tools (`get_entity`, `get_slot`, `get_category_schema`, ...) read raw page slots and -never trigger it. +never trigger it. So choose `uvx` whenever a client may call either of those two +operations, and the editable install otherwise. diff --git a/pyproject.toml b/pyproject.toml index 094d9c63..83295121 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,6 +87,9 @@ mcp = [ # official MCP Python SDK; provides MCPServer from mcp.server. # requires 2.x: 1.x has no MCPServer, and 2.0 removed the vendored FastMCP. "mcp>=2", + # without it osw.wiki_tools warns at import time, which is noise on the + # server's stderr and appears in every client's log. + "osw[wikitext]", ] workflow = [ "prefect>=2.20.25,<3.0", diff --git a/uv.lock b/uv.lock index 0ef50ed4..2cbdbfe3 100644 --- a/uv.lock +++ b/uv.lock @@ -2121,6 +2121,7 @@ db = [ ] mcp = [ { name = "mcp" }, + { name = "mwparserfromhell" }, ] s3 = [ { name = "boto3" }, @@ -2191,6 +2192,7 @@ requires-dist = [ { name = "opensemantic-core", specifier = ">=0.57.4" }, { name = "osw", extras = ["dataimport"], marker = "extra == 'tutorial'" }, { name = "osw", extras = ["dataimport", "db", "ui", "s3", "wikitext", "mcp"], marker = "extra == 'all'" }, + { name = "osw", extras = ["wikitext"], marker = "extra == 'mcp'" }, { name = "prefect", marker = "extra == 'workflow'", specifier = ">=2.20.25,<3.0" }, { name = "psycopg2", marker = "extra == 'db'" }, { name = "pybars3-wheel" },