You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add OAuth browser flow with DCR for stdio transport (#22)
## Summary
- Introduces `DwsApiClient` abstraction with pluggable token provider (API key or OAuth)
- Adds OAuth browser flow module (`src/auth/nutrient-oauth.ts`) with PKCE, token caching (`~/.nutrient/credentials.json`), refresh, and Dynamic Client Registration
- When no `NUTRIENT_DWS_API_KEY` is set, the server opens a browser for Nutrient OAuth consent on first tool call (similar to `gh auth login`)
- DCR registers a client automatically at `{AUTH_SERVER_URL}/oauth/register` — no pre-registered client ID needed
- Adds Winston logger with file transport for stdio mode (avoids interfering with MCP protocol on stdout/stderr)
- Adds Github actions setup to run lint and basic tests
## Auth flow
1. If `NUTRIENT_DWS_API_KEY` is set, use the API key directly
2. Otherwise, check for a cached token at `~/.nutrient/credentials.json`
3. If the cached token is valid, return it
4. If expired but a refresh token exists, refresh it
5. If no token or refresh fails, register a client via DCR and start a browser OAuth flow, then cache the token
## Environment variables
| Variable | Default | Description |
|----------|---------|-------------|
| `NUTRIENT_DWS_API_KEY` | — | API key (skips OAuth if set) |
| `AUTH_SERVER_URL` | `https://api.nutrient.io` | OAuth server base URL |
| `CLIENT_ID` | — | Override DCR with a specific client ID |
| `DWS_API_BASE_URL` | `https://api.nutrient.io` | DWS API base URL |
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Relative paths resolve relative to the sandbox directory
216
221
- All input file paths are validated to ensure they reside in the sandbox
217
222
- Processed files are saved within the sandbox
@@ -222,25 +227,80 @@ When sandbox mode is enabled:
222
227
223
228
Processed files are saved to a location determined by the AI. To guide output placement, use natural language (e.g., "save the result to `output/result.pdf`") or create an `output` directory in your sandbox.
224
229
230
+
### Authentication
231
+
232
+
The server authenticates to the Nutrient DWS API (`https://api.nutrient.io`) using one of:
233
+
234
+
| Method | When | Config |
235
+
|--------|------|--------|
236
+
|**API key**|`NUTRIENT_DWS_API_KEY` is set | Static key passed as Bearer token to DWS API |
237
+
|**OAuth browser flow**| No API key set | Opens browser for Nutrient OAuth consent, caches token locally |
238
+
239
+
When no API key is configured, the server opens a browser-based OAuth flow on the first tool call (similar to `gh auth login`). Tokens are cached at `~/.nutrient/credentials.json` and refreshed automatically.
240
+
225
241
### Environment Variables
226
242
227
-
| Variable | Required | Description |
228
-
|----------|----------|-------------|
229
-
|`NUTRIENT_DWS_API_KEY`| Yes | Your Nutrient DWS API key ([get one free](https://dashboard.nutrient.io/sign_up/)) |
230
-
|`SANDBOX_PATH`| Recommended | Directory to restrict file operations to |
|`NUTRIENT_DWS_API_KEY`| No*| Nutrient DWS API key ([get one free](https://dashboard.nutrient.io/sign_up/)) |
246
+
|`SANDBOX_PATH`| Recommended | Directory to restrict file operations to |
247
+
|`CLIENT_ID`| No | OAuth client ID. Skips DCR and enables token refresh when set |
248
+
|`DWS_API_BASE_URL`| No | DWS API base URL (default: `https://api.nutrient.io`) |
249
+
|`LOG_LEVEL`| No | Winston logger level (`info` default). Logs are written to `MCP_LOG_FILE` in stdio mode |
250
+
|`MCP_LOG_FILE`| No | Override log file path (default: system temp directory) |
251
+
252
+
\* If omitted, the server uses an OAuth browser flow to authenticate with the Nutrient API.
253
+
254
+
### Security Note: Token Storage
255
+
256
+
When using the OAuth browser flow, access tokens and refresh tokens are cached in plaintext at `~/.nutrient/credentials.json` (permissions `0600`). This file contains credentials equivalent to your API key. Do not commit it to version control or include it in shared backups.
231
257
232
258
## Troubleshooting
233
259
260
+
### Reset authentication to a clean state
261
+
262
+
If OAuth authentication stops working, delete the cached token file to start fresh:
263
+
264
+
```bash
265
+
rm ~/.nutrient/credentials.json
266
+
```
267
+
268
+
The server will automatically register a new client and open the browser for consent on the next tool call.
269
+
270
+
### FAQ
271
+
234
272
**Server not appearing in Claude Desktop?**
273
+
235
274
- Ensure Node.js 18+ is installed (`node --version`)
236
275
- Check the config file path is correct for your OS
237
276
- Restart Claude Desktop completely (check Task Manager/Activity Monitor)
238
277
278
+
**Browser doesn't open for OAuth login?**
279
+
280
+
- This happens in headless or remote environments (SSH, Docker, CI). Set `NUTRIENT_DWS_API_KEY` instead — the server skips the browser flow when an API key is configured.
281
+
- On macOS, ensure a default browser is set in System Settings → Desktop & Dock → Default web browser.
282
+
283
+
**"Token exchange failed" or "OAuth authorization failed"?**
284
+
285
+
- Delete `~/.nutrient/credentials.json` and try again.
286
+
- If using a custom `AUTH_SERVER_URL`, verify the server is reachable and its `/oauth/token` endpoint is working.
287
+
288
+
**"Dynamic client registration failed"?**
289
+
290
+
- If using a custom `AUTH_SERVER_URL`, verify it is reachable.
291
+
- Ensure the custom auth server supports RFC 7591 Dynamic Client Registration at its `/oauth/register` endpoint.
292
+
239
293
**"API key invalid" errors?**
294
+
240
295
- Verify your API key at [dashboard.nutrient.io](https://dashboard.nutrient.io)
241
296
- Ensure the key is set correctly in the `env` section (no extra spaces)
242
297
298
+
**Token expired but refresh fails?**
299
+
300
+
- The server automatically refreshes expired tokens using the cached refresh token. If refresh fails (e.g., the refresh token was revoked), delete `~/.nutrient/credentials.json` — the server will re-authenticate via the browser on the next call.
301
+
243
302
**Files not found?**
303
+
244
304
- Check that `SANDBOX_PATH` points to an existing directory
245
305
- Ensure your documents are inside the sandbox directory
246
306
- Use the `sandbox_file_tree` tool to verify visible files
This guide covers local testing against both production DWS (`api.nutrient.io`) and local DWS debug builds. Docker/deployment steps are intentionally omitted.
4
+
5
+
## Prerequisites
6
+
7
+
- Node.js 18+
8
+
- pnpm
9
+
- Project dependencies installed:
10
+
11
+
```bash
12
+
pnpm install
13
+
```
14
+
15
+
## Run Commands
16
+
17
+
```bash
18
+
pnpm run build && pnpm start
19
+
```
20
+
21
+
---
22
+
23
+
## stdio Transport
24
+
25
+
### With API key
26
+
27
+
```bash
28
+
export NUTRIENT_DWS_API_KEY=your_dws_api_key
29
+
pnpm run build && pnpm start
30
+
```
31
+
32
+
### With OAuth browser flow
33
+
34
+
When no API key is set, the server opens a browser for Nutrient OAuth consent on the first tool call. Tokens are cached at `~/.nutrient/credentials.json`.
35
+
36
+
```bash
37
+
pnpm run build && pnpm start
38
+
```
39
+
40
+
To test against a local DWS auth server instead of production:
41
+
42
+
```bash
43
+
export AUTH_SERVER_URL=http://localhost:4000
44
+
export DWS_API_BASE_URL=http://localhost:4000
45
+
pnpm run build && pnpm start
46
+
```
47
+
48
+
The OAuth flow will use `{AUTH_SERVER_URL}/oauth/authorize` and `{AUTH_SERVER_URL}/oauth/token`. The `CLIENT_ID` env var can override the default client ID (`nutrient-dws-mcp-server`).
0 commit comments