@cueai/omni-reader-mcp 1.0.2 → 1.1.1
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 +115 -26
- package/dist/artifact-store.d.ts +11 -0
- package/dist/artifact-store.js +94 -48
- package/dist/cli/agent-config.d.ts +29 -4
- package/dist/cli/agent-config.js +910 -107
- package/dist/cli/arguments.d.ts +32 -0
- package/dist/cli/arguments.js +120 -0
- package/dist/cli/doctor.d.ts +42 -1
- package/dist/cli/doctor.js +109 -36
- package/dist/cli/setup.d.ts +3 -0
- package/dist/cli/setup.js +103 -18
- package/dist/cli/uninstall.d.ts +6 -0
- package/dist/cli/uninstall.js +37 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +7 -0
- package/dist/cube-client.d.ts +5 -3
- package/dist/cube-client.js +16 -11
- package/dist/cursor.js +2 -0
- package/dist/errors.d.ts +32 -1
- package/dist/errors.js +26 -1
- package/dist/iiis-client.d.ts +18 -4
- package/dist/iiis-client.js +194 -40
- package/dist/index.d.ts +3 -0
- package/dist/index.js +93 -32
- package/dist/multipart-body.js +2 -0
- package/dist/onboarding-policy.d.ts +10 -0
- package/dist/onboarding-policy.js +58 -0
- package/dist/operation-journal.d.ts +50 -1
- package/dist/operation-journal.js +473 -114
- package/dist/operation-manager.d.ts +75 -0
- package/dist/operation-manager.js +1324 -0
- package/dist/path-security.d.ts +1 -0
- package/dist/path-security.js +26 -6
- package/dist/progress.d.ts +6 -1
- package/dist/protocol.d.ts +26 -13
- package/dist/protocol.js +34 -10
- package/dist/remote-client.d.ts +17 -0
- package/dist/remote-client.js +233 -0
- package/dist/result-contract.d.ts +199 -0
- package/dist/result-contract.js +235 -0
- package/dist/server.js +21 -4
- package/dist/source.d.ts +8 -0
- package/dist/source.js +37 -0
- package/dist/task-runtime.d.ts +13 -0
- package/dist/task-runtime.js +94 -0
- package/dist/tools.d.ts +19 -1
- package/dist/tools.js +317 -112
- package/package.json +3 -3
package/dist/path-security.d.ts
CHANGED
package/dist/path-security.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { constants, } from "node:fs";
|
|
2
3
|
import { open as nodeOpen, realpath as nodeRealpath, stat as nodeStat, } from "node:fs/promises";
|
|
3
4
|
import { homedir } from "node:os";
|
|
@@ -41,6 +42,7 @@ const CONTENT_TYPES = {
|
|
|
41
42
|
".html": "text/html",
|
|
42
43
|
".htm": "text/html",
|
|
43
44
|
};
|
|
45
|
+
const SUPPORTED_EXTENSIONS = [...new Set(Object.keys(CONTENT_TYPES).map((extension) => extension.slice(1)))].sort();
|
|
44
46
|
const NODE_FILE_SYSTEM = {
|
|
45
47
|
realpath: (candidate) => nodeRealpath(candidate),
|
|
46
48
|
open: (candidate, flags) => nodeOpen(candidate, flags),
|
|
@@ -50,12 +52,14 @@ class DescriptorBackedFile {
|
|
|
50
52
|
size;
|
|
51
53
|
safeExtension;
|
|
52
54
|
contentType;
|
|
55
|
+
sourceFingerprint;
|
|
53
56
|
#handle;
|
|
54
57
|
#closed = false;
|
|
55
|
-
constructor(handle, size, safeExtension, contentType) {
|
|
58
|
+
constructor(handle, size, safeExtension, contentType, sourceFingerprint) {
|
|
56
59
|
this.size = size;
|
|
57
60
|
this.safeExtension = safeExtension;
|
|
58
61
|
this.contentType = contentType;
|
|
62
|
+
this.sourceFingerprint = sourceFingerprint;
|
|
59
63
|
this.#handle = handle;
|
|
60
64
|
}
|
|
61
65
|
createReadStream() {
|
|
@@ -72,14 +76,20 @@ class DescriptorBackedFile {
|
|
|
72
76
|
await this.#handle.close();
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
|
-
function bridgeError(code, message, retryable) {
|
|
79
|
+
function bridgeError(code, message, retryable, constraints) {
|
|
76
80
|
return new OmniBridgeError({
|
|
77
81
|
code,
|
|
82
|
+
failureScope: "source",
|
|
83
|
+
sourceKind: "local",
|
|
78
84
|
message,
|
|
85
|
+
userAction: "Provide an accessible supported file inside an allowed directory.",
|
|
86
|
+
operationCreated: false,
|
|
79
87
|
fileUploaded: false,
|
|
88
|
+
parserStarted: false,
|
|
80
89
|
billed: false,
|
|
81
90
|
contentReleased: false,
|
|
82
91
|
retryable,
|
|
92
|
+
...(constraints === undefined ? {} : { constraints }),
|
|
83
93
|
});
|
|
84
94
|
}
|
|
85
95
|
function validatePathText(value) {
|
|
@@ -115,7 +125,7 @@ function requireRegularFile(fileStat) {
|
|
|
115
125
|
}
|
|
116
126
|
function requireAllowedSize(fileStat) {
|
|
117
127
|
if (fileStat.size > BigInt(MAX_FILE_BYTES)) {
|
|
118
|
-
throw bridgeError("
|
|
128
|
+
throw bridgeError("SOURCE_TOO_LARGE", "The source exceeds the 256 MiB limit.", false, { max_bytes: MAX_FILE_BYTES });
|
|
119
129
|
}
|
|
120
130
|
return Number(fileStat.size);
|
|
121
131
|
}
|
|
@@ -128,14 +138,24 @@ function requireSameFile(before, after) {
|
|
|
128
138
|
throw bridgeError("FILE_CHANGED_DURING_OPEN", "The local file changed while it was being opened. Retry after file changes stop.", true);
|
|
129
139
|
}
|
|
130
140
|
}
|
|
141
|
+
function redactedSourceFingerprint(fileStat) {
|
|
142
|
+
const identity = [
|
|
143
|
+
fileStat.dev,
|
|
144
|
+
fileStat.ino,
|
|
145
|
+
fileStat.size,
|
|
146
|
+
fileStat.mtimeNs,
|
|
147
|
+
fileStat.ctimeNs,
|
|
148
|
+
].map((value) => value.toString()).join(":");
|
|
149
|
+
return `sha256:${createHash("sha256").update(identity, "utf8").digest("hex")}`;
|
|
150
|
+
}
|
|
131
151
|
function safeFileType(resolvedPath) {
|
|
132
152
|
const safeExtension = path.extname(resolvedPath).toLowerCase();
|
|
133
153
|
if (!/^\.[a-z0-9]{1,10}$/u.test(safeExtension)) {
|
|
134
|
-
throw bridgeError("
|
|
154
|
+
throw bridgeError("UNSUPPORTED_MEDIA_TYPE", "The source media type is not supported.", false, { supported_extensions: SUPPORTED_EXTENSIONS });
|
|
135
155
|
}
|
|
136
156
|
const contentType = CONTENT_TYPES[safeExtension];
|
|
137
157
|
if (contentType === undefined) {
|
|
138
|
-
throw bridgeError("
|
|
158
|
+
throw bridgeError("UNSUPPORTED_MEDIA_TYPE", "The source media type is not supported.", false, { supported_extensions: SUPPORTED_EXTENSIONS });
|
|
139
159
|
}
|
|
140
160
|
return { safeExtension, contentType };
|
|
141
161
|
}
|
|
@@ -229,7 +249,7 @@ export async function openAllowedFile(input, options) {
|
|
|
229
249
|
requireRegularFile(after);
|
|
230
250
|
requireSameFile(before, after);
|
|
231
251
|
const { safeExtension, contentType } = safeFileType(afterPath);
|
|
232
|
-
const opened = new DescriptorBackedFile(handle, size, safeExtension, contentType);
|
|
252
|
+
const opened = new DescriptorBackedFile(handle, size, safeExtension, contentType, redactedSourceFingerprint(before));
|
|
233
253
|
handle = undefined;
|
|
234
254
|
return opened;
|
|
235
255
|
}
|
package/dist/progress.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
export interface ProgressDetail {
|
|
2
|
+
readonly unit: "page" | "sheet" | "slide" | "frame" | "segment";
|
|
3
|
+
readonly completed: number;
|
|
4
|
+
readonly total: number;
|
|
5
|
+
}
|
|
1
6
|
export interface ProgressSink {
|
|
2
|
-
report(progress: number, total: number, message: string): Promise<void>;
|
|
7
|
+
report(progress: number, total: number, message: string, detail?: ProgressDetail): Promise<void>;
|
|
3
8
|
}
|
|
4
9
|
export declare const NOOP_PROGRESS: ProgressSink;
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare const LOCAL_BRIDGE_PROTOCOL_VERSION = "omni.local_bridge_tools.
|
|
3
|
-
export declare const
|
|
4
|
-
export declare const
|
|
5
|
-
|
|
6
|
-
no_store: z.ZodDefault<z.ZodBoolean>;
|
|
7
|
-
output: z.ZodDefault<z.ZodLiteral<"markdown">>;
|
|
2
|
+
export declare const LOCAL_BRIDGE_PROTOCOL_VERSION = "omni.local_bridge_tools.v2";
|
|
3
|
+
export declare const MACHINE_INSTRUCTIONS: string;
|
|
4
|
+
export declare const parseSchema: z.ZodObject<{
|
|
5
|
+
source: z.ZodEffects<z.ZodString, string, string>;
|
|
8
6
|
}, "strict", z.ZodTypeAny, {
|
|
9
|
-
|
|
10
|
-
no_store: boolean;
|
|
11
|
-
output: "markdown";
|
|
7
|
+
source: string;
|
|
12
8
|
}, {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
source: string;
|
|
10
|
+
}>;
|
|
11
|
+
export declare const getParseStatusSchema: z.ZodObject<{
|
|
12
|
+
operation_id: z.ZodString;
|
|
13
|
+
wait_ms: z.ZodOptional<z.ZodNumber>;
|
|
14
|
+
}, "strict", z.ZodTypeAny, {
|
|
15
|
+
operation_id: string;
|
|
16
|
+
wait_ms?: number | undefined;
|
|
17
|
+
}, {
|
|
18
|
+
operation_id: string;
|
|
19
|
+
wait_ms?: number | undefined;
|
|
20
|
+
}>;
|
|
21
|
+
export declare const cancelParseSchema: z.ZodObject<{
|
|
22
|
+
operation_id: z.ZodString;
|
|
23
|
+
}, "strict", z.ZodTypeAny, {
|
|
24
|
+
operation_id: string;
|
|
25
|
+
}, {
|
|
26
|
+
operation_id: string;
|
|
16
27
|
}>;
|
|
17
28
|
export declare const readResultSchema: z.ZodObject<{
|
|
18
29
|
result_id: z.ZodString;
|
|
@@ -34,6 +45,8 @@ export declare const discardResultSchema: z.ZodObject<{
|
|
|
34
45
|
}, {
|
|
35
46
|
result_id: string;
|
|
36
47
|
}>;
|
|
37
|
-
export type
|
|
48
|
+
export type ParseArguments = z.infer<typeof parseSchema>;
|
|
49
|
+
export type GetParseStatusArguments = z.infer<typeof getParseStatusSchema>;
|
|
50
|
+
export type CancelParseArguments = z.infer<typeof cancelParseSchema>;
|
|
38
51
|
export type ReadResultArguments = z.infer<typeof readResultSchema>;
|
|
39
52
|
export type DiscardResultArguments = z.infer<typeof discardResultSchema>;
|
package/dist/protocol.js
CHANGED
|
@@ -1,16 +1,40 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { RESULT_CHUNK_MAX_BYTES } from "./constants.js";
|
|
3
|
-
export const LOCAL_BRIDGE_PROTOCOL_VERSION = "omni.local_bridge_tools.
|
|
4
|
-
export const
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
2
|
+
import { RESULT_CHUNK_MAX_BYTES, STATUS_LONG_POLL_MAX_MS } from "./constants.js";
|
|
3
|
+
export const LOCAL_BRIDGE_PROTOCOL_VERSION = "omni.local_bridge_tools.v2";
|
|
4
|
+
export const MACHINE_INSTRUCTIONS = [
|
|
5
|
+
"Pass the user-provided source string directly to parse.",
|
|
6
|
+
"Treat only HTTP(S) as URL; ordinary paths require the local Bridge.",
|
|
7
|
+
"Never read, attach, base64-encode, or insert local source content before calling Omni.",
|
|
8
|
+
"Never use file://, localhost, or public temporary upload services as fallback.",
|
|
9
|
+
"Ask before installing Bridge or expanding allowed roots.",
|
|
10
|
+
"Never ask the user to paste an API Key into chat.",
|
|
11
|
+
"Retry only when structured state permits; recover an existing operation before resubmitting.",
|
|
12
|
+
"A tool-level error is not an MCP disconnection.",
|
|
13
|
+
"Do not promise background notification when the client lacks task support.",
|
|
14
|
+
"Do not claim deletion before cleanup is confirmed.",
|
|
15
|
+
"Report authoritative unit progress when present; never treat partial output as final.",
|
|
16
|
+
"After parsing, continue the user's original task.",
|
|
17
|
+
].join("\n");
|
|
18
|
+
const operationIdSchema = z.string().regex(/^op_[A-Za-z0-9_-]{16,64}$/u);
|
|
8
19
|
const resultIdSchema = z.string().regex(/^result_[A-Za-z0-9_-]{16,64}$/u);
|
|
9
|
-
export const
|
|
20
|
+
export const parseSchema = z
|
|
10
21
|
.object({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
source: z
|
|
23
|
+
.string()
|
|
24
|
+
.min(1)
|
|
25
|
+
.max(8192)
|
|
26
|
+
.refine((value) => !value.includes("\0")),
|
|
27
|
+
})
|
|
28
|
+
.strict();
|
|
29
|
+
export const getParseStatusSchema = z
|
|
30
|
+
.object({
|
|
31
|
+
operation_id: operationIdSchema,
|
|
32
|
+
wait_ms: z.number().int().min(0).max(STATUS_LONG_POLL_MAX_MS).optional(),
|
|
33
|
+
})
|
|
34
|
+
.strict();
|
|
35
|
+
export const cancelParseSchema = z
|
|
36
|
+
.object({
|
|
37
|
+
operation_id: operationIdSchema,
|
|
14
38
|
})
|
|
15
39
|
.strict();
|
|
16
40
|
export const readResultSchema = z
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ParseResult } from "./result-contract.js";
|
|
2
|
+
export interface RemoteOmniClient {
|
|
3
|
+
parse(source: string, clientRequestId: string, signal: AbortSignal): Promise<ParseResult>;
|
|
4
|
+
status(operationId: string, waitMs: number | undefined, signal: AbortSignal): Promise<ParseResult>;
|
|
5
|
+
cancel(operationId: string, signal: AbortSignal): Promise<ParseResult>;
|
|
6
|
+
}
|
|
7
|
+
export interface HttpRemoteOmniClientOptions {
|
|
8
|
+
readonly apiKey?: string;
|
|
9
|
+
readonly fetchImpl?: typeof fetch;
|
|
10
|
+
}
|
|
11
|
+
export declare class HttpRemoteOmniClient implements RemoteOmniClient {
|
|
12
|
+
#private;
|
|
13
|
+
constructor(options: HttpRemoteOmniClientOptions);
|
|
14
|
+
parse(source: string, clientRequestId: string, signal: AbortSignal): Promise<ParseResult>;
|
|
15
|
+
status(operationId: string, waitMs: number | undefined, signal: AbortSignal): Promise<ParseResult>;
|
|
16
|
+
cancel(operationId: string, signal: AbortSignal): Promise<ParseResult>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { REMOTE_OMNI_MCP_URL } from "./constants.js";
|
|
2
|
+
import { OmniBridgeError } from "./errors.js";
|
|
3
|
+
import { parseResultSchema } from "./result-contract.js";
|
|
4
|
+
import { classifySource } from "./source.js";
|
|
5
|
+
function remoteError(options) {
|
|
6
|
+
return new OmniBridgeError({
|
|
7
|
+
code: options.code,
|
|
8
|
+
failureScope: options.failureScope,
|
|
9
|
+
sourceKind: "url",
|
|
10
|
+
message: options.message,
|
|
11
|
+
userAction: options.userAction,
|
|
12
|
+
operationCreated: options.operationCreated,
|
|
13
|
+
fileUploaded: false,
|
|
14
|
+
parserStarted: false,
|
|
15
|
+
billed: false,
|
|
16
|
+
contentReleased: false,
|
|
17
|
+
retryable: options.retryable,
|
|
18
|
+
...(options.retryAfter === undefined ? {} : { retryAfter: options.retryAfter }),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function retryAfterSeconds(response) {
|
|
22
|
+
const value = response.headers.get("retry-after");
|
|
23
|
+
if (value === null || !/^\d+$/u.test(value))
|
|
24
|
+
return undefined;
|
|
25
|
+
const seconds = Number(value);
|
|
26
|
+
return Number.isSafeInteger(seconds) ? seconds : undefined;
|
|
27
|
+
}
|
|
28
|
+
function responseError(response) {
|
|
29
|
+
const retryAfter = retryAfterSeconds(response);
|
|
30
|
+
if (response.status === 401) {
|
|
31
|
+
return remoteError({
|
|
32
|
+
code: "API_KEY_INVALID",
|
|
33
|
+
message: "The Cue API Key is invalid or expired.",
|
|
34
|
+
failureScope: "authentication",
|
|
35
|
+
userAction: "Configure a valid Cue API Key without pasting it into chat.",
|
|
36
|
+
operationCreated: false,
|
|
37
|
+
retryable: false,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (response.status === 402) {
|
|
41
|
+
return remoteError({
|
|
42
|
+
code: "INSUFFICIENT_CREDITS",
|
|
43
|
+
message: "The Cue account does not have enough credits for this parse.",
|
|
44
|
+
failureScope: "billing",
|
|
45
|
+
userAction: "Review the account balance or free daily allowance before retrying.",
|
|
46
|
+
operationCreated: false,
|
|
47
|
+
retryable: false,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (response.status === 403) {
|
|
51
|
+
return remoteError({
|
|
52
|
+
code: "OMNI_NOT_ENTITLED",
|
|
53
|
+
message: "This Cue account cannot use Omni Reader.",
|
|
54
|
+
failureScope: "authentication",
|
|
55
|
+
userAction: "Use an account that is entitled to Omni Reader.",
|
|
56
|
+
operationCreated: false,
|
|
57
|
+
retryable: false,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (response.status === 429) {
|
|
61
|
+
return remoteError({
|
|
62
|
+
code: "RATE_LIMITED",
|
|
63
|
+
message: "Omni is temporarily rate limited.",
|
|
64
|
+
failureScope: "service",
|
|
65
|
+
userAction: "Retry after the server-provided delay.",
|
|
66
|
+
operationCreated: false,
|
|
67
|
+
retryable: true,
|
|
68
|
+
...(retryAfter === undefined ? {} : { retryAfter }),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (response.status >= 500) {
|
|
72
|
+
return remoteError({
|
|
73
|
+
code: "SERVICE_TEMPORARILY_UNAVAILABLE",
|
|
74
|
+
message: "Omni is temporarily unavailable.",
|
|
75
|
+
failureScope: "service",
|
|
76
|
+
userAction: "Retry with bounded backoff while preserving the same request identity.",
|
|
77
|
+
operationCreated: true,
|
|
78
|
+
retryable: true,
|
|
79
|
+
...(retryAfter === undefined ? {} : { retryAfter }),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return remoteError({
|
|
83
|
+
code: "REMOTE_REQUEST_REJECTED",
|
|
84
|
+
message: "The remote Omni request was rejected.",
|
|
85
|
+
failureScope: "service",
|
|
86
|
+
userAction: "Check the structured request and retry only when permitted.",
|
|
87
|
+
operationCreated: false,
|
|
88
|
+
retryable: false,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function protocolError() {
|
|
92
|
+
return remoteError({
|
|
93
|
+
code: "REMOTE_PROTOCOL_ERROR",
|
|
94
|
+
message: "The remote Omni response did not match the expected contract.",
|
|
95
|
+
failureScope: "service",
|
|
96
|
+
userAction: "Retry later without changing the source or request identity.",
|
|
97
|
+
operationCreated: true,
|
|
98
|
+
retryable: false,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function jsonFromSse(body, requestId) {
|
|
102
|
+
for (const event of body.split(/\r?\n\r?\n/u)) {
|
|
103
|
+
const data = event
|
|
104
|
+
.split(/\r?\n/u)
|
|
105
|
+
.filter((line) => line.startsWith("data:"))
|
|
106
|
+
.map((line) => line.slice(5).trimStart())
|
|
107
|
+
.join("\n");
|
|
108
|
+
if (data.length === 0 || data === "[DONE]")
|
|
109
|
+
continue;
|
|
110
|
+
try {
|
|
111
|
+
const value = JSON.parse(data);
|
|
112
|
+
if (value !== null &&
|
|
113
|
+
typeof value === "object" &&
|
|
114
|
+
!Array.isArray(value) &&
|
|
115
|
+
value.id === requestId) {
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
throw protocolError();
|
|
124
|
+
}
|
|
125
|
+
function decodeEnvelope(value, requestId) {
|
|
126
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
127
|
+
throw protocolError();
|
|
128
|
+
}
|
|
129
|
+
const envelope = value;
|
|
130
|
+
if (envelope.jsonrpc !== "2.0" || envelope.id !== requestId || envelope.error !== undefined) {
|
|
131
|
+
throw protocolError();
|
|
132
|
+
}
|
|
133
|
+
if (envelope.result === null || typeof envelope.result !== "object" || Array.isArray(envelope.result)) {
|
|
134
|
+
throw protocolError();
|
|
135
|
+
}
|
|
136
|
+
const result = envelope.result;
|
|
137
|
+
try {
|
|
138
|
+
return parseResultSchema.parse(result.structuredContent);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
throw protocolError();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export class HttpRemoteOmniClient {
|
|
145
|
+
#apiKey;
|
|
146
|
+
#fetch;
|
|
147
|
+
constructor(options) {
|
|
148
|
+
this.#apiKey = options.apiKey;
|
|
149
|
+
this.#fetch = options.fetchImpl ?? fetch;
|
|
150
|
+
}
|
|
151
|
+
async parse(source, clientRequestId, signal) {
|
|
152
|
+
const classified = classifySource(source);
|
|
153
|
+
if (classified.kind !== "url") {
|
|
154
|
+
throw remoteError({
|
|
155
|
+
code: "UNSUPPORTED_SOURCE",
|
|
156
|
+
message: "The remote Omni service accepts only HTTP(S) sources.",
|
|
157
|
+
failureScope: "source",
|
|
158
|
+
userAction: "Use the local Bridge path for local files.",
|
|
159
|
+
operationCreated: false,
|
|
160
|
+
retryable: false,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return this.#call("parse", { source: classified.source }, clientRequestId, signal);
|
|
164
|
+
}
|
|
165
|
+
status(operationId, waitMs, signal) {
|
|
166
|
+
return this.#call("get_parse_status", {
|
|
167
|
+
operation_id: operationId,
|
|
168
|
+
...(waitMs === undefined ? {} : { wait_ms: waitMs }),
|
|
169
|
+
}, `${operationId}:status`, signal);
|
|
170
|
+
}
|
|
171
|
+
cancel(operationId, signal) {
|
|
172
|
+
return this.#call("cancel_parse", { operation_id: operationId }, `${operationId}:cancel`, signal);
|
|
173
|
+
}
|
|
174
|
+
async #call(name, args, requestId, signal) {
|
|
175
|
+
if (this.#apiKey === undefined || this.#apiKey.length === 0) {
|
|
176
|
+
throw remoteError({
|
|
177
|
+
code: "API_KEY_REQUIRED",
|
|
178
|
+
message: "A Cue API Key is required for Omni parsing.",
|
|
179
|
+
failureScope: "authentication",
|
|
180
|
+
userAction: "Create or configure an API Key at https://cuecue.cn/api-key without pasting it into chat.",
|
|
181
|
+
operationCreated: false,
|
|
182
|
+
retryable: false,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
let response;
|
|
186
|
+
try {
|
|
187
|
+
response = await this.#fetch(REMOTE_OMNI_MCP_URL, {
|
|
188
|
+
method: "POST",
|
|
189
|
+
headers: {
|
|
190
|
+
authorization: `Bearer ${this.#apiKey}`,
|
|
191
|
+
accept: "application/json, text/event-stream",
|
|
192
|
+
"cache-control": "no-store",
|
|
193
|
+
"content-type": "application/json",
|
|
194
|
+
"idempotency-key": requestId,
|
|
195
|
+
},
|
|
196
|
+
body: JSON.stringify({
|
|
197
|
+
jsonrpc: "2.0",
|
|
198
|
+
id: requestId,
|
|
199
|
+
method: "tools/call",
|
|
200
|
+
params: { name, arguments: args },
|
|
201
|
+
}),
|
|
202
|
+
signal,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
if (signal.aborted)
|
|
207
|
+
throw error;
|
|
208
|
+
throw remoteError({
|
|
209
|
+
code: "SERVICE_TEMPORARILY_UNAVAILABLE",
|
|
210
|
+
message: "Omni could not reach the remote parsing service.",
|
|
211
|
+
failureScope: "service",
|
|
212
|
+
userAction: "Retry with bounded backoff while preserving the same request identity.",
|
|
213
|
+
operationCreated: true,
|
|
214
|
+
retryable: true,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
if (!response.ok)
|
|
218
|
+
throw responseError(response);
|
|
219
|
+
const body = await response.text();
|
|
220
|
+
let envelope;
|
|
221
|
+
try {
|
|
222
|
+
envelope = response.headers.get("content-type")?.includes("text/event-stream") === true
|
|
223
|
+
? jsonFromSse(body, requestId)
|
|
224
|
+
: JSON.parse(body);
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
if (error instanceof OmniBridgeError)
|
|
228
|
+
throw error;
|
|
229
|
+
throw protocolError();
|
|
230
|
+
}
|
|
231
|
+
return decodeEnvelope(envelope, requestId);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { OmniBridgeErrorPayload } from "./errors.js";
|
|
4
|
+
export interface DataHandling {
|
|
5
|
+
processing_copy: "in_use" | "pending" | "deleted";
|
|
6
|
+
temporary_data: "in_use" | "pending" | "deleted";
|
|
7
|
+
delivery_result: "not_created" | "pending" | "deleted_after_ack" | {
|
|
8
|
+
expires_at: string;
|
|
9
|
+
};
|
|
10
|
+
original_source: "unchanged";
|
|
11
|
+
remote_content_retained?: false;
|
|
12
|
+
}
|
|
13
|
+
export type ParseResult = {
|
|
14
|
+
status: "processing";
|
|
15
|
+
operation_id: string;
|
|
16
|
+
stage: string;
|
|
17
|
+
progress: number;
|
|
18
|
+
progress_detail?: {
|
|
19
|
+
unit: "page" | "sheet" | "slide" | "frame" | "segment";
|
|
20
|
+
completed: number;
|
|
21
|
+
total: number;
|
|
22
|
+
};
|
|
23
|
+
next_action: "check_status";
|
|
24
|
+
poll_after_seconds: number;
|
|
25
|
+
can_cancel: boolean;
|
|
26
|
+
data_handling: DataHandling;
|
|
27
|
+
} | {
|
|
28
|
+
status: "completed";
|
|
29
|
+
operation_id: string;
|
|
30
|
+
result: {
|
|
31
|
+
kind: "inline";
|
|
32
|
+
text: string;
|
|
33
|
+
} | {
|
|
34
|
+
kind: "artifact";
|
|
35
|
+
result_id: string;
|
|
36
|
+
result_bytes: number;
|
|
37
|
+
expires_at: string;
|
|
38
|
+
preview: string;
|
|
39
|
+
next_cursor: string;
|
|
40
|
+
};
|
|
41
|
+
billing?: {
|
|
42
|
+
credits_charged: number;
|
|
43
|
+
credits_remaining: number;
|
|
44
|
+
};
|
|
45
|
+
data_handling: DataHandling;
|
|
46
|
+
local_result_cache?: {
|
|
47
|
+
expires_at: string;
|
|
48
|
+
discard_action: "discard_result";
|
|
49
|
+
};
|
|
50
|
+
} | {
|
|
51
|
+
status: "cleanup_pending";
|
|
52
|
+
operation_id: string;
|
|
53
|
+
result?: {
|
|
54
|
+
kind: "inline";
|
|
55
|
+
text: string;
|
|
56
|
+
} | {
|
|
57
|
+
kind: "artifact";
|
|
58
|
+
result_id: string;
|
|
59
|
+
result_bytes: number;
|
|
60
|
+
expires_at: string;
|
|
61
|
+
preview: string;
|
|
62
|
+
next_cursor: string;
|
|
63
|
+
};
|
|
64
|
+
cleanup_deadline: string;
|
|
65
|
+
data_handling: DataHandling;
|
|
66
|
+
} | {
|
|
67
|
+
status: "failed";
|
|
68
|
+
error: OmniBridgeErrorPayload;
|
|
69
|
+
} | {
|
|
70
|
+
status: "canceled";
|
|
71
|
+
operation_id: string;
|
|
72
|
+
cleanup_deadline?: string;
|
|
73
|
+
data_handling: DataHandling;
|
|
74
|
+
} | {
|
|
75
|
+
status: "expired";
|
|
76
|
+
operation_id: string;
|
|
77
|
+
requires_user_confirmation: true;
|
|
78
|
+
};
|
|
79
|
+
export declare const stableErrorSchema: z.ZodObject<{
|
|
80
|
+
ok: z.ZodLiteral<false>;
|
|
81
|
+
code: z.ZodString;
|
|
82
|
+
failure_scope: z.ZodOptional<z.ZodEnum<["source", "local_capability", "authentication", "billing", "service", "parser", "operation", "cleanup", "bridge"]>>;
|
|
83
|
+
source_kind: z.ZodOptional<z.ZodEnum<["local", "url"]>>;
|
|
84
|
+
retryable: z.ZodBoolean;
|
|
85
|
+
message: z.ZodString;
|
|
86
|
+
user_action: z.ZodOptional<z.ZodString>;
|
|
87
|
+
request_id: z.ZodOptional<z.ZodString>;
|
|
88
|
+
operation_created: z.ZodBoolean;
|
|
89
|
+
file_uploaded: z.ZodBoolean;
|
|
90
|
+
parser_started: z.ZodBoolean;
|
|
91
|
+
billed: z.ZodBoolean;
|
|
92
|
+
content_released: z.ZodBoolean;
|
|
93
|
+
retry_after: z.ZodOptional<z.ZodNumber>;
|
|
94
|
+
constraints: z.ZodOptional<z.ZodObject<{
|
|
95
|
+
max_bytes: z.ZodOptional<z.ZodNumber>;
|
|
96
|
+
supported_extensions: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
97
|
+
}, "strict", z.ZodTypeAny, {
|
|
98
|
+
max_bytes?: number | undefined;
|
|
99
|
+
supported_extensions?: string[] | undefined;
|
|
100
|
+
}, {
|
|
101
|
+
max_bytes?: number | undefined;
|
|
102
|
+
supported_extensions?: string[] | undefined;
|
|
103
|
+
}>>;
|
|
104
|
+
}, "strict", z.ZodTypeAny, {
|
|
105
|
+
billed: boolean;
|
|
106
|
+
retryable: boolean;
|
|
107
|
+
code: string;
|
|
108
|
+
file_uploaded: boolean;
|
|
109
|
+
content_released: boolean;
|
|
110
|
+
parser_started: boolean;
|
|
111
|
+
message: string;
|
|
112
|
+
ok: false;
|
|
113
|
+
operation_created: boolean;
|
|
114
|
+
failure_scope?: "source" | "local_capability" | "authentication" | "billing" | "service" | "parser" | "operation" | "cleanup" | "bridge" | undefined;
|
|
115
|
+
source_kind?: "local" | "url" | undefined;
|
|
116
|
+
user_action?: string | undefined;
|
|
117
|
+
request_id?: string | undefined;
|
|
118
|
+
retry_after?: number | undefined;
|
|
119
|
+
constraints?: {
|
|
120
|
+
max_bytes?: number | undefined;
|
|
121
|
+
supported_extensions?: string[] | undefined;
|
|
122
|
+
} | undefined;
|
|
123
|
+
}, {
|
|
124
|
+
billed: boolean;
|
|
125
|
+
retryable: boolean;
|
|
126
|
+
code: string;
|
|
127
|
+
file_uploaded: boolean;
|
|
128
|
+
content_released: boolean;
|
|
129
|
+
parser_started: boolean;
|
|
130
|
+
message: string;
|
|
131
|
+
ok: false;
|
|
132
|
+
operation_created: boolean;
|
|
133
|
+
failure_scope?: "source" | "local_capability" | "authentication" | "billing" | "service" | "parser" | "operation" | "cleanup" | "bridge" | undefined;
|
|
134
|
+
source_kind?: "local" | "url" | undefined;
|
|
135
|
+
user_action?: string | undefined;
|
|
136
|
+
request_id?: string | undefined;
|
|
137
|
+
retry_after?: number | undefined;
|
|
138
|
+
constraints?: {
|
|
139
|
+
max_bytes?: number | undefined;
|
|
140
|
+
supported_extensions?: string[] | undefined;
|
|
141
|
+
} | undefined;
|
|
142
|
+
}>;
|
|
143
|
+
export declare const dataHandlingSchema: z.ZodObject<{
|
|
144
|
+
processing_copy: z.ZodEnum<["in_use", "pending", "deleted"]>;
|
|
145
|
+
temporary_data: z.ZodEnum<["in_use", "pending", "deleted"]>;
|
|
146
|
+
delivery_result: z.ZodUnion<[z.ZodLiteral<"not_created">, z.ZodLiteral<"pending">, z.ZodLiteral<"deleted_after_ack">, z.ZodObject<{
|
|
147
|
+
expires_at: z.ZodString;
|
|
148
|
+
}, "strict", z.ZodTypeAny, {
|
|
149
|
+
expires_at: string;
|
|
150
|
+
}, {
|
|
151
|
+
expires_at: string;
|
|
152
|
+
}>]>;
|
|
153
|
+
original_source: z.ZodLiteral<"unchanged">;
|
|
154
|
+
remote_content_retained: z.ZodOptional<z.ZodLiteral<false>>;
|
|
155
|
+
}, "strict", z.ZodTypeAny, {
|
|
156
|
+
processing_copy: "in_use" | "pending" | "deleted";
|
|
157
|
+
temporary_data: "in_use" | "pending" | "deleted";
|
|
158
|
+
delivery_result: "not_created" | "pending" | "deleted_after_ack" | {
|
|
159
|
+
expires_at: string;
|
|
160
|
+
};
|
|
161
|
+
original_source: "unchanged";
|
|
162
|
+
remote_content_retained?: false | undefined;
|
|
163
|
+
}, {
|
|
164
|
+
processing_copy: "in_use" | "pending" | "deleted";
|
|
165
|
+
temporary_data: "in_use" | "pending" | "deleted";
|
|
166
|
+
delivery_result: "not_created" | "pending" | "deleted_after_ack" | {
|
|
167
|
+
expires_at: string;
|
|
168
|
+
};
|
|
169
|
+
original_source: "unchanged";
|
|
170
|
+
remote_content_retained?: false | undefined;
|
|
171
|
+
}>;
|
|
172
|
+
export declare const progressDetailSchema: z.ZodEffects<z.ZodObject<{
|
|
173
|
+
unit: z.ZodEnum<["page", "sheet", "slide", "frame", "segment"]>;
|
|
174
|
+
completed: z.ZodNumber;
|
|
175
|
+
total: z.ZodNumber;
|
|
176
|
+
}, "strict", z.ZodTypeAny, {
|
|
177
|
+
unit: "page" | "sheet" | "slide" | "frame" | "segment";
|
|
178
|
+
total: number;
|
|
179
|
+
completed: number;
|
|
180
|
+
}, {
|
|
181
|
+
unit: "page" | "sheet" | "slide" | "frame" | "segment";
|
|
182
|
+
total: number;
|
|
183
|
+
completed: number;
|
|
184
|
+
}>, {
|
|
185
|
+
unit: "page" | "sheet" | "slide" | "frame" | "segment";
|
|
186
|
+
total: number;
|
|
187
|
+
completed: number;
|
|
188
|
+
}, {
|
|
189
|
+
unit: "page" | "sheet" | "slide" | "frame" | "segment";
|
|
190
|
+
total: number;
|
|
191
|
+
completed: number;
|
|
192
|
+
}>;
|
|
193
|
+
export declare const parseResultSchema: z.ZodTypeAny;
|
|
194
|
+
export declare const readResultOutputSchema: z.ZodTypeAny;
|
|
195
|
+
export declare const discardResultOutputSchema: z.ZodTypeAny;
|
|
196
|
+
export declare const parseToolOutputSchema: z.ZodTypeAny;
|
|
197
|
+
export declare const readResultToolOutputSchema: z.ZodTypeAny;
|
|
198
|
+
export declare const discardResultToolOutputSchema: z.ZodTypeAny;
|
|
199
|
+
export declare function structuredResult(schema: z.ZodTypeAny, value: unknown): CallToolResult;
|