Skip to content

Agents over MCP, phase 2: change tools, approved in the console - #6

Merged
jcombs-pointblue merged 6 commits into
mainfrom
feature/mcp-phase2
Oct 31, 2026
Merged

jcombs-pointblue merged 6 commits into
mainfrom
feature/mcp-phase2

Conversation

@jcombs-pointblue

Copy link
Copy Markdown
Contributor

Phase 2 of docs/MCP.md: the change tools, with approval in the console.

Stacked on #5: the base is feature/mcp-phase1, so the diff shows phase 2 only. Merge #4, then #5, then this; GitHub retargets this PR to main when #5's branch is deleted.

Change tools

Each tool is listed only when the key has the role it needs, and refuses at once when that role's scope doesn't reach the object:

Tool Role API call
enable_account, disable_account editor PATCH objects/{guid}/account {disabled}
unlock_account editor POST objects/{guid}/unlock
add_to_group, remove_from_group editor POST objects/{guid}/memberships (both sides)
start_driver, stop_driver operator POST idm/drivers/{guid}/start|stop
run_job, abort_job operator POST idm/jobs/{guid}/start|abort
resync_driver operator POST idm/drivers/{guid}/resync {since?}
migrate_object_to_app operator POST idm/drivers/{guid}/migrate/to-app with the object as base
migrate_from_app operator POST idm/drivers/{guid}/migrate/from-app
change_status any the state of a change the key asked for

The design note listed "resynchronize an object". The API resynchronizes a driver (all of it, or since a time), so that's the tool; the note now says so.

Approval (approveChanges, per directory, on by default)

  • Asking: the tool answers 202 with a pending change (AgentChanges): its id, a summary and the link /agent-keys?change=<id>.
  • Approving or declining: on the Agent keys page, Changes agents asked for lists them. The link opens the review dialog. The person approves, typing the directory's name on production, or declines; an admin can also decline.
  • What approval does: the call is made then, in-process, as the key: Agents.check checks the key and the account again, and eDirectory decides with the person's rights.
  • When it can't be approved: after an hour the change expires; a change from a key revoked since can't be approved.
  • Storage: pending changes are kept in memory, so a restart drops them (documented).
  • With approval off: the change is made at once. On production the agent passes confirm = the directory's name, which the engine's operations require.
  • Audit: agent-change.request, agent-change.approve and agent-change.decline, plus the change's own entry with via = agent and the key.

Also

Account changes and unlock now report eDirectory's "no such attribute" answer, which it gives under proxied authorization for a write the person has no right to, as insufficient-rights, as object edits already did. This affects single sign-on sessions too.

Tests

  • AgentsTest:
    • a read-only key gets no change tools;
    • out-of-scope changes are refused;
    • pending → approve, then the change is in the directory;
    • only the key's owner can approve;
    • decline;
    • a revoked key's change can't be approved;
    • approval off: the driver starts at once, and on production confirm is required;
    • approving on production needs the directory's name.
  • All 165 server tests and 53 UI tests pass; npm run build passes.
  • On the DemoServer: pat's agent asked to disable a Sales user; the link opened the review dialog; Approve disabled the account; the list showed it approved.
  • Live on idm254 with LiveAgentsCheck, all steps passing:
    • the test user's key asked to disable a second scratch user, and the change waited for approval;
    • the user approved it, and eDirectory refused it with insufficient-rights: the user has no rights over that account, which shows the change runs with the person's rights;
    • change_status reported the outcome, a second change was declined, and with approval off the change ran at once (refused by eDirectory the same way);
    • the phase 1 steps still pass, and the scratch container was deleted.

Not in this phase: setting passwords (design: generated, then applied by an operator in the console), and Identity Applications (phase 3).

🤖 Generated with Claude Code

jcombs-pointblue and others added 5 commits October 30, 2026 13:19
eDirectory refuses the proxied-authorization control for disabled and
expired accounts, but accepts it for an account locked by intruder
detection: on the lab (9.3.3) the locked account's own bind returned 53
while a proxied read as it succeeded. So a locked person could sign in
through single sign-on and act in the console.

