@cueai/omni-reader-mcp 1.5.2 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,7 +64,7 @@ differ from the numbers above, report the live values.
64
64
  Always use an audited exact version, never an implicit `latest`:
65
65
 
66
66
  ```sh
67
- npx -y @cueai/omni-reader-mcp@1.5.2 setup
67
+ npx -y @cueai/omni-reader-mcp@1.5.3 setup
68
68
  ```
69
69
 
70
70
  The interactive setup supports Hermes, Cursor, Claude Desktop, and generic stdio
@@ -72,9 +72,9 @@ configuration. Non-interactive installation uses the same argument parsing and w
72
72
  logic:
73
73
 
74
74
  ```sh
75
- npx -y @cueai/omni-reader-mcp@1.5.2 setup --client hermes --allowed-root /absolute/minimum/root --yes --json
76
- npx -y @cueai/omni-reader-mcp@1.5.2 setup --client cursor --add-root /absolute/minimum/root --yes --json
77
- npx -y @cueai/omni-reader-mcp@1.5.2 setup --client claude-desktop --allowed-root /absolute/minimum/root --yes --json
75
+ npx -y @cueai/omni-reader-mcp@1.5.3 setup --client hermes --allowed-root /absolute/minimum/root --yes --json
76
+ npx -y @cueai/omni-reader-mcp@1.5.3 setup --client cursor --add-root /absolute/minimum/root --yes --json
77
+ npx -y @cueai/omni-reader-mcp@1.5.3 setup --client claude-desktop --allowed-root /absolute/minimum/root --yes --json
78
78
  ```
79
79
 
80
80
  When an agent or script runs under a pty (stdin is still a TTY), declare non-interactive
@@ -82,7 +82,7 @@ mode explicitly with `--headless` (alias `--non-interactive`): no `--yes` is req
82
82
  stdin is never read:
83
83
 
84
84
  ```sh
85
- npx -y @cueai/omni-reader-mcp@1.5.2 setup --client cursor --allowed-root /absolute/minimum/root --headless --json
85
+ npx -y @cueai/omni-reader-mcp@1.5.3 setup --client cursor --allowed-root /absolute/minimum/root --headless --json
86
86
  ```
87
87
 
88
88
  ## Windows configuration
@@ -101,14 +101,14 @@ automatically; the second is the most robust when you configure by hand.
101
101
  1. **`cmd` + `npx` (what `setup` writes on Windows)** — `cmd.exe` is a real executable,
102
102
  so a client's no-shell spawn works:
103
103
  ```json
104
- { "command": "cmd", "args": ["/d", "/c", "npx", "-y", "@cueai/omni-reader-mcp@1.5.2"] }
104
+ { "command": "cmd", "args": ["/d", "/c", "npx", "-y", "@cueai/omni-reader-mcp@1.5.3"] }
105
105
  ```
106
106
  2. **`node` + the package entry (most robust)** — skips `npx` entirely; the absolute
107
107
  path must be a stable install you control:
108
108
  ```json
109
109
  { "command": "node", "args": ["C:\\path\\to\\node_modules\\@cueai\\omni-reader-mcp\\dist\\index.js"] }
110
110
  ```
111
- A deliberate local `npm install @cueai/omni-reader-mcp@1.5.2` gives you that stable
111
+ A deliberate local `npm install @cueai/omni-reader-mcp@1.5.3` gives you that stable
112
112
  path. Do not point the entry at an `_npx` cache directory — its hash subdirectory
113
113
  changes between installs.
114
114
  3. **Never use a session/timestamp directory** — such paths are cleaned up, and the
@@ -251,16 +251,16 @@ satisfies the constraints.
251
251
  ## Commands
252
252
 
253
253
  ```sh
254
- npx -y @cueai/omni-reader-mcp@1.5.2 doctor
255
- npx -y @cueai/omni-reader-mcp@1.5.2 doctor --json
256
- npx -y @cueai/omni-reader-mcp@1.5.2 clean
257
- npx -y @cueai/omni-reader-mcp@1.5.2 uninstall --yes --json
254
+ npx -y @cueai/omni-reader-mcp@1.5.3 doctor
255
+ npx -y @cueai/omni-reader-mcp@1.5.3 doctor --json
256
+ npx -y @cueai/omni-reader-mcp@1.5.3 clean
257
+ npx -y @cueai/omni-reader-mcp@1.5.3 uninstall --yes --json
258
258
  ```
