diff --git a/AGENTS.md b/AGENTS.md index 7e6d521..b872e50 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,8 +37,9 @@ or plain text. - If the server rejects a file format, say so and list the file. Do not convert files to another format to force them through unless the user asks. -- On an auth error, tell the user to re-authenticate with - `/mcp auth transform`. Do not try to work around it. +- On an auth error, tell the user to re-authenticate with their MCP + client (for the Gemini CLI extension, restarting the session re-runs + the browser sign-in). Do not try to work around it. ## Boundaries diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5c35d9a..db0e101 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,6 +17,30 @@ requests are reviewed by Unstructured maintainers. pull request. - Run `./scripts/validate.sh` before opening a pull request. +## Transport + +The manifest launches the server through `npx -y mcp-remote ` rather +than pointing Gemini CLI's native `httpUrl` remote transport at the +server directly. The Transform server authenticates with OAuth via +Dynamic Client Registration, and Gemini CLI's native remote-OAuth path +does not attach the acquired token on the request that follows sign-in +for DCR / auto-discovered servers, so the connection fails to establish +([google-gemini/gemini-cli#27745](https://github.com/google-gemini/gemini-cli/issues/27745)). +`mcp-remote` performs the OAuth flow correctly and caches the token, so +sign-in is a one-time browser step. The cost is a Node.js 18+ / `npx` +runtime requirement for users. + +When the upstream bug is fixed and released, this can move back to the +simpler native transport: + +```json +"transform": { "httpUrl": "https://mcp.transform.unstructured.io" } +``` + +That change also needs `scripts/validate.sh` reverted to read the URL +from `.mcpServers.transform.httpUrl` and the README install steps updated +back to `/mcp auth transform`. + ## Shipping updates This extension is installed from its GitHub URL diff --git a/README.md b/README.md index 2f25d6b..4f6d240 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,16 @@ manifest and the agent guidance the extension ships. gemini extensions install https://github.com/Unstructured-IO/transform-gemini-extension ``` -Then sign in once with `/mcp auth transform` (browser OAuth; no API key -needed). The extension registers the `transform` server and ships the -agent guidance in [`AGENTS.md`](AGENTS.md). +The first time the `transform` server starts, a browser window opens for +a one-time Unstructured sign-in (OAuth; no API key needed). The token is +cached, so you only sign in once. The extension ships the agent guidance +in [`AGENTS.md`](AGENTS.md). + +The server is launched through [`mcp-remote`](https://www.npmjs.com/package/mcp-remote), +so you need Node.js 18+ with `npx` on your PATH. `mcp-remote` is fetched +on first use. It handles the OAuth flow that Gemini CLI's native remote +transport does not yet complete reliably; see +[CONTRIBUTING.md](CONTRIBUTING.md#transport) for the detail. Using another tool (Claude Code, Claude Desktop, Cursor, Codex, Google Antigravity, Cline)? See the diff --git a/gemini-extension.json b/gemini-extension.json index 2f3f1e8..d18f240 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -1,11 +1,12 @@ { "name": "unstructured-transform", - "version": "0.1.0", + "version": "0.2.0", "description": "Parse documents (PDF, DOCX, PPTX, XLSX, HTML, EML, images, and multiple other formats) into markdown, element JSON, HTML, or plain text with the Unstructured Transform MCP server.", "contextFileName": "AGENTS.md", "mcpServers": { "transform": { - "httpUrl": "https://mcp.transform.unstructured.io" + "command": "npx", + "args": ["-y", "mcp-remote", "https://mcp.transform.unstructured.io"] } } } diff --git a/scripts/validate.sh b/scripts/validate.sh index 3597c30..7628278 100755 --- a/scripts/validate.sh +++ b/scripts/validate.sh @@ -29,16 +29,17 @@ else echo "SKIP: no JSON validator found for gemini-extension.json (install jq)" >&2 fi -# Extract the canonical server URL from the manifest. Parse the JSON -# where a parser is available; only fall back to text scanning when -# neither jq nor python3 is present. +# Extract the canonical server URL from the manifest. The server is +# launched through `npx mcp-remote `, so the URL is the https entry +# in the transform server's args. Parse the JSON where a parser is +# available; only fall back to text scanning when neither jq nor python3 +# is present. if command -v jq >/dev/null 2>&1; then - canonical=$(jq -r '.mcpServers.transform.httpUrl // empty' gemini-extension.json 2>/dev/null || true) + canonical=$(jq -r '.mcpServers.transform.args[]? | select(startswith("https://"))' gemini-extension.json 2>/dev/null | head -1 || true) elif have_python; then - canonical=$(python3 -c 'import json; print(json.load(open("gemini-extension.json")).get("mcpServers", {}).get("transform", {}).get("httpUrl", ""))' 2>/dev/null || true) + canonical=$(python3 -c 'import json; args = json.load(open("gemini-extension.json")).get("mcpServers", {}).get("transform", {}).get("args", []); print(next((a for a in args if isinstance(a, str) and a.startswith("https://")), ""))' 2>/dev/null || true) else - canonical=$(grep -oE '"httpUrl"[[:space:]]*:[[:space:]]*"https://[^"]+"' gemini-extension.json \ - | grep -oE 'https://[^"]+' | head -1 || true) + canonical=$(grep -oE 'https://mcp\.[A-Za-z0-9./-]*[A-Za-z0-9/-]' gemini-extension.json | head -1 || true) fi if [ -z "$canonical" ]; then