OidcLogin's search now reads lockedByIntruder and loginIntruderResetTime
and refuses with the new code "locked". A lock whose reset time has
passed counts as over: eDirectory leaves the attribute TRUE after the
reset until the next login (checked on the lab with a real lockout).
A single sign-on session reaching another server (Server.on) checks the
same on that server before acting as the person there. The service
account check covers reading the two attributes, and the live check
LivePhase3Check covers a locked and a lapsed account.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent keys (docs/MCP.md): a person signed in directly or with single
sign-on makes a key under their name > Agent keys (password typed again,
or a sign-in within 10 minutes). dc_ + 32 random bytes, SHA-256 kept in
agents/keys.json on the data volume, outside the versioned configuration
and outside backups. Viewer always, editor and operator only if the person
has them, never admin, no task presets. 30 days by default, at most the
directory's longest life (90) and 14 days with single sign-on; 14 days
unused stops a key. Admins list and revoke everyone's.

A key's calls run as the person the way single sign-on does: the service
account's connection with the proxied-authorization control, and the
engine opened acting as them. On every call the console reads the
person's account as the service account and refuses a locked (intruder
detection: eDirectory accepts the control for it), disabled or expired
one; direct sign-in grants come from that read, single sign-on grants
from the last sign-in.

/mcp: JSON-RPC 2.0, Streamable HTTP, stateless. 22 read tools, each an
in-process call to the existing API with the key's session (Mcp.Local),
so roles, scopes and rights stay the API's. A key works only there.

Per directory (Settings > Directory > Agent keys): on/off (off by
default), longest life, and approveChanges, required by default, for the
change tools of phase 2. Audit: agent-key.create/revoke/use, and the
actor's agent (label and id) on entries made with a key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A test user made for the run (no password) signs in through a fake
provider, makes a key, and its agent calls /mcp: whoami, reads under the
control, the object inspector, Identity Manager under the user's rights;
then a locked, an unlocked and a disabled account, and a revoked key. The
container is deleted at the end. Passed on idm254 (eDirectory 9.3.3, IDM
4.10.2), 2026-10-30.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Change tools, offered only when the key has the role, its scopes
reaching the object: enable, disable and unlock an account, add to and
remove from a group (editor); start and stop a driver, run and abort a
job, resynchronize a driver, migrate an object into the application and
objects from it (operator); change_status.

With the directory's approveChanges on (the default) a change is kept as
pending (AgentChanges, in memory, an hour) and the agent gets a link,
/agent-keys?change=<id>. The key's owner reviews it there and approves
it (typing the directory's name on production) or declines it; approving
makes the API call then, in-process, as the key, checked again. With
approval off the call is made at once, with confirm as the production
directory's name. Audited as agent-change.request/approve/decline, and the
change itself as the agent.

Account changes and unlock now explain eDirectory's "no such attribute"
answer to a write without rights under proxied authorization as
insufficient-rights, as object edits already did.

LiveAgentsCheck covers approving, declining and approval off; passed on
idm254 2026-10-30.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	console-server/src/main/java/com/pointblue/console/server/http/Agents.java
#	console-server/src/main/java/com/pointblue/console/server/http/Mcp.java
#	console-server/src/main/java/com/pointblue/console/server/http/Server.java
#	console-server/src/test/java/com/pointblue/console/server/http/AgentsTest.java
#	console-server/src/test/java/com/pointblue/console/server/live/LiveAgentsCheck.java
#	console-web/src/api.ts
#	console-web/src/help/content.ts
#	console-web/src/pages/AgentKeysPage.tsx
#	console-web/src/pages/SettingsPage.tsx
#	docs/API.md
#	docs/MCP.md
#	docs/STATUS.md
@jcombs-pointblue
jcombs-pointblue changed the base branch from feature/mcp-phase1 to main October 31, 2026 00:34
@jcombs-pointblue
jcombs-pointblue merged commit 93ab0d7 into main Oct 31, 2026
1 check passed
@jcombs-pointblue
jcombs-pointblue deleted the feature/mcp-phase2 branch October 31, 2026 00:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant