Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ For concept introductions, tutorials, and guides, see the [Learn](/learn) sectio
| ---------------------------------------------- | -------------------------------------------------------------------------- |
| [REST](./rest/overview.md) | Auto-REST interface, querying, content types, headers, WebSockets, and SSE |
| [HTTP](./http/overview.md) | HTTP server configuration, TLS, and the `server` API |
| [MCP](./mcp/overview.md) | Built-in Model Context Protocol server, profiles, CLI, and migration |
| [Security](./security/overview.md) | Authentication mechanisms, certificates, and CORS/SSL configuration |
| [Users & Roles](./users-and-roles/overview.md) | RBAC, roles configuration, and user management operations |

Expand Down
128 changes: 128 additions & 0 deletions reference/mcp/cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
---
title: Harper MCP CLI
---

# Harper MCP CLI

<VersionBadge version="v5.1.0" />

`harper mcp` is a stdio bridge that lets MCP hosts (Claude Desktop, Cursor, Zed, or any client that speaks the [stdio MCP transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio)) talk to a running Harper instance over Harper's Streamable HTTP MCP endpoint.

The CLI is bundled with Harper itself; if `harper` is on your `PATH`, so is `harper mcp`.

## Subcommands

```bash
harper mcp [subcommand] [flags]
```

| Subcommand | Purpose |
| -------------- | ------------------------------------------------------------------------------------------------ |
| _(default)_ | Run the stdio bridge — JSON-RPC frames on stdin, responses on stdout, until stdin closes. |
| `print-config` | Emit a paste-ready config block for an MCP host (`--client claude-desktop`, `cursor`, or `zed`). |
| `doctor` | Connect, complete an `initialize` handshake, list tools, clean up; report each step. |
| `help` | Print the help text. |

## Connection modes

`harper mcp` connects in one of two modes, chosen automatically based on whether `--target` is set:

### Local UDS (default)

With no `--target` flag, the CLI connects to the Harper running on the same host via the operations API Unix Domain Socket — the same socket `bin/cliOperations` uses. Filesystem permissions on the socket are the access gate; no credentials are required or sent.

The UDS path is derived from `operationsApi.network.domainSocket` in `harperdb-config.yaml` and is typically `<rootPath>/sockets/operations-server`.

### Network HTTPS / HTTP

`--target https://node.example.com:9926` connects over the network to a remote Harper. Credentials are resolved with this precedence (highest first):

1. `--bearer <token>` — explicit bearer token.
2. `--username` + `--password` — explicit Basic auth.
3. URL-embedded user/pass, e.g. `--target https://alice:pw@node:9926`.
4. Saved JWT from `~/.harperdb/credentials.json` for the resolved target (populated by `harper login`).

If none of these are present, the request goes out unauthenticated and Harper gates the response accordingly.

Use `--insecure` to skip TLS certificate validation (network mode only) — useful for local self-signed certs during development.

## Flags

| Flag | Default | Purpose |
| --------------------- | ----------------------------- | --------------------------------------------------------------------------------- |
| `--profile <name>` | `application` | `operations` or `application`. Determines which MCP endpoint the CLI connects to. |
| `--target <url>` | _(local UDS)_ | Network endpoint. Switches the CLI into network mode. |
| `--mount-path <path>` | `/mcp` | Overrides the mount path. Match whatever `mcp.<profile>.mountPath` is set to. |
| `--username <u>` | _(none)_ | Basic auth username (network mode). |
| `--password <p>` | _(none)_ | Basic auth password (network mode). |
| `--bearer <token>` | _(none)_ | Bearer token (network mode). Wins over `--username` / `--password`. |
| `--insecure` | _(off)_ | Skip TLS certificate validation (network mode only). |
| `--client <name>` | _(required for print-config)_ | `claude-desktop`, `cursor`, or `zed`. |
| `--help`, `-h` | _(off)_ | Print the help text and exit. |

## `harper mcp` (the bridge)

The default subcommand runs until stdin closes. The expected use is to invoke it from an MCP host's configuration block (see [print-config](#harper-mcp-print-config) below). Each line of stdin is parsed as a JSON-RPC frame and POSTed to the Harper MCP endpoint; each response (whether JSON or SSE-streamed) is emitted to stdout as line-delimited JSON-RPC.

