From c74c42a87753be751b7a5f9516b49fe3f7f8bcd3 Mon Sep 17 00:00:00 2001 From: Oli <7861086+olihou@users.noreply.github.com> Date: Sat, 17 Oct 2026 12:13:36 +0300 Subject: [PATCH] Ask Codex for the service's own OAuth scopes Without scopes in .mcp.json, Codex requests every scope the authorization server lists. Hosted grants them, and the service's token exchange then refuses the grant, so every Nutrient tool call failed in Codex. The plugin now asks for mcp:invoke and offline_access, the scope Claude Code takes from the service's sign-in challenge, and ci/check.py keeps it. The README names each client's sign-in step, and the version is 1.0.1 so existing installs pick up the change. Co-Authored-By: Claude Opus 5.5 --- .claude-plugin/marketplace.json | 2 +- AGENTS.md | 4 ++++ ci/check.py | 3 +++ plugins/nutrient/.claude-plugin/plugin.json | 2 +- plugins/nutrient/.codex-plugin/plugin.json | 2 +- plugins/nutrient/.mcp.json | 6 +++++- plugins/nutrient/README.md | 9 ++++++--- 7 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index aa11d5a..f4fdab5 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ "name": "nutrient", "source": "./plugins/nutrient", "description": "Parse, extract from and classify documents with Nutrient DWS: OCR for scans, Markdown with layout, structured fields with citations, and labels, as stored runs in your Nutrient account.", - "version": "1.0.0", + "version": "1.0.1", "author": { "name": "Nutrient", "url": "https://www.nutrient.io" }, "homepage": "https://www.nutrient.io/api/", "license": "MIT" diff --git a/AGENTS.md b/AGENTS.md index 06b34b4..655931b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,6 +22,10 @@ OpenAI's directory takes a ZIP of `plugins/nutrient`, uploaded for each release. Never change `name` in `plugin.json`: it is the plugin's permanent identity. +Keep `scopes` in `.mcp.json`. Without it, Codex asks for every scope the authorization server lists instead of the +service's own, and the service can't exchange that grant, so every tool call fails. Claude Code asks for the same pair, +taken from the service's sign-in challenge, and ChatGPT for the scopes the service advertises. `ci/check.py` checks it. + ## Refreshing the tool snapshot `ci/tools.json` and `ci/tools-chatgpt.json` are the catalogs Hosted MCP (`hosted-mcp/` in the private Nutrient monorepo) diff --git a/ci/check.py b/ci/check.py index 2f8dc9a..14c6283 100644 --- a/ci/check.py +++ b/ci/check.py @@ -133,6 +133,9 @@ def collect(node, names): servers = load(PLUGIN / ".mcp.json").get("mcpServers", {}) check(list(servers) == ["nutrient"] and servers["nutrient"].get("type") == "http" and servers["nutrient"].get("url") == SERVICE, f".mcp.json declares one server, nutrient, at {SERVICE} over HTTP (OpenAI connects one server per plugin)") +# Without scopes, Codex asks for every scope the authorization server lists, and the service can't use that grant. +check(servers.get("nutrient", {}).get("scopes") == ["mcp:invoke", "offline_access"], + ".mcp.json requests the service's own scopes, mcp:invoke and offline_access") skills = sorted((PLUGIN / "skills").glob("*/SKILL.md")) check(len(skills) > 0, "the plugin has at least one skill") diff --git a/plugins/nutrient/.claude-plugin/plugin.json b/plugins/nutrient/.claude-plugin/plugin.json index c770367..fa6d118 100644 --- a/plugins/nutrient/.claude-plugin/plugin.json +++ b/plugins/nutrient/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "nutrient", "displayName": "Nutrient", - "version": "1.0.0", + "version": "1.0.1", "description": "Parse, extract from and classify documents with Nutrient DWS: OCR for scans, Markdown with layout, structured fields with citations, and labels, as stored runs in your Nutrient account.", "author": { "name": "Nutrient", "url": "https://www.nutrient.io" }, "homepage": "https://www.nutrient.io/api/", diff --git a/plugins/nutrient/.codex-plugin/plugin.json b/plugins/nutrient/.codex-plugin/plugin.json index fd62fd7..2b81140 100644 --- a/plugins/nutrient/.codex-plugin/plugin.json +++ b/plugins/nutrient/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "nutrient", - "version": "1.0.0", + "version": "1.0.1", "description": "Parse, extract from and classify documents with Nutrient DWS: OCR for scans, Markdown with layout, structured fields with citations, and labels, as stored runs in your Nutrient account.", "author": { "name": "Nutrient", diff --git a/plugins/nutrient/.mcp.json b/plugins/nutrient/.mcp.json index 3b633ab..21d51ed 100644 --- a/plugins/nutrient/.mcp.json +++ b/plugins/nutrient/.mcp.json @@ -2,7 +2,11 @@ "mcpServers": { "nutrient": { "type": "http", - "url": "https://mcp.nutrient.io/mcp" + "url": "https://mcp.nutrient.io/mcp", + "scopes": [ + "mcp:invoke", + "offline_access" + ] } } } diff --git a/plugins/nutrient/README.md b/plugins/nutrient/README.md index fd26616..e080e5d 100644 --- a/plugins/nutrient/README.md +++ b/plugins/nutrient/README.md @@ -7,9 +7,12 @@ file is attached to the chat. ## Use it -1. Add the plugin, then connect the Nutrient connector and sign in with your Nutrient account. In Claude Code, install it - with `/plugin install nutrient --marketplace PSPDFKit-labs/nutrient-mcp-plugins`; in Codex, with - `codex plugin marketplace add PSPDFKit-labs/nutrient-mcp-plugins` and `codex plugin add nutrient@nutrient`. +1. Add the plugin, then connect the Nutrient connector and sign in with your Nutrient account. The tools appear once + you're signed in. + - In Claude Code, install it with `/plugin install nutrient --marketplace PSPDFKit-labs/nutrient-mcp-plugins`, then + run `/mcp`, choose `plugin:nutrient:nutrient` and sign in. + - In Codex, install it with `codex plugin marketplace add PSPDFKit-labs/nutrient-mcp-plugins` and + `codex plugin add nutrient@nutrient`, sign in with `codex mcp login nutrient`, then start a new session. 2. Attach a PDF, Office document or image, point to a file on disk or an HTTPS link, and ask to parse it, convert it to Markdown, extract fields or tables, or classify it. 3. The assistant sends the file to your Nutrient account and returns the result: Markdown with layout, structured fields