task-mcp 0.3.0

MCP server for task runner integration — Agent-safe harness for defined tasks
Documentation
# task-mcp

Agent-safe task runner MCP server backed by [just](https://just.systems/).

Exposes predefined justfile recipes as MCP tools, with access control via the `[allow-agent]` group attribute.

## Tools

| Tool | Description | Annotations |
|------|-------------|-------------|
| `session_start` | Set the working directory for this session. Must be called before `run` or `list` (unless `list` is given an explicit `justfile` path). | destructive |
| `list` | List available tasks from justfile. Returns names, descriptions, parameters, and groups. Requires `session_start` when no `justfile` parameter is given. | read-only, idempotent |
| `run` | Execute a predefined task. Only tasks visible in `list` can be run. Requires `session_start`. | destructive |
| `logs` | Retrieve execution logs of recent runs. Returns summary list or full output by task ID. | read-only, idempotent |
| `info` | Show current session state: active workdir, resolved justfile path, and task mode. | read-only, idempotent |

## Installation

```bash
cargo install --path .
```

Requires `just` to be installed and available on `PATH`.

## Usage

Start as MCP server (stdio transport):

```bash
task-mcp --mcp
```

### Claude Code integration

Add to your Claude Code MCP configuration:

```json
{
  "mcpServers": {
    "task-mcp": {
      "command": "task-mcp",
      "args": ["--mcp"]
    }
  }
}
```

## Configuration

Configuration is loaded from `.task-mcp.env` in the current directory (if present), then from environment variables.

| Variable | Default | Description |
|----------|---------|-------------|
| `TASK_MCP_MODE` | `agent-only` | `agent-only`: only `[allow-agent]` tagged recipes; `all`: all non-private recipes |
| `TASK_MCP_JUSTFILE` | auto-detect | Path to justfile (relative or absolute) |
| `TASK_MCP_ALLOWED_DIRS` | _(any)_ | Comma-separated list of directories allowed as session working directories. If unset, any directory is accepted. Paths are canonicalized on parse. |

`.task-mcp.env` example:

```env
TASK_MCP_MODE=agent-only
TASK_MCP_JUSTFILE=./justfile
TASK_MCP_ALLOWED_DIRS=/home/user/projects/my-project,/home/user/projects/other-project
```

## Justfile setup

Mark recipes as agent-safe using the `[group('agent')]` attribute:

```just
# Build the project
[group('agent')]
build:
    cargo build --release

# Run tests
[group('agent')]
test filter="":
    cargo test {{filter}}

# Deploy to production — NOT exposed to agent
deploy:
    ./scripts/deploy.sh
```

In `agent-only` mode (default), only `build` and `test` are returned by `list` and executable via `run`. The `deploy` recipe is hidden.

In `all` mode, all non-private recipes are exposed.

## Workflow

The typical agent workflow is:

1. Call `session_start` with the project directory to establish the working context.
2. Call `list` to discover available tasks.
3. Call `run` to execute a task.
4. Call `logs` to inspect output of a past run.
5. Call `info` at any point to confirm the current session state.

`list` can also be called without a prior `session_start` when an explicit `justfile` path is provided — this is useful for exploration before committing to a working directory.

## Tool details

### session_start

```json
{ "workdir": "/absolute/path/to/project" }
```

- `workdir`: absolute path to the project directory; must be accessible and, if `TASK_MCP_ALLOWED_DIRS` is set, must fall within one of the allowed directories
- Returns a confirmation message with the resolved path on success

### info

No parameters required. Returns:

- `workdir`: active working directory (or `null` if `session_start` has not been called)
- `justfile`: resolved justfile path (or `null` if not resolvable from the current session state)
- `mode`: current task mode (`agent-only` or `all`)

### list

```json
{
  "filter": "agent",
  "justfile": "./justfile"
}
```

- `filter`: optional group name to narrow results
- `justfile`: optional path override

### run

```json
{
  "task_name": "test",
  "args": { "filter": "unit" },
  "timeout_secs": 120
}
```

- `task_name`: must appear in `list` output
- `args`: named arguments matching recipe parameter names
- `timeout_secs`: default 60

Returns a `TaskExecution` object with `id`, `exit_code`, `stdout`, `stderr`, `duration_ms`, and `truncated` fields.

### logs

```json
{ "task_id": "<uuid>", "tail": 50 }
```

- `task_id`: omit to get summary of the 10 most recent executions; provide to get full output
- `tail`: restrict stdout to the last N lines (only when `task_id` is provided)

In-memory only; logs are lost on server restart.

## Output truncation

stdout and stderr are capped at 100 KB per execution. When truncated, the `truncated` field is `true` and the head and tail of the output are preserved. Use `logs` with `tail` to retrieve specific portions.

## Security

- Only recipes whitelisted by `list` can be executed via `run`
- `session_start` validates the requested directory against `TASK_MCP_ALLOWED_DIRS` using canonicalized `Path::starts_with`; symlinks are resolved before comparison to prevent traversal attacks
- Argument values are validated to reject shell metacharacters
- Execution timeout is enforced (default: 60 s)
- `open_world_hint = false` on all tools: no external network calls

## License

MIT OR Apache-2.0