Metadata-Version: 2.4
Name: persona-mcp-workspace
Version: 0.1.4
Summary: Per-job shared workspace (files) exposed as MCP tools for persona agents and the workflow engine
Requires-Python: >=3.10
Requires-Dist: fastmcp>=2.8.0
Requires-Dist: python-dotenv>=1.0.0
Description-Content-Type: text/markdown

# persona-mcp-workspace

Full-machine access exposed as MCP tools: `read`, `write`, `edit`, `bash`.
This server is meant to be installed on a real machine so an agent can
operate it remotely — the remote equivalent of a local coding agent's own
tools, with no folder confinement. Paths are used exactly as given
(absolute, or resolved against the server process's own working
directory); `bash` runs with no forced cwd, inheriting the server
process's own.

## Tools

| tool | description |
|---|---|
| `read` | read a file (text or base64), with offset/limit |
| `write` | create or overwrite a file, parents auto-created |
| `edit` | precise text replacement — one or more disjoint edits per call |
| `bash` | run a shell command, no working directory imposed |
| `skill` | load one skill's full instructions + bundled file paths |

Listing, searching, deleting and moving files are **not** separate
tools — `bash` covers them (`ls`, `grep`, `find`, `rm`, `mv`). One fewer
surface to keep in sync with the agent-facing API, and the agent already
knows how to use a shell.

There is no dedicated node in persona-workflows for these tools: a
workflow mounts this server on an agent step (or calls it) through the
existing generic `tool` node, which is agnostic to whatever tools a given
MCP server exposes — it never needs updating when this server's tool
surface changes.

## Skills

Skills are installed on disk under `.agents/skills/<name>/SKILL.md`
(override the root with `WORKSPACE_SKILLS_DIR`) — the same on-disk Agent
Skills convention used by Claude Code and other harnesses: YAML
frontmatter (`name`, `description`, optional `allowed-tools`) followed by
the skill's instructions body, plus optional `scripts/`, `references/`,
`assets/` subfolders. Drop a new skill folder in and it's picked up
immediately, no restart needed.

The catalog (name + description of every installed skill) is exposed as
the `resource://skills` MCP resource, not a tool — see below for why
and how to wire it into the system prompt. `skill(name)` is the one
remaining tool: it loads one skill's full instructions plus the relative
paths of its bundled files — fetch those with `read`, same reasoning as
above (no separate content-fetch tool).

## MCP resources

| resource | returns |
|---|---|
| `resource://cwd` | this server's working directory |
| `resource://skills` | the skill catalog — name + description, one per line |

`resource://cwd` reflects `WORKSPACE_CWD` if set at startup (the process
`chdir`s there before doing anything else, so `read`/`write`/`edit`
relative paths and `bash`'s cwd all agree with what's reported);
otherwise it's whatever directory the process was launched from.

### Getting these into the system prompt

**MCP resources are not read automatically by persona-core** — there's
no handshake-time mechanism that pulls them in for you. To wire one in,
configure a resource-backed `Variable` on the agent:

```json
{
  "name": "cwd",
  "source": {
    "type": "resource",
    "mcpServer": "<this feature's registered name>",
    "resourceUri": "resource://cwd"
  }
}
```

(same shape for `resourceUri: "resource://skills"`) and reference
`{{ cwd }}` / `{{ skills }}` inside the agent's `systemInstructions`.
persona-core resolves each fresh on every turn (no caching), so a skill
added after the agent was created still shows up. Without this wiring,
`skill(name)` still works as an explicit tool call — you'd just be
missing the ambient catalog telling the agent what's available to load.

### `edit` semantics

`edit(path, edits: [{old_string, new_string, replace_all?}])`. Each
`old_string` must match exactly once in the file unless `replace_all` is
set, in which case every occurrence is replaced. Edits in one call are
applied in order against the progressively-edited content. All edits are
validated before anything is written — if one fails (not found, or
ambiguous without `replace_all`), the file is left untouched.

## No confinement, by design

There is no per-job workspace folder and no path jail: `read`/`write`/
`edit` accept any path this process can reach, and `bash` has no forced
working directory. Running this server means trusting the project's
agents with the machine under the server's user account — the **only**
gate is authentication (JWT), not a filesystem boundary. Install it under
a dedicated user with exactly the permissions you want to grant.

`project_id` (from the verified JWT) and `execution_id`/`workspace_id`
(when the caller sends them in the MCP request `_meta` — persona-core
forwards the `AgentContext` there automatically, persona-workflows sets
`workspace_id` to the root execution id) are logged with every call for
audit/traceability, but they never restrict what a call can touch.

`bash` always shells out through a real `bash`, never the OS default
shell — on Windows a `bash` executable must be on PATH (Git for Windows
or WSL both provide one); plain cmd.exe/PowerShell won't do, since agents
send POSIX shell syntax.

`WORKSPACE_SHELL_MAX_TIMEOUT` (default 600s) caps per-command timeouts.
`WORKSPACE_CWD`, if set, is `chdir`'d into at startup, before anything
else — otherwise the process just keeps whatever cwd it was launched
with. `WORKSPACE_SKILLS_DIR` (default `.agents/skills`) points at the
skills root.

## Running

```bash
uv sync
uv run persona-mcp-workspace --port 9100    # JWT auth on (default)
uv run persona-mcp-workspace --no-auth      # local development

# or, from any machine with no local checkout:
uvx persona-mcp-workspace --port 9100
```

Register it in persona as a global feature template (type `mcp`,
transport `http`, `enablePersonaAuth: true`) named `workspace`; mount it
on an agent step via its per-step `toolsets`, or invoke it directly with
a generic `tool` node.