259
259
 
260
260
  Running the pinned version without a command starts the stdio MCP server:
261
261
 
262
262
  ```sh
263
- npx -y @cueai/omni-reader-mcp@1.5.2
263
+ npx -y @cueai/omni-reader-mcp@1.5.3
264
264
  ```
265
265
 
266
266
  `doctor --json` returns package/npm/client adapter, Key present/absent, allowed-root
@@ -269,29 +269,36 @@ status; it never prints the Key, private source paths, or content.
269
269
 
270
270
  ## Network diagnostics
271
271
 
272
- Bridge connects to the Omni parse API at `https://cubefile.ai.iiis.co:9443`. When a
273
- connection fails, run `doctor` first — its endpoint-compatibility check covers the
274
- API reachability from your machine. The common symptoms:
272
+ Run `npx -y @cueai/omni-reader-mcp@1.5.3 doctor --json` first. Diagnose failures by
273
+ stage instead of guessing an endpoint:
275
274
 
276
- | Symptom | Most likely cause | Check |
275
+ | Code | Stage | Meaning |
277
276
  |---|---|---|
278
- | `MCP error -32000: Connection closed` on Windows | launcher shape (see Windows configuration) | `doctor` package/client rows |
279
- | `CUBE_PROTOCOL_ERROR` or connection refused/timeout on connect | the hostname resolves to a private address on your network, or the route cannot reach the public endpoint | `nslookup cubefile.ai.iiis.co` the public record is `122.200.68.28` |
280
- | Works in the browser / on another machine but not this one | a local hosts-file override hides a DNS or route problem | check `/etc/hosts` (or `C:\Windows\System32\drivers\etc\hosts`) for a `cubefile.ai.iiis.co` line; test `curl --noproxy '*' -w '%{remote_ip}\n' https://cubefile.ai.iiis.co:9443/omni/health` |
281
-
282
- If the API is reachable but a specific public URL fails to parse (`parse_timeout` /
283
- parse failed) while it works in your browser, that is a fetch-layer condition of the
284
- source on the parse API side not a Bridge configuration problem and it affects the
285
- URL the same way from every client.
277
+ | `CUBE_UNAVAILABLE` | before upload | the control plane could not create a parse grant |
278
+ | `PRINCIPAL_CONCURRENCY_LIMIT` | before upload | the principal already has the maximum active parses; wait for `retry_after`, then retry the same parse |
279
+ | `IIIS_UNAVAILABLE` | after grant creation | the granted upload stage could not complete |
280
+ | `CUBE_PROTOCOL_ERROR` | control-plane response | the response did not match the required contract |
281
+ | `MCP error -32000: Connection closed` | before Bridge starts | the client launcher shape is invalid; see Windows configuration |
282
+
283
+ Use only the compatibility and endpoint facts reported by `doctor`. Do not guess or
284
+ publish service hosts or ports, hard-code IP addresses, modify hosts files, or probe an
285
+ endpoint copied from logs. If an enterprise network policy blocks a required connection,
286
+ provide the redacted `doctor --json` result to support for the applicable egress
287
+ requirements.
288
+
289
+ If the secure parse environment is reachable but a specific public URL fails to parse
290
+ (`parse_timeout` / parse failed) while it works in a browser, that is a fetch-layer
291
+ condition of the source — not a Bridge configuration problem — and it affects the URL
292
+ the same way from every client.
286
293
 
287
294
  ## Uninstall and rollback
288
295
 
289
- `uninstall --yes --json` removes only a trusted 1.5.1 or 1.5.2 Bridge entry; when a
290
- matching trusted backup exists, it restores the original URL-only `omni-reader` entry.
291
- Uninstall never deletes user source files and never silently removes unexpired local
292
- results.
296
+ `uninstall --yes --json` removes only a trusted 1.5.2 or 1.5.3 Bridge entry, plus
297
+ the exact broken bare-`npx` Windows entry written by 1.5.1. When a matching trusted
298
+ backup exists, it restores the original URL-only `omni-reader` entry. Uninstall never
299
+ deletes user source files and never silently removes unexpired local results.
293
300
 
