Skip to main content
Glama
kdrive-mcp-server

kdrive-mcp-server

by sstolz
README.md
# kdrive-mcp-server

> **Beta: the interface may change before 1.0.** Tool names, parameters,
> settings and outputs can still change between beta versions.
>
> **Unofficial project, not affiliated with Infomaniak.** It is distinct from
> the `@infomaniak/mcp-server-kdrive` npm package.

An [MCP](https://modelcontextprotocol.io) server that gives AI clients
controlled access to an Infomaniak kDrive. A client can browse, search and read
files under the folders you configure; in `write` mode it can also create,
rename, move, trash, restore and upload. Every call goes through a policy
(configured roots, deny patterns, received-share rules) and every write is
recorded in an audit log before and after it is sent.

**Status:** development version `0.1.0` ([VERSION](VERSION)). No release has been
published yet, so you build the image or the package from this repository.
Changes are listed under [Unreleased](CHANGELOG.md#unreleased).

## What it offers

- **Three modes**, cumulative: `read` (default), `write`, `share`. Today `share`
  adds nothing beyond `write`; sharing tools are planned.
- **Two transports:** stdio for a local client, and authenticated Streamable
  HTTP (OAuth proxy) for a remote connector.
- **Scoped access:** only configured drives and root folders are visible; deny
  patterns hide paths; items shared with you are off unless you opt in.
- **Bounded results:** reads, downloads and listings have size, item and time
  limits; names, paths and file content are returned as untrusted data.

## Install

Prerequisites:

- an Infomaniak API token for kDrive, from an account with the **admin** role on
  every drive you configure (startup checks this);
- Docker with Compose and GNU Make 3.81 or later to build from this repository;
- for the packaged CLI only: Node.js 26.13 or later (26.x).

Build from a checkout of this repository:

```sh
make setup        # build the development container and install dependencies
make image        # container image: kdrive-mcp-server:runtime-<arch>
make pack         # npm package: artifacts/kdrive-mcp-server-0.1.0.tgz
```

`make image IMAGE_PLATFORMS=linux/amd64` builds one architecture only. Nothing
is pushed to a registry. To use the CLI on your machine, install the package
with `npm install -g artifacts/kdrive-mcp-server-0.1.0.tgz`; it provides the
`kdrive-mcp-server` command. `kdrive-mcp-server --help` and `--version` need no
configuration.

## Configure

Settings come from, in priority order: CLI options, environment variables, one
YAML file selected with `--config PATH`, then defaults. No `.env` file is read.
The essential settings:

| Environment variable        | Purpose                                                                                                                      | Default            |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| `KDRIVE_TOKEN_FILE`         | Absolute path of a file holding the kDrive API token (preferred). `KDRIVE_TOKEN` passes it inline.                           | —                  |
| `KDRIVE_ID` / `KDRIVE_IDS`  | One drive ID, or a comma-separated list whose first entry is the default drive.                                              | —                  |
| `KDRIVE_MCP_MODE`           | `read`, `write` or `share`.                                                                                                  | `read`             |
| `KDRIVE_MCP_ROOTS`          | Root folders per drive, by ID or path, for example `7:42,8:/Documents`. Must cover every configured drive.                   | folder 1 per drive |
| `KDRIVE_MCP_DENY`           | Comma-separated path patterns to hide, for example `/Private/**`. Case-sensitive; see [Limits and risks](#limits-and-risks). | none               |
| `KDRIVE_MCP_AUDIT_LOG`      | `stderr` or an absolute file path for the write audit log (`write` and `share` modes).                                       | `stderr`           |
| `KDRIVE_MCP_SHARED_WITH_ME` | `1` to read items other users shared with you (`0` or `1`).                                                                  | `0`                |
| `KDRIVE_MCP_TRANSPORT`      | `stdio` or `http`.                                                                                                           | `stdio`            |

Never put a token in a CLI argument or in the YAML file. Size limits
(`KDRIVE_MCP_MAX_READ_BYTES`, `KDRIVE_MCP_MAX_OUTPUT_CHARS`, …), the HTTP and
OAuth settings, every CLI option and YAML key, defaults, bounds and error codes
are in the [configuration reference](features/F-002-configuration.md). A
configuration or startup failure prints one line,
`Configuration error [<code>] (<setting>).`, and exits 1.

## Connect an AI client

### Local client (stdio)

Most desktop clients and coding assistants start local MCP servers from a JSON
configuration of this shape (see your client's documentation for the file
location):

```json
{
  "mcpServers": {
    "kdrive": {
      "command": "kdrive-mcp-server",
      "env": {
        "KDRIVE_TOKEN_FILE": "/home/me/.config/kdrive/token",
        "KDRIVE_ID": "123456",
        "KDRIVE_MCP_ROOTS": "123456:/Documents",
        "KDRIVE_MCP_MODE": "read"
      }
    }
  }
}
```

The server speaks MCP on stdin and stdout and writes its own diagnostics to
stderr.

### Remote connector (authenticated HTTP)

For web and mobile clients, run the container image behind a TLS reverse proxy.
The server acts as an OAuth proxy in front of an OpenID Connect provider (for
example Infomaniak's) and only accepts the subjects or emails you allow.

1. Register an OAuth client at your provider with the redirect URI
   `<MCP_PUBLIC_URL>/oauth/callback`, for example
   `https://mcp.example.com/mcp/oauth/callback`.
2. Provision three secret files: the kDrive token, the upstream client secret,
   and an EC P-256 signing key in PKCS#8 PEM (the server never generates one).
3. Start from [examples/compose.yaml](examples/compose.yaml) (environment and
   Compose secrets behind Traefik) or [examples/config.yaml](examples/config.yaml)
   (YAML alternative). The key settings:

   ```yaml
   KDRIVE_MCP_TRANSPORT: http
   OAUTH_MODE: proxy
   MCP_HTTP_HOST: 0.0.0.0 # inside the container only
   MCP_PUBLIC_URL: https://mcp.example.com/mcp
   OAUTH_ISSUER: https://login.example.com # your OIDC provider
   OAUTH_UPSTREAM_CLIENT_ID: <client id>
   OAUTH_ALLOWED_EMAILS: you@example.com
   OAUTH_ALLOWED_REDIRECT_URIS: https://claude.ai/api/mcp/auth_callback
   ```

4. In the client, add a custom connector with the URL `MCP_PUBLIC_URL`. The
   client registers itself, you log in at your provider, and the server checks
   your identity against the allowlist.

`OAUTH_ALLOWED_REDIRECT_URIS` lists the callback URLs of the clients you use;
the [client compatibility matrix](docs/mcp-client-compatibility.md) gives them
for common clients. Operator details (signing key, redirect matching, restart
behaviour, reverse proxy) are in the [OAuth proxy notes](docs/oauth-proxy.md)
and the [deployment guide](docs/deployment.md), which walks through a personal
server installation step by step. In `write` and `share` modes a
file audit log needs a writable mount, and `stop_grace_period` must be at least
75 s so in-flight writes can record their outcome.

## Tools

Item metadata has exactly `drive_id`, `id`, `name`, `type` and `path`.

Read tools (all modes):

| Tool                    | Purpose                                                                        |
| ----------------------- | ------------------------------------------------------------------------------ |
| `kdrive_list_drives`    | List the configured drives.                                                    |
| `kdrive_list`           | List a folder's children, with optional `type` filter and continuation.        |
| `kdrive_info`           | Metadata of one item.                                                          |
| `kdrive_resolve_path`   | Find an item by its path.                                                      |
| `kdrive_search`         | Search names under the roots or one folder (`query` required).                 |
| `kdrive_recents`        | Recently changed items under the roots or one folder.                          |
| `kdrive_shared_with_me` | Items other users shared with you (needs `KDRIVE_MCP_SHARED_WITH_ME`).         |
| `kdrive_my_shared`      | Items you shared.                                                              |
| `kdrive_read`           | Extract text (UTF-8/16, Windows-1252), PDF text, or a PNG preview of an image. |
| `kdrive_download`       | Raw bytes as base64 chunks, continued with `offset_bytes` and `etag`.          |

Resource template: `kdrive://{drive_id}/file/{id}` returns one complete binary
file (`application/octet-stream`) when it fits the read limit.

Write tools (`write` and `share` modes only):

| Tool                   | Purpose                                                          |
| ---------------------- | ---------------------------------------------------------------- |
| `kdrive_create_folder` | Create a folder.                                                 |
| `kdrive_rename`        | Rename an item.                                                  |
| `kdrive_move`          | Move an item to another folder of the same drive.                |
| `kdrive_trash`         | Move an item to the kDrive trash (never a permanent delete).     |
| `kdrive_restore`       | Restore a trashed item to its former parent.                     |
| `kdrive_upload`        | Upload `text` or `base64` content, new file or in-place replace. |

Each write returns `succeeded`, `failed`, `uncertain` or `denied`. After
`uncertain`, check the item with a read tool before trying again: the server
never replays an uncertain write.

Key limits:

- `kdrive_upload` accepts at most 524,288 decoded bytes; over HTTP the request
  body limit is 262,144 bytes. Use `base64` for large or non-ASCII content.
- Names given to `kdrive_upload`, `kdrive_create_folder` and `kdrive_rename`
  must be in Unicode NFC (composed form); other forms are refused with
  `INVALID_ARGUMENTS`.
- `kdrive_read` handles a conservative PDF profile only, so many valid PDFs
  return `UNSUPPORTED_FORMAT`; images are returned within 512x512 pixels.
- Listings return at most 256 items per call and continue with single-use
  cursors that expire after five minutes; downloads continue with
  `offset_bytes` and the previous chunk's `etag`.

The full read behaviour (pagination, encodings, PDF profile, bounds) is
described in [docs/development.md](docs/development.md).

## Limits and risks

The write tools exist only in `write` and `share` modes. Read
[SECURITY.md](SECURITY.md) for the full security boundary.

Tree writes (create, rename, move, trash, restore):

- This server never requests permanent deletion: `kdrive_trash` moves an item
  to the kDrive trash and no route empties the trash or deletes for good.
- `kdrive_restore` returns an item only to its former parent, and only when
  that parent is live and authorized; otherwise the restore is refused. A
  folder restore is refused while deny patterns are configured (the trashed
  tree cannot be enumerated).
- Configured roots cannot be trashed, renamed or moved.
- Received-share writes stay refused, even with `KDRIVE_MCP_SHARED_WRITE=1`.
- A check-then-act window remains (SPEC §10.6): the drive can change between
  the check and the write. If the former parent is trashed between the check
  and a restore, kDrive recreates a folder at the former path and restores into
  it; when that former parent is a configured root, the recreated folder has a
  new ID outside every configured root. If an item is trashed between the check
  and a trash request, the effect of `DELETE /2/drive/{d}/files/{f}` on the
  already trashed item is unmeasured.
- Residual risk R-17-1 (not observed on the measured server): restore
  authorizes the former parent's current path, so an item trashed under a denied
  folder that is renamed or moved out of the deny patterns outside this server
  could be restored if kDrive recomputed the trash `path`; owner acceptance on
  2026-11-10 observed a deletion-time snapshot, so such a restore was denied.
- Residual risk R-17-2: the root protection covers only a configured root
  targeted directly. With nested roots, trashing, renaming or moving an
  authorized folder of an outer root that contains an inner configured root
  also affects that inner root, and with deny patterns the trashed folder
  cannot be restored through this server.

Upload (`kdrive_upload`):

- `overwrite:false` (the default) asks kDrive to refuse an existing file of
  the same name (`conflict=error`). That refusal is observed kDrive behaviour
  (9 of 9 measured samples, checked when the upload completes), not a
  guarantee of this server. Names are case-sensitive.
- `overwrite:true` requires `file_id`, the existing file called `name` in
  `parent_id`; its content is replaced in place under the same ID. An existing
  file whose name is not NFC must be renamed to its NFC name before it can be
  overwritten.
- An `uncertain` upload is never replayed; check the file with a read tool
  before any new attempt.
- Received-share uploads stay refused, even with `KDRIVE_MCP_SHARED_WRITE=1`.
- The 524,288-byte bound applies to the decoded bytes, while the transport
  limit (1 MiB stdio frame, 262,144-byte HTTP body) applies to the
  JSON-encoded request. `text` that JSON escaping enlarges can be refused by
  the transport below 524,288 bytes: over HTTP the request gets HTTP 413; over
  stdio a frame above 1 MiB terminates the server with `STDIO_INPUT_FAILED`.
  An upload already in flight completes and its outcome is recorded in the
  audit log before exit, but its tool result is not delivered.
- Residual risks R-18-1 to R-18-5: the `conflict=error` refusal is not proven
  under every interleaving (R-18-1); between the check and a replace the target
  can change, giving `uncertain`, an undetected deny-pattern rename, or
  `succeeded` for a destination moved or trashed out of the configured roots
  (R-18-2);
  kDrive stores a file named like an existing folder under another name, not
  checked against the deny patterns (R-18-3, accepted); kDrive's own version
  history decides whether replaced content is kept (R-18-4); untested name
  rewriting by kDrive could store a file at an unchecked path, reported
  `uncertain` for upload and folder creation but `succeeded` for rename
  (R-18-5). Details: [SECURITY.md](SECURITY.md) and
  [docs/development.md](docs/development.md#upload).

Deny patterns:

- Patterns match full, case-sensitive paths. `/private/**` covers the folder
  and everything below it; `/private` matches only that exact path.
- Patterns match the exact stored code points. Write the NFC form of a
  non-ASCII pattern to cover names these tools create, and keep (or add) the
  stored form for any existing item whose name is not NFC; replacing a
  working NFD pattern by its NFC form alone would drop the deny for those
  items, reads included.

Audit log: each write gets one JSON line before it is sent and one after; if
the first cannot be written, nothing is sent. Records hold IDs, never names,
paths, content or secrets. With `stderr`, records are only as durable as what
collects stderr; use a file for a durable trail.

## More documentation

- [SECURITY.md](SECURITY.md): security boundary and how to report a
  vulnerability.
- [Configuration reference](features/F-002-configuration.md),
  [deployment guide](docs/deployment.md), [OAuth proxy notes](docs/oauth-proxy.md).
- [Publishing](docs/publishing.md): package and image names, versions and the
  release gate.
- [Development notes](docs/development.md): building, checks, quality gates and
  feature history, until the developer documentation exists.
- Contributors follow [AGENTS.md](AGENTS.md) and [PROJECT.md](PROJECT.md).
  This project is developed with AI coding agents under the maintainer's
  direction and review; see AGENTS.md.

## License

Original project code uses the [MIT license](LICENSE). [NOTICE](NOTICE) records
the upstream comparison baseline and dependency licensing.