After the `initialize` handshake completes, the bridge opens a long-lived `GET /mcp` request — Harper's server-push channel — and forwards every `notifications/tools/list_changed` and `notifications/resources/list_changed` frame to stdout. The host sees one unified MCP stream.

Logs (status, errors, dropped invalid stdin lines) go to stderr so they never collide with the JSON-RPC channel.

## `harper mcp print-config`

Emits a paste-ready JSON block for the requested MCP host along with a comment indicating where to put it.

```bash
harper mcp print-config --client claude-desktop
```

Produces:

```text
# Target file: ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows)
{
"mcpServers": {
"harper": {
"command": "harper",
"args": ["mcp"]
}
}
}
# Note: Restart Claude Desktop after editing the file.
# Note: Merge into an existing `mcpServers` block if you already have one.
```

The generated `args` array reflects whatever flags you pass to `print-config` (other than `--client`). For instance:

```bash
harper mcp print-config --client cursor --target https://node.example.com:9926 --profile operations
```

emits a block whose `args` is `["mcp", "--profile", "operations", "--target", "https://node.example.com:9926"]`. This lets you generate fully-resolved config blocks for non-default deployments without hand-editing.

Supported clients:

- **`claude-desktop`** — Anthropic Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows).
- **`cursor`** — Cursor IDE (`~/.cursor/mcp.json`).
- **`zed`** — Zed editor (Zed `settings.json`, under `context_servers`).

## `harper mcp doctor`

Runs a three-step smoke check against the configured connection:

1. POST `initialize` — verifies the transport works and Harper accepts the handshake. Captures the negotiated protocol version and session id.
2. POST `tools/list` — verifies the session is usable and reports how many tools are visible to the authenticated user.
3. DELETE `/mcp` — cleans up the session. This step is allowed to fail (when `mcp.session.allowClientDelete` is `false` the server returns 405); overall doctor exit is still success.

Each step prints `[OK]` or `[FAIL]` with a short detail line. Exit code is `0` on success, `1` on any non-tolerable failure.

```bash
$ harper mcp doctor --target https://node.example.com:9926 --bearer $TOKEN
[OK ] initialize - session=ab12... protocol=2025-06-18
[OK ] tools/list - 14 tool(s) visible
[OK ] session cleanup

All checks passed.
```

Use `doctor` as a quick "is the wire path healthy?" check before pointing a real MCP host at the server.
156 changes: 156 additions & 0 deletions reference/mcp/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
---
title: MCP Configuration
---

# MCP Configuration

<VersionBadge version="v5.1.0" />

All MCP configuration lives under the top-level `mcp:` block in `harperdb-config.yaml`. Each profile (`operations`, `application`) is enabled by the **presence** of its sub-block — there is no separate `enabled` flag. A minimal "turn it on" config is therefore just:

```yaml
mcp:
operations: {}
application: {}
```

That boots both profiles with default settings: the operations profile mounts at `/mcp` on the operations server, the application profile mounts at `/mcp` on the application HTTP server, and the default allow lists / rate limits apply.

## `mcp.operations.*`

Configures the operations-profile MCP endpoint that wraps Harper's operation catalog.

### `mcp.operations.mountPath`

Type: `string`

Default: `/mcp`

URL path the MCP endpoint mounts on. Change it if `/mcp` collides with another route in your application.

### `mcp.operations.allow`

Type: `array<string>` (glob patterns or literal operation names)

Default: `['describe_*', 'list_*', 'search_*', 'get_job', 'get_status', 'get_analytics', 'get_metrics', 'system_information', 'read_log', 'read_audit_log']`

Operations exposed as MCP tools. Glob `*` matches any sequence of characters; literal names match exactly. Setting `allow` **replaces** the default list; it does not merge. To add destructive or sensitive operations to the surface (e.g. `set_configuration`, `drop_table`), include them here explicitly.

The default list intentionally avoids `get_*` as a glob because that pulls in `get_configuration` (which can return TLS / S3 / authentication secrets), `get_components` / `get_component_file` / `get_custom_function*` (which return component source that can embed secrets), `get_backup`, and `get_deployment*`. These are all gated by `verifyPerms` at dispatch, but defaulting to "expose them to an LLM if a super_user calls them" is the wrong default — opt them in deliberately.