294
- To roll back from 1.5.2:
301
+ To roll back from 1.5.3:
295
302
 
296
303
  1. stop recommending or installing that version;
297
304
  2. run `uninstall --yes --json` to restore the trusted URL-only entry;
@@ -4,11 +4,16 @@ import { chmod, lstat, mkdir, open, realpath, rename, unlink, } from "node:fs/pr
4
4
  import path from "node:path";
5
5
  import { BRIDGE_RELEASE_VERSION, REMOTE_OMNI_MCP_URL, } from "../constants.js";
6
6
  const PACKAGE_SPEC = `@cueai/omni-reader-mcp@${BRIDGE_RELEASE_VERSION}`;
7
- // The release that the current version is a trusted upgrade from: uninstall and
8
- // rollback recognize exactly {previous, current} and nothing else. Bump this to
9
- // the version we just published when BRIDGE_RELEASE_VERSION advances.
10
- const PREVIOUS_RELEASE_VERSION = "1.5.1";
7
+ // The release that the current version is a trusted upgrade from: normal upgrade,
8
+ // uninstall, and rollback recognize exactly {previous, current}. Bump this to the
9
+ // version we just published when BRIDGE_RELEASE_VERSION advances.
10
+ const PREVIOUS_RELEASE_VERSION = "1.5.2";
11
11
  const PREVIOUS_PACKAGE_SPEC = `@cueai/omni-reader-mcp@${PREVIOUS_RELEASE_VERSION}`;
12
+ // Preserve the one evidenced two-release migration exception: 1.5.1 setup wrote a
13
+ // bare `npx` launcher on Windows, so later setup/uninstall versions must still be able
14
+ // to identify and replace that otherwise-unrunnable entry. 1.5.2 already writes `cmd`.
15
+ const WINDOWS_BARE_NPX_MIGRATION_VERSION = "1.5.1";
16
+ const WINDOWS_BARE_NPX_MIGRATION_SPEC = `@cueai/omni-reader-mcp@${WINDOWS_BARE_NPX_MIGRATION_VERSION}`;
12
17
  const LEGACY_PACKAGE_SPEC = "@cueai/omni-reader-mcp";
13
18
  const TRUSTED_EXACT_PACKAGE_SPECS = new Set([PREVIOUS_PACKAGE_SPEC, PACKAGE_SPEC]);
