diff --git a/CHANGELOG.md b/CHANGELOG.md index 789e0d4..6da223c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,27 @@ All notable changes to DirXMLDevWeb are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.73.0] - 2026-10-22 + +Needs DirXMLDev 0.50.0. + +### Added + +- **An agent moves between branches on its own:** MCP tools `open_branch` (work on another branch; + a new name branches from the base branch, nothing reaches the repository until a commit and push) + and `list_branches` (the projects, branches and base branch, and which you have open). Before, + an agent stayed on the branch its token's session first opened. The page and the agent each keep + their own branch. + +### Changed + +- **Docs and help for agents.** docs/agents.md now starts with a four-step quick start for Claude + Code, lists every MCP tool with its required and optional arguments and accepted values, the + resources and prompts, Cursor's and a script's connection, which branch an agent works on, and + what to do when it is refused. The agent tokens dialog says what the agent does next and links + there; `setup`'s summary names `/mcp__dirxmldev__setup`. The README, quickstart, tutorial, + features, upgrading, team-server install and security docs cover agents and their tokens. + ## [0.72.0] - 2026-10-22 Needs DirXMLDev 0.50.0. diff --git a/README.md b/README.md index 6fdf742..cc7aa94 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ pop-out window, and recording to a file. ## What it is, and is not -It is a third client of the DirXMLDev core, next to the `idm` CLI and the MCP server. The page +It is a third client of the DirXMLDev core, next to the `idm` CLI and DirXMLDev's MCP server. The page calls the same typed operations they call, so anything the core can do, it can do, with the same validation and the same refusals. It is not a port of Designer and contains no OpenText code. The tree on disk (in git) is the only state: edit here, with the CLI or with an agent, and @@ -103,6 +103,12 @@ and pull requests, sign-in and audit. Every write goes through one review (a dry of the tree, file by file) and is applied as one transaction, or not at all. The whole list, view by view: [docs/features.md](docs/features.md). +**Your agents too.** The server is an MCP server (`/mcp`). Make a token on the page, connect Claude +Code, Cursor or a script, and run the `setup` prompt: the agent gets this version's rules, skills +and docs from the server and acts as you, under your roles, with the same previews, plan and +typed confirmation before a vault write as the page. The `idm`, `sim` and `apps` CLIs are in the +image too. [docs/agents.md](docs/agents.md). + ## Three ways to run it | | **Local** — one person, one container | **Server** — a team | **Workstation** — from a checkout | @@ -129,6 +135,7 @@ for developing it or for a quick look at a tree. Upgrading an install is running | [docs/tutorial.md](docs/tutorial.md) | a new install to a first deploy: read a vault, change a policy, commit, deploy | | [docs/git-workflow.md](docs/git-workflow.md) | working with git: branches, dev/test/production of one driver set, promotion, merging into main, remotes | | [docs/features.md](docs/features.md) | every view, by area | +| [docs/agents.md](docs/agents.md) | agents: tokens, connecting Claude Code or Cursor, the setup prompt, every MCP tool and its arguments, the CLIs, the agent kit | | [docs/data-model.md](docs/data-model.md) | projects, workspaces, trees, environments and vaults: the entity relationship diagram | | [docs/multi-user.md](docs/multi-user.md) | design note: per-user connections, data and traces on a hosted server | | [docs/architecture-view.md](docs/architecture-view.md) | design note: the Architecture (Designer Modeler) and Developer views | diff --git a/bin/smoke-hosted.sh b/bin/smoke-hosted.sh index c38bb67..ef8e6d4 100755 --- a/bin/smoke-hosted.sh +++ b/bin/smoke-hosted.sh @@ -287,6 +287,16 @@ check("MCP: another host is refused", s == 403) s, _, _ = anon.call("POST", "/mcp", {"jsonrpc": "2.0", "id": 7, "method": "tools/list"}, csrf=False) check("MCP: without a token it says to sign in", s == 401) check("MCP: GET has no stream (405)", ci.call("GET", "/mcp", headers=mine)[0] == 405) +page_branch = alice.call("GET", "/api/session")[1]["branch"] +s, ob, _ = mcp({"jsonrpc": "2.0", "id": 13, "method": "tools/call", "params": {"name": "open_branch", "arguments": {"branch": "feature/agent-moved"}}}) +s, r, _ = mcp({"jsonrpc": "2.0", "id": 14, "method": "tools/call", "params": {"name": "session", "arguments": {}}}) +check("MCP: open_branch moves the agent to another branch", not ob["result"]["isError"] and json.loads(r["result"]["content"][0]["text"])["branch"] == "feature/agent-moved") +check("MCP: ... and the page stays on its own", alice.call("GET", "/api/session")[1]["branch"] == page_branch) +s, lb, _ = mcp({"jsonrpc": "2.0", "id": 15, "method": "tools/call", "params": {"name": "list_branches", "arguments": {}}}) +check("MCP: list_branches names the branch the agent opened as one of yours", "feature/agent-moved" in lb["result"]["content"][0]["text"]) +s, bad, _ = mcp({"jsonrpc": "2.0", "id": 16, "method": "tools/call", "params": {"name": "open_branch", "arguments": {"branch": "../escape"}}}) +check("MCP: a bad branch name is refused", bad["result"]["isError"]) +mcp({"jsonrpc": "2.0", "id": 17, "method": "tools/call", "params": {"name": "open_branch", "arguments": {"branch": page_branch}}}) s, rl, _ = mcp({"jsonrpc": "2.0", "id": 8, "method": "resources/list"}) uris = [x["uri"] for x in rl["result"]["resources"]] check("MCP: the agent kit is listed as resources", s == 200 and "dirxmldev://kit/AGENTS.md" in uris) diff --git a/docs/agents.md b/docs/agents.md index 964cb15..27b9cc6 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -5,17 +5,57 @@ the same rules. Installing DirXMLDevWeb gives an agent everything installing Dir the simulator, the Identity Applications helper, the skills and docs of this version, and an MCP server. -## 1. A token +## Quick start -On the page, your name at the top right → **agent tokens…** → *Make the token*. Give it a label -(`claude-laptop`) and a lifetime (90 days by default). The token is shown once, with the command -that connects Claude Code. +For Claude Code, on the machine where you run it (macOS, Windows or Linux; the commands are the +same in PowerShell): + +1. **Make a token.** On the page, your name at the top right → **agent tokens…** → give it a label + (`claude-laptop`) → *Make the token*. Copy the command it shows; the token is shown only once. +2. **Connect Claude Code** with that command. Add `--scope user` to have the server in every + project, not only the folder you run it in: + + ```bash + claude mcp add --scope user --transport http dirxmldev http://localhost:8080/mcp --header "Authorization: Bearer " + ``` + + On a team server, use its address (`https://idm.example.com/mcp`). `claude mcp list` should + show `dirxmldev` as connected. +3. **Set it up.** In Claude Code: + + ``` + /mcp__dirxmldev__setup + ``` + + It reads the rules, installs the `dirxml-dev` and `dirxml-policy-testing` skills into + `~/.claude/skills` (asking before it overwrites one), reads the agent docs, then tells you who + it acts as, the branch, the drivers and what `validate` says. It changes nothing in the tree or + a vault. +4. **Ask in your own words**: "why does the AD driver's placement policy put contractors in the + wrong OU?", "add a GCV for the HR feed's URL and use it in the driver", "compare the tree with + QA". The agent previews every edit before applying it, validates after, and shows you the plan + before a deploy. A deploy, rollback or driver start or stop needs you to type the environment's + name in the chat. + +Another agent (Cursor, Codex, a script) connects to the same address with the same header; see +[Other clients](#other-clients). If it has no MCP prompts, the dialog's *Set up your agent* has the +setup text to paste into the chat. It holds no token. + +## 1. Tokens + +On the page, your name at the top right → **agent tokens…** → *Make the token*: + +- **Label:** up to 60 characters, different from your other tokens' (`claude-laptop`, + `cursor-work`). It names the token in the audit log. +- **Expires after:** 7, 30, 90 (the default), 180 or 365 days, up to what the server allows. + +The token is shown once, with the command that connects Claude Code. The dialog lists your tokens +with when each was made, when it expires and when it was last used; **revoke** stops one at once. An agent with your token **acts as you**: your roles (a reader's agent reads, an editor's edits, a -deployer's may deploy), your connections and vault credentials, your workspace of the branch you have -open. Every call it makes is in the audit log as yours, marked `via` the token's label; its commits -are yours. Revoke a token from the same dialog; it stops working at once. An agent cannot make or -revoke tokens. +deployer's may deploy), your connections and vault credentials, your workspaces. Every call it makes +is in the audit log as yours, marked `via` the token's label; its commits are yours. An agent cannot +make or revoke tokens. Only a SHA-256 of each token is kept (`users//agent-tokens.json` in the server's data folder, owner-only), with who you were when you last used the page, so a change of your roles reaches your @@ -23,63 +63,130 @@ tokens. On a team server, an administrator can turn personal tokens off (`agent. shorten their longest life (`agent.tokens.maxDays`, default 365) in `hosted.properties`. Agents that act for no one (CI) keep their own `agents=` entries in `hosted.properties` -([hosted-mode.md](hosted-mode.md)). +([hosted-mode.md](hosted-mode.md)). The local install's `idmweb.env` holds one of those too +(`IDMWEB_AGENT_TOKEN`). For your own agent a personal token is better: the audit log then names you +and the token, and you can revoke it from the page. ## 2. MCP -The server is an MCP server at `/mcp` (Streamable HTTP, stateless). With Claude Code: - -```bash -claude mcp add --transport http dirxmldev http://localhost:8080/mcp --header "Authorization: Bearer " +The server is an MCP server at `/mcp`: Streamable HTTP, stateless, JSON answers (no server-sent +stream; `GET` answers 405). Every tool is a call to the server's own API, made with your token: the +same sign-in, origin check, roles, audit, per-environment write lock and production gate as the +page. Nothing in the MCP layer decides what an agent may do; the API does. + +### What the agent works on + +The agent works in **your workspaces**: the same files the page shows you. Its first call opens the +project and branch you last had open on the page; `session` says which. Ask it to work on another +("work on feature/hr-url") and it calls `open_branch`: a branch you have open already is the same +folder the page uses, one you haven't is cloned for you, and a new name branches from the base +branch (nothing reaches the repository until it commits and pushes). The page and the agent each +keep their own branch: moving one doesn't move the other. When both are on the same branch, the +agent's applied edits show on the page at once, and yours show to it. + +### Tools + +Required arguments are in **bold**. Every tool answers with the API's JSON as text; a refusal comes +back as an error result with the server's reason. Answers longer than 200,000 characters are cut, +with a note: narrow the request (one driver, one path). + +**Looking around** (any role): + +| Tool | Arguments | What it does | +|---|---|---| +| `session` | | who you act as, your roles, the project and branch open, the versions | +| `outline` | | the tree: drivers, policies, resources, objects, and its revision | +| `read_artifact` | **`path`**: the tree path, as `outline` lists it (`drivers/AD/subscriber/sub-ctp`) | an artifact's content and details | +| `read_file` | **`file`**: a path in the tree (`drivers/AD/driver.xml`, `overrides/QA.properties`) | a file of the tree | +| `validate` | | the engine's own checks: compile, linkage, GCVs, mapping tables, filter, schema map | +| `fishbone` | **`driver`**: the driver's name | a driver's policy flow, policy set by policy set | +| `git_status` | | the branch, uncommitted files, unpushed commits, where it stands against the base branch | +| `git_diff` | `against`: `head` (default, the last commit) or `base` (the base branch) | what differs | +| `list_branches` | `project`: another project | the projects, the project's branches and base branch, and which you have open | + +**Editing** (editor role): + +| Tool | Arguments | What it does | +|---|---|---| +| `list_operations` | | every edit operation (`policy.add`, `gcv.set`, `driver.set`, `override.set`, …) and its arguments | +| `run_operations` | **`operations`**: a list of `{name, args, files}`, names and args as `list_operations` gives them; `files` maps an argument to file content, inline. `dryRun`: `true` (default) previews, `false` applies | one or more edits, applied together or not at all | +| `open_branch` | **`branch`**: letters, digits, `.`, `_`, `-` and `/`; `project`: another project | works on that branch from now on (a new name branches from the base branch) | +| `commit` | **`message`**; `push`: `true` to push too | commits the workspace's changes | + +`run_operations` fetches the tree's revision itself, so an edit planned against an older tree is +refused instead of overwriting someone else's change. Example arguments: + +```json +{"operations": [{"name": "gcv.set", "args": {"name": "hr.url", "value": "https://hr.example.com/api"}}], "dryRun": true} ``` -Use your server's address for a team server (`https://idm.example.com/mcp`). Keep the token in an -environment variable rather than in a file you commit. +**The vaults, reading** (any role; `env` is an environment's name, as `environments` lists it): -Every tool is a call to the server's own API, made with your token: the same sign-in, origin check, -roles, audit, per-environment write lock and production gate as the page. +| Tool | Arguments | What it does | +|---|---|---| +| `environments` | | the project's environments, their tier (dev, test, prd), whether you are connected | +| `compare` | **`env`**; `drivers`: a list of the tree's driver names (all when omitted) | the tree against the vault: what a deploy would write | +| `compare_environments` | **`env`**, **`with`** | two vaults side by side: what only one holds, values that differ, likely renames, likely causes | +| `plan_deploy` | **`env`**; `drivers`; `restart`: `true` (default) restarts drivers a change needs restarted; `secrets`: `none` (default), `missing` or `all` | a deploy step by step, and what stands in its way | +| `driver_status` | **`env`**; `driver` | the drivers' state, or one driver's | +| `trace` | **`env`**, **`driver`** | a driver's recent trace | +| `snapshots` | **`env`** | the snapshots earlier deploys took (what a rollback puts back) | +| `simulate` | `env`: apply that environment's overrides | runs the project's simulator cases (`cases/`) against the tree | -| Tool | What it does | -|---|---| -| `session` | who you act as, your roles, the project and branch open, the versions | -| `outline` | the tree: drivers, policies, resources, objects, and its revision | -| `read_artifact`, `read_file` | an artifact's content by tree path; a file of the tree | -| `validate` | the engine's own checks over the tree | -| `fishbone` | a driver's policy flow | -| `git_status`, `git_diff` | the workspace against the last commit or the base branch | -| `list_operations` | every edit operation and its arguments | -| `run_operations` | edit the tree: preview (the default), then apply | -| `commit` | commit, and push | -| `environments` | the environments, their tier, whether you are connected | -| `compare` | the tree against an environment's vault | -| `compare_environments` | two environments' vaults side by side | -| `plan_deploy` | a deploy step by step, and what stands in its way | -| `driver_status`, `trace`, `snapshots` | drivers' state, a driver's trace, the snapshots | -| `simulate` | run the project's simulator cases | -| `deploy`, `rollback`, `driver_lifecycle` | vault writes: need the deployer role, and the environment's name typed by the person as `confirm` | - -The server tells the agent the rules when it connects: preview before applying, the plan before any -vault write, the person types the environment's name, production only from `main`, never a secret. - -It also serves this version's [agent kit](#4-the-agent-kit) as MCP **resources** -(`dirxmldev://kit/AGENTS.md`, `dirxmldev://kit/.claude/skills/…`, `dirxmldev://kit/docs/…`), and two -**prompts**: - -- `setup`: read the rules, install the skills (into `~/.claude/skills` unless you give `skills_dir`; - it asks before overwriting), read the agent docs, then orient. Nothing is changed in the tree or a - vault. -- `orient`: who you act as, the project and branch, what `validate` says, what is uncommitted. - -### Set up your agent - -After `claude mcp add`, in Claude Code: +**The vaults, writing** (deployer role). Each needs `confirm`: the environment's name, **typed by +you in the chat**. The agent must not fill it in itself. + +| Tool | Arguments | What it does | +|---|---|---| +| `deploy` | **`env`**, **`confirm`**; `drivers`, `restart`, `secrets` as for `plan_deploy` | deploys, after a snapshot | +| `rollback` | **`env`**, **`snapshot`** (as `snapshots` lists it), **`confirm`** | puts a snapshot back | +| `driver_lifecycle` | **`env`**, **`driver`**, **`action`**: `start`, `stop` or `restart`; **`confirm`** | starts, stops or restarts a driver | + +The server applies the page's rules to these: a production deploy comes from `main`, pushed and +current (unless the environment is set to deploy from any branch), validation errors block a +deploy, and one write per environment runs at a time. The agent reports a refusal; it doesn't work +around it. + +### Resources and prompts +The server also gives the agent what it needs to know, so nothing has to be copied by hand: + +- **Instructions**, when it connects: preview before applying, the plan before any vault write, you + type the environment's name, production only from `main`, never a secret. +- **Resources:** this version's [agent kit](#4-the-agent-kit), with the server's MCP address filled + in: `dirxmldev://kit/AGENTS.md` (the rules), `dirxmldev://kit/.claude/skills/…` (the two skills), + `dirxmldev://kit/docs/…` (the agent guide, this page, the environment and git workflow docs). +- **Prompts:** + + | Prompt | Arguments | What it does | + |---|---|---| + | `setup` | `skills_dir`: where to install the skills (default `~/.claude/skills`; `.claude/skills` for one project) | reads the rules, installs the skills, reads the agent docs, then orients. Changes nothing | + | `orient` | | who you act as and what you may do, the branch and its uncommitted work, the drivers, validation errors | + + In Claude Code they are `/mcp__dirxmldev__setup` and `/mcp__dirxmldev__orient`; an argument + follows the name (`/mcp__dirxmldev__setup .claude/skills`). + +Run `setup` again after an upgrade: the skills and docs are the server's version. + +### Other clients + +**Cursor:** `.cursor/mcp.json` in the project (or `~/.cursor/mcp.json` for every project): + +```json +{"mcpServers": {"dirxmldev": {"url": "http://localhost:8080/mcp", "headers": {"Authorization": "Bearer "}}}} ``` -/mcp__dirxmldev__setup + +**Any client, or a script:** POST JSON-RPC 2.0 to `/mcp` with the header. For example, from a +shell with the token in `IDMWEB_AGENT`: + +```bash +curl -s http://localhost:8080/mcp -H "Authorization: Bearer $IDMWEB_AGENT" -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"validate","arguments":{}}}' ``` -Agents that don't offer MCP prompts can be given the same text: *agent tokens…* → *Set up your -agent* → *Copy*, and paste it into the chat. It holds no token. +The answer is `{"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"…"}],"isError":false}}`, +with the API's JSON in `text`. `initialize`, `tools/list`, `resources/list`, `resources/read`, +`prompts/list` and `prompts/get` work the same way. Protocol versions 2025-06-18, 2025-03-26 and +2024-11-05 are accepted. ## 3. The CLIs @@ -102,6 +209,9 @@ docker exec idmweb apps --env lab tasks Paths are the container's: use paths relative to the workspace (`tree`, `cases`). On Windows, Git Bash rewrites arguments that look like Unix paths; prefix `MSYS_NO_PATHCONV=1` for an absolute one. +Wherever a skill or doc says `bin/idm`, `bin/sim` or `bin/apps`, an agent on that machine runs these +instead. An agent on another machine uses MCP. + ## 4. The agent kit The rules, skills and docs of this version, for any agent that reads `AGENTS.md` or skills: @@ -111,10 +221,26 @@ The rules, skills and docs of this version, for any agent that reads `AGENTS.md` - `.claude/skills/dirxml-policy-testing/`: testing and debugging policies with the simulator; - `docs/`: the agent guide, this page, and how work moves between branches and environments. -The local install's `setup` writes it to `agent/` in your `idmweb` folder, and refreshes it on every -upgrade; start Claude Code there, or copy `agent/.claude/skills` to `~/.claude/skills` to have the -skills everywhere. On any server, *agent tokens…* → *Download the agent kit* gives it as a zip with -that server's MCP address. +Three ways to get it, all the same files: + +- over MCP, as resources: the `setup` prompt installs the skills from them; +- the local install's `setup` writes it to `agent/` in your `idmweb` folder, and refreshes it on + every upgrade: start Claude Code there, or copy `agent/.claude/skills` to `~/.claude/skills`; +- on any server, *agent tokens…* → *Download the agent kit* gives it as a zip with that server's + MCP address. + +## When something goes wrong + +| What you see | Why, and what to do | +|---|---| +| `claude mcp list` shows `dirxmldev` failed, or every call answers 401 | the token is wrong, expired or revoked (the dialog shows an expired one). Make a new one, then `claude mcp remove dirxmldev` and add it again | +| `sign in first` | the request carried no token: check the `--header` part of the command | +| the agent is on the wrong branch | it starts on the one you last had open on the page: tell it which to work on, and it calls `open_branch` | +| an edit is refused because the tree moved | the tree changed after the agent read it (you on the page, perhaps): the agent reads it again and repeats the preview | +| `deploy` is refused | the reason names the rule: no deployer role, `confirm` isn't the environment's name, validation errors, a production deploy that isn't from `main` pushed and current, or another write to that environment running. Fix the cause; the agent must not route around it | +| a compare or plan says you aren't connected | your sign-in for that vault is missing or needs a new password: *Connections* on the page | +| an answer ends with `cut at 200000 characters` | ask for less: one driver (`drivers`), one artifact (`read_artifact`) | +| the skills are older than the server | run `/mcp__dirxmldev__setup` again after an upgrade | ## What stays on a workstation diff --git a/docs/features.md b/docs/features.md index 30a8101..c2c2a03 100644 --- a/docs/features.md +++ b/docs/features.md @@ -278,6 +278,13 @@ Every view of the page, by area, in every install; the items marked *local and s (Connections, *My Git provider tokens*) the request is yours on the provider. A workstation install (`serve `) creates it through `gh` or `glab`, signed in as you already; the local container is a hosted server and uses the API. +- **Agent tokens** (local and server installs): your name at the top right → *agent tokens…*. + Make a token for an agent (a label, 7 to 365 days), shown once with the command that connects + Claude Code; your tokens with when each was made, expires and was last used, and *revoke*. + *Set up your agent* names the MCP `setup` prompt and gives its text to paste into other agents; + *Download the agent kit* gives this version's rules, skills and docs. An agent with your token + acts as you through the MCP server at `/mcp` (docs/agents.md), marked `via` the token in the + audit log. - **Navigation**: every view is an address (`#type=…`), so the browser's Back and Forward move between views, a reload keeps the place, and a link to a policy, a PRD or a file can be shared. - **Live follow**: any change to the tree, by anything, reloads the page within a second. diff --git a/docs/hosted-install.md b/docs/hosted-install.md index 1837864..fef04a5 100644 --- a/docs/hosted-install.md +++ b/docs/hosted-install.md @@ -107,8 +107,13 @@ workbench with "open a branch". ## 5. Agents and the CLI -An agent (CI, an MCP server, a coding agent) does not install anything: +An agent (CI, a coding agent, a script) does not install anything: +- **A person's agent** (Claude Code, Cursor): the person makes a token on the page (*agent + tokens…*) and connects the agent to `https:///mcp`; the agent acts as that person + ([agents.md](agents.md)). `agent.tokens=false` turns personal tokens off, `agent.tokens.maxDays` + (default 365) caps their life. Nothing to set up on the server: `/mcp` and the agent kit are in + the image. - **API**: `Authorization: Bearer ` with a token from `agents=` gives it a session with its roles; every endpoint the page uses. Its name is on every audit line. - **CLI**: `docker exec idmweb idm validate /var/lib/idmweb/workspaces////tree`, diff --git a/docs/quickstart.md b/docs/quickstart.md index 4061f78..a96bd9b 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -120,7 +120,7 @@ It needs `dirxml.jar`, `dirxml_misc.jar`, `nxsl.jar`, `xp.jar`, `CommonDriverShi ## 5. The CLI, the simulator, your agents -The `idm` CLI runs inside the container, in your workspace of the branch you opened +The `idm` CLI runs inside the container, in your workspace of the first project's branch `main` (`docker exec -e IDMWEB_BRANCH=feature/x idmweb idm …` for another branch, `IDMWEB_PROJECT` for another project): @@ -141,10 +141,16 @@ The simulator's CLI is `docker exec idmweb sim …` and the Identity Application - **MCP:** the server is an MCP server at `http://localhost:8080/mcp`. Make a token on the page (your name at the top right → *agent tokens…*), which shows the command that connects Claude Code. The agent acts as you, under your roles. -- **The kit:** `setup` writes this version's rules, skills and docs to `agent/` in the folder: start - Claude Code there, or copy `agent/.claude/skills` into your own. +- **Set it up:** in Claude Code, run `/mcp__dirxmldev__setup`. It reads the rules, installs the + skills from the server and looks around, changing nothing. For another agent, the dialog's + *Set up your agent* has the same text to paste. +- **The kit:** `setup` also writes this version's rules, skills and docs to `agent/` in the folder: + start Claude Code there, or copy `agent/.claude/skills` into your own. - **The CLIs** above, and the API at `http://localhost:8080/api/…` with the same token. +[agents.md](agents.md#quick-start) has the four steps for Claude Code, every MCP tool with its +arguments, and what to do when the agent is refused. + ## 6. Vaults *Connections* on the page sets up each environment with a connection test. The container is a diff --git a/docs/security.md b/docs/security.md index 24210f2..d0a8750 100644 --- a/docs/security.md +++ b/docs/security.md @@ -53,6 +53,18 @@ The rest holds in both modes unless it names one. - Nothing is read from a vault until you choose an environment and ask. Every write to a vault (deploy, driver start, stop, restart) needs that environment's name typed into the page; the server checks it, and `IDM_AGENT_ALLOW_WRITE` does not stand in for it here. +- **Agents** (hosted mode, docs/agents.md). A person's agent token (`idmw_` and 32 random bytes) is + shown once; only its SHA-256 is kept, in the person's owner-only `users//agent-tokens.json`. + It expires (90 days by default, at most `agent.tokens.maxDays`), the person can revoke it at once, + and it carries the person's roles as of their last use of the page: a role taken away reaches + their agents the next time they use the page. Someone who leaves keeps working tokens until they + expire, so when you remove a person from the identity provider, also delete their + `users//agent-tokens.json` (or set `agent.tokens=false`) and restart. A token's session cannot make or revoke tokens. The MCP endpoint (`/mcp`) + holds no rules of its own: each tool is a call to the API with the caller's own headers, so the + sign-in, Host and Origin checks, roles, revision check, write lock, production gate and audit line + are the page's. A vault write over MCP needs `confirm`, the environment's name, which the agent is + told to take from the person in chat; the server checks it as for the page. Tool output never + holds a secret or the page's CSRF value. `agent.tokens=false` turns personal tokens off. ## Older TLS suites diff --git a/docs/tutorial.md b/docs/tutorial.md index 8378ae5..c053ee4 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -247,5 +247,8 @@ Also: `docker exec idmweb idm vault.deploy tree --env lab --dry-run` plans a deploy. - Add the engine jars to the folder's `lib/` for the compile checks and the simulator ([quickstart.md](quickstart.md#4-optional-the-engine-jars)). +- Bring your agent: make a token (your name at the top right → *agent tokens…*), connect Claude + Code with the command it shows, and run `/mcp__dirxmldev__setup`. It can then do what you did + here, asking you before it writes to the vault ([agents.md](agents.md#quick-start)). - [features.md](features.md) lists every view. - [hosted-install.md](hosted-install.md) is the same server for a team. diff --git a/docs/upgrading.md b/docs/upgrading.md index 0afde05..ff184b7 100644 --- a/docs/upgrading.md +++ b/docs/upgrading.md @@ -54,6 +54,10 @@ changes. Name a version (`:0.59.0`) to take exactly that one; then no `--pull` i gave `--port`, `--user` or `--podman` the first time, give them again. Your browser session survives the upgrade, and the page comes back to the branch and view you were on. +Setup also refreshes the agent kit in the folder's `agent/`. If you use an agent, run its setup +again (`/mcp__dirxmldev__setup` in Claude Code) so its skills are the new version's +([agents.md](agents.md)). Agent tokens survive the upgrade. + ### Check that it worked The top bar shows `web … · core …`, and it should name the new release. `setup` also prints the @@ -93,7 +97,8 @@ sed -i 's#^IDMWEB_IMAGE=.*#IDMWEB_IMAGE=ghcr.io/pointbluetechnology/dirxml-dev-w docker compose pull idmweb && docker compose up -d idmweb ``` -People signed in are asked to sign in again only if the release notes say so. Check the new +People signed in are asked to sign in again only if the release notes say so. Agent tokens are +kept; people's agents get the new version's skills by running the `setup` prompt again. Check the new version in `/api/session` or the page's top bar. Keycloak and Caddy are upgraded on their own schedule; nothing in a DirXMLDevWeb release requires a newer one unless its notes say so. diff --git a/hosted/agent-kit/AGENTS.md b/hosted/agent-kit/AGENTS.md index 3af92d6..ad7dc4c 100644 --- a/hosted/agent-kit/AGENTS.md +++ b/hosted/agent-kit/AGENTS.md @@ -20,6 +20,11 @@ Then run the server's `setup` prompt (`/mcp__dirxmldev__setup` in Claude Code): skills below from the server's resources (`dirxmldev://kit/…`) and orients the agent. An agent without MCP prompts can be given the same text from *Agent tokens…* → *Set up your agent*. +Over MCP you work in the person's own workspaces, starting on the project and branch they last had +open on the page (`session` says which). `open_branch` moves you to the branch they name +(`list_branches` lists them); the page keeps its own. Do the work on a branch, not on `main`, +unless the person says otherwise. + **The CLIs, on the machine that runs the container:** `docker exec idmweb idm …` (DirXMLDev), `docker exec idmweb sim …` (the policy simulator), `docker exec idmweb apps …` (Identity Applications). They run in your workspace of the first project's branch `main`; diff --git a/pom.xml b/pom.xml index 8c92c9d..76d0237 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.pointblue.dirxml dirxml-dev-web - 0.72.0 + 0.73.0 MIT License diff --git a/src/main/java/com/pointblue/dirxml/web/LocalSetup.java b/src/main/java/com/pointblue/dirxml/web/LocalSetup.java index 2b1a447..9084d87 100644 --- a/src/main/java/com/pointblue/dirxml/web/LocalSetup.java +++ b/src/main/java/com/pointblue/dirxml/web/LocalSetup.java @@ -217,7 +217,8 @@ static void run(Options o, PrintStream out) throws IOException, InterruptedExcep out.println("The CLI: docker exec idmweb idm validate tree (also: sim, apps)"); if (kitFiles > 0) { out.println("Agents: agent/ holds the rules, skills and docs (start Claude Code there, or copy agent/.claude/skills to your own);"); - out.println(" MCP at http://localhost:" + o.port + "/mcp with a token from the page (your name at the top right > Agent tokens...)"); + out.println(" MCP at http://localhost:" + o.port + "/mcp with a token from the page (your name at the top right > Agent tokens...);"); + out.println(" then, in Claude Code, /mcp__dirxmldev__setup installs the skills and looks around (docs/agents.md)"); } out.println("Engine jars: optional (validate's compile check and the simulator): copy them into lib/, then docker restart idmweb"); out.println("Your vault password goes in " + dir.relativize(mine) + " (the page's Connections view writes it)."); diff --git a/src/main/java/com/pointblue/dirxml/web/Mcp.java b/src/main/java/com/pointblue/dirxml/web/Mcp.java index bdd7f2a..d8eac16 100644 --- a/src/main/java/com/pointblue/dirxml/web/Mcp.java +++ b/src/main/java/com/pointblue/dirxml/web/Mcp.java @@ -36,7 +36,7 @@ final class Mcp implements HttpHandler { private static final String INSTRUCTIONS = String.join("\n", "DirXMLDevWeb: an IDM driver set as code (a git workspace of the branch the person has open) and its Identity Vaults.", "Rules, the same as the page's:", - "1. Orient with session, outline, validate. Edit the tree with run_operations: preview (dryRun, the default) before applying; list_operations names every operation and its arguments.", + "1. Orient with session, outline, validate; open_branch moves you to the branch the person names (list_branches lists them). Edit the tree with run_operations: preview (dryRun, the default) before applying; list_operations names every operation and its arguments.", "2. Validate after edits; commit with a message. Never commit secrets.", "3. Before any vault write, run plan_deploy and show the person the plan. deploy, rollback and driver_lifecycle need confirm = the environment's name, typed by the person in chat. Never fill it in yourself.", "4. Production deploys come from main, pushed and current; the server refuses otherwise. Never work around a refusal: report it.", @@ -414,6 +414,22 @@ private void define() { props("against", str("head (default) or base")), List.of(), false, a -> new Request("GET", "/api/git/diff?against=" + (a.s("against") == null ? "head" : URLEncoder.encode(a.s("against"), StandardCharsets.UTF_8)), null)); + // branches: the agent's own session, so it moves without the page + tool("list_branches", "The projects, the open one's branches and its base branch, and which branches you have open.", + props("project", str("another project (optional)")), List.of(), false, + a -> new Request("GET", "/api/workspaces" + (a.s("project") == null ? "" : "?" + a.q("project").substring(1)), null)); + edit("open_branch", "Work on another branch from now on: your workspace of it, cloned for you the first time; a new name branches from the base branch. " + + "Changes nothing in the repository until you commit and push. The page keeps its own branch.", + props("branch", str("the branch (letters, digits, '.', '_', '-' and '/')"), "project", str("another project (optional)")), List.of("branch"), + a -> { + Map body = new LinkedHashMap<>(); + body.put("branch", a.s("branch")); + if (a.s("project") != null) { + body.put("project", a.s("project")); + } + return new Request("POST", "/api/workspaces/open", body); + }); + // editing the tree tool("list_operations", "Every edit operation the tree takes (policy.add, gcv.set, driver.set, override.set, environments.set, …) with its arguments.", props(), List.of(), false, a -> new Request("GET", "/api/catalog", null)); diff --git a/src/main/java/com/pointblue/dirxml/web/Serve.java b/src/main/java/com/pointblue/dirxml/web/Serve.java index b96be9d..c6452a5 100644 --- a/src/main/java/com/pointblue/dirxml/web/Serve.java +++ b/src/main/java/com/pointblue/dirxml/web/Serve.java @@ -14,7 +14,7 @@ */ public final class Serve { - public static final String VERSION = "0.72.0"; + public static final String VERSION = "0.73.0"; private Serve() { } diff --git a/web/src/components/AgentTokensDialog.tsx b/web/src/components/AgentTokensDialog.tsx index eb45b4d..32beaef 100644 --- a/web/src/components/AgentTokensDialog.tsx +++ b/web/src/components/AgentTokensDialog.tsx @@ -138,6 +138,13 @@ export function AgentTokensDialog({ onClose }: { onClose: () => void }) {

It reads the rules, installs the skills from the server, then looks around without changing anything.

+

+ Then ask it in your own words. It starts on the project and branch you last had open here and moves to another when you ask; it previews every edit before + applying it, and shows you the plan before a deploy; a deploy, rollback or driver start or stop needs you to type the environment's name in the chat.{" "} + + How agents work with this server + +

The agent kit

This version's rules (AGENTS.md), skills and docs, with this server's MCP address ({mcpUrl}). Unzip it where you start your