### `mcp.operations.deny`

Type: `array<string>` (glob patterns or literal operation names)

Default: `[]`

Operations to filter out **after** the allow list has been applied. Useful for taking back a single operation that a broad allow glob would otherwise expose.

### `mcp.operations.maxTools`

Type: `integer` (minimum 1)

Default: `200`

Maximum number of tools returned in a single `tools/list` response page. The MCP cursor is used to page through any overflow.

### `mcp.operations.rateLimit.*`

See [`mcp.<profile>.rateLimit.*`](#mcpprofileratelimit) below — the schema is identical for both profiles.

Default per-profile values for `operations`: `perToolPerSecond: 10`, `perToolBurst: 20`, `sessionConcurrency: 25`, `sessionPerSecond: 100`.

## `mcp.application.*`

Configures the application-profile MCP endpoint that walks your exported `Resource` classes. All `mcp.operations.*` keys above also apply here; the additional knob is:

### `mcp.application.searchMaxResults`

Type: `integer` (minimum 1)

Default: `1000`

Hard cap on the number of records a generated `search_<resource>` tool can return per call. Clients still pass `limit`, but the server clamps to this ceiling regardless of what the client requests — bounded to keep a runaway agent from exhausting memory.

Default per-profile rate-limit values for `application`: `perToolPerSecond: 25`, `perToolBurst: 50`, `sessionConcurrency: 50`, `sessionPerSecond: 200`.

## `mcp.<profile>.rateLimit.*`

Per-session, per-tool token-bucket rate limits. A bucket exists per `(session, tool)` pair plus one per-session bucket across all tools. When either bucket is exhausted, `tools/call` returns `result.isError = true` with `kind: "rate_limited"` — **not** a JSON-RPC error — so the LLM can read the message and back off without the transport tearing down.

### `mcp.<profile>.rateLimit.perToolPerSecond`

Type: `number` (minimum 0)

Default: `10` (operations) / `25` (application)

Sustained rate at which the per-tool token bucket refills. Set to `0` to disable per-tool throttling on this profile.

### `mcp.<profile>.rateLimit.perToolBurst`

Type: `number` (minimum 0)

Default: `20` (operations) / `50` (application)

Burst capacity of the per-tool token bucket — how many back-to-back calls a single tool can absorb before sustained-rate refill kicks in.

### `mcp.<profile>.rateLimit.sessionConcurrency`

Type: `integer` (minimum 0)

Default: `25` (operations) / `50` (application)

Maximum number of `tools/call` invocations a single session may have in flight at once. Subsequent attempts return `kind: "rate_limited"` with `scope: "concurrency"`.

### `mcp.<profile>.rateLimit.sessionPerSecond`

Type: `number` (minimum 0)

Default: `100` (operations) / `200` (application)

Sustained per-session rate across **all** tools combined. Protects a worker from a single session that spreads its calls across many distinct tools (and so would otherwise dodge `perTool*`).

## `mcp.session.*`

Settings that apply to MCP session lifecycle on both profiles.

### `mcp.session.idleTimeoutSeconds`

Type: `integer` (minimum 1)

Default: `1800` (30 minutes)

Idle window after which a session record in `system.mcp_session` is TTL-evicted. The next request bearing the evicted session id receives HTTP 404 and the client is expected to re-`initialize`.

### `mcp.session.allowClientDelete`

Type: `boolean`

Default: `false`

When `true`, Harper accepts client-issued `DELETE /mcp` requests that explicitly terminate a session. When `false` (the default), `DELETE` returns 405 with an `Allow` header — sessions only end via idle eviction or explicit server-side cleanup.

## Example

A common deployment pattern that locks down the operations profile to a small explicit set, enables MCP DELETE for graceful client logout, and raises per-tool throughput for the application profile:

```yaml
mcp:
operations:
allow:
- describe_all
- describe_database
- system_information
- get_job
rateLimit:
perToolPerSecond: 5
perToolBurst: 10
application:
searchMaxResults: 500
rateLimit:
perToolPerSecond: 50
perToolBurst: 100
session:
idleTimeoutSeconds: 3600
allowClientDelete: true
```
Loading