14
19
  function isRecord(value) {
@@ -146,10 +151,9 @@ function entryPackageSpec(entry, platform) {
146
151
  /**
147
152
  * Matches the trusted invocation shape for the platform: `cmd /d /c npx -y <spec>` on
148
153
  * Windows (runnable by a no-shell spawn; `/d` skips the AutoRun command whose stdout
149
- * would corrupt MCP stdio frames), `npx -y <spec>` everywhere else. On Windows the
150
- * previous release's bare-`npx` shape is additionally recognized as the migration
151
- * source 1.5.1's setup wrote it there and it can never run, but upgrade and
152
- * uninstall must still identify it as a trusted entry so 1.5.2 can replace it.
154
+ * would corrupt MCP stdio frames), `npx -y <spec>` everywhere else. Windows also keeps
155
+ * the exact 1.5.1 bare-`npx` migration source: that release wrote an unrunnable entry,
156
+ * while 1.5.2 and later write `cmd` and therefore need no widening exception.
153
157
  */
154
158
  function entryInvocationMatches(entry, platform) {
155
159
  const args = entry.args;
@@ -163,12 +167,12 @@ function entryInvocationMatches(entry, platform) {
163
167
  && args[3] === "-y"
164
168
  && typeof args[4] === "string"
165
169
  && TRUSTED_EXACT_PACKAGE_SPECS.has(args[4]);
166
- const previousNpxForm = entry.command === "npx"
170
+ const migrationNpxForm = entry.command === "npx"
167
171
  && Array.isArray(args)
168
172
  && args.length === 2
169
173
  && args[0] === "-y"
170
- && args[1] === PREVIOUS_PACKAGE_SPEC;
171
- return cmdForm || previousNpxForm;
174
+ && args[1] === WINDOWS_BARE_NPX_MIGRATION_SPEC;
175
+ return cmdForm || migrationNpxForm;
172
176
  }
173
177
  return entry.command === "npx"
174
178
  && Array.isArray(args)
@@ -629,25 +633,27 @@ export function expectedBridgeVersion(target, value, platform) {
629
633
  if (!isExpectedOmniEntry(target, value, platform))
630
634
  return undefined;
631
635
  const packageSpec = entryPackageSpec(value, platform);
632
- return packageSpec === PREVIOUS_PACKAGE_SPEC ? PREVIOUS_RELEASE_VERSION : BRIDGE_RELEASE_VERSION;
636
+ if (packageSpec === PREVIOUS_PACKAGE_SPEC)
637
+ return PREVIOUS_RELEASE_VERSION;
638
+ if (packageSpec === WINDOWS_BARE_NPX_MIGRATION_SPEC) {
639
+ return WINDOWS_BARE_NPX_MIGRATION_VERSION;
640
+ }
641
+ return BRIDGE_RELEASE_VERSION;
633
642
  }
634
643
  function isLegacyBridgeEntry(target, value, platform) {
635
- // The legacy npx form (what every pre-1.5.2 setup wrote, incl. on Windows) plus, on
636
- // Windows, the current cmd form both are recognizable Bridge entries.
637
- const legacyNpx = (entry) => {
644
+ // Keep the historical unpinned package form recognizable for setup migration;
645
+ // every exact-version form delegates to the single platform policy above.
646
+ const legacyUnpinnedNpx = (entry) => {
638
647
  const args = entry.args;
639
648
  return entry.command === "npx"
640
649
  && Array.isArray(args)
641
650
  && args.length === 2
642
651
  && args[0] === "-y"
643
- && typeof args[1] === "string"
644
- && (args[1] === LEGACY_PACKAGE_SPEC || TRUSTED_EXACT_PACKAGE_SPECS.has(args[1]));
652
+ && args[1] === LEGACY_PACKAGE_SPEC;
645
653
  };
646
654
  if (!isRecord(value))
647
655
  return false;
648
- const shapeOk = platform === "win32"
649
- ? (legacyNpx(value) || entryInvocationMatches(value, platform))
650
- : legacyNpx(value);
656
+ const shapeOk = legacyUnpinnedNpx(value) || entryInvocationMatches(value, platform);
651
657
  if (!shapeOk)
652
658
  return false;
653
659
  if (value.env === undefined)
@@ -6,7 +6,7 @@ export declare const CUBE_GRANT_PROTOCOL_VERSION = "omni.parse_grant.v1";
6
6
  export declare const GRANTED_STREAM_PROTOCOL_VERSION = "omni.granted_parse_stream.v1";
7
7
  export declare const DEFAULT_CUBE_BASE_URL = "https://mcp.cuecue.cn";
8
8
  export declare const DEFAULT_IIIS_GRANTED_BASE_URL = "https://cubefile.ai.iiis.co:9443/omni/granted/";
9
- export declare const BRIDGE_RELEASE_VERSION = "1.5.2";
9
+ export declare const BRIDGE_RELEASE_VERSION = "1.5.3";
10
10
  export declare const REMOTE_OMNI_MCP_URL = "https://mcp.cuecue.cn/api/omni-reader/mcp/";
11
11
  export declare const FOREGROUND_BUDGET_MS = 15000;
12
12
  export declare const STATUS_LONG_POLL_MAX_MS = 20000;
package/dist/constants.js CHANGED
@@ -6,7 +6,7 @@ export const CUBE_GRANT_PROTOCOL_VERSION = "omni.parse_grant.v1";
6
6
  export const GRANTED_STREAM_PROTOCOL_VERSION = "omni.granted_parse_stream.v1";
7
7
  export const DEFAULT_CUBE_BASE_URL = "https://mcp.cuecue.cn";
8
8
  export const DEFAULT_IIIS_GRANTED_BASE_URL = "https://cubefile.ai.iiis.co:9443/omni/granted/";
9
- export const BRIDGE_RELEASE_VERSION = "1.5.2";
9
+ export const BRIDGE_RELEASE_VERSION = "1.5.3";
10
10
  export const REMOTE_OMNI_MCP_URL = "https://mcp.cuecue.cn/api/omni-reader/mcp/";
11
11
  export const FOREGROUND_BUDGET_MS = 15_000;
12
12
  export const STATUS_LONG_POLL_MAX_MS = 20_000;
@@ -25,6 +25,16 @@ const grantResponseSchema = z
25
25
  protocol_version: z.enum([CUBE_GRANT_PROTOCOL_VERSION, "omni.parse_grant.v2"]),
26
26
  })
27
27
  .strict();
28
+ const principalConcurrencyErrorSchema = z
29
+ .object({
30
+ code: z.literal("PRINCIPAL_CONCURRENCY_LIMIT"),
31
+ message: z.literal("The maximum number of concurrent parsing operations is already active."),
32
+ file_uploaded: z.literal(false),
33
+ billed: z.literal(false),
34
+ content_released: z.literal(false),
35
+ retryable: z.literal(true),
36
+ })
37
+ .strict();
28
38
  // Closed v3 grant response: the exact tuple of the selected direct profile is
29
39
  // required before the upload phase may start. The tuple is re-checked against
30
40
  // the profile after schema parsing so a drifting server cannot slip through.
@@ -47,7 +57,7 @@ const grantResponseV3Schema = z
47
57
  approved_result_max_bytes: z.literal(67108864),
48
58
  })
49
59
  .strict();
50
- function bridgeError(code, message, retryable) {
60
+ function bridgeError(code, message, retryable, options = {}) {
51
61
  return new OmniBridgeError({
52
62
  code,
53
63
  message,
@@ -57,6 +67,9 @@ function bridgeError(code, message, retryable) {
57
67
  billed: false,
58
68
  contentReleased: false,
59
69
  retryable,
70
+ ...(options.failureScope === undefined ? {} : { failureScope: options.failureScope }),
71
+ ...(options.userAction === undefined ? {} : { userAction: options.userAction }),
72
+ ...(options.retryAfter === undefined ? {} : { retryAfter: options.retryAfter }),
60
73
  });
61
74
  }
62
75
  function grantRequestBody(input, profile) {
@@ -137,6 +150,33 @@ function responseError(status) {
137
150
  }
138
151
  return bridgeError("CUBE_UNAVAILABLE", "Cube could not create the parse grant. Retry this same grant request later.", status >= 500);
139
152
  }
153
+ function retryAfterSeconds(response) {
154
+ const value = response.headers.get("retry-after");
155
+ if (value === null || !/^\d+$/u.test(value))
156
+ return undefined;
157
+ const seconds = Number(value);
158
+ return Number.isSafeInteger(seconds) && seconds > 0 ? seconds : undefined;
159
+ }
160
+ async function grantResponseError(response) {
161
+ const retryAfter = retryAfterSeconds(response);
162
+ if (response.status === 429 && retryAfter !== undefined) {
163
+ let body;
164
+ try {
165
+ body = await response.json();
166
+ }
167
+ catch {
168
+ return responseError(response.status);
169
+ }
170
+ if (principalConcurrencyErrorSchema.safeParse(body).success) {
171
+ return bridgeError("PRINCIPAL_CONCURRENCY_LIMIT", "The maximum number of concurrent parsing operations is already active.", true, {
172
+ failureScope: "service",
173
+ userAction: "Wait for retry_after seconds, then retry the same local-file parse.",
174
+ retryAfter,
175
+ });
176
+ }
177
+ }
178
+ return responseError(response.status);
179
+ }
140
180
  export class CubeGrantClient {
141
181
  #journal;
142
182
  #apiKey;
@@ -256,7 +296,7 @@ export class CubeGrantClient {
256
296
  throw bridgeError("CUBE_UNAVAILABLE", "Cube could not create the parse grant. Retry this same grant request later.", true);
257
297
  }
258
298
  if (!response.ok) {
259
- throw responseError(response.status);
299
+ throw await grantResponseError(response);
260
300
  }
261
301
  // The v3 response tuple is required before this promise resolves: the
262
302
  // upload phase can only start after the negotiated representation is
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cueai/omni-reader-mcp",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "Local stdio MCP bridge for direct Omni document parsing",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",