@manfred-kunze-dev/backbone-mcp-server 2.9.0 → 2.10.0-dev.2
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/dist/tools/conversion.js +56 -49
- package/package.json +1 -1
package/dist/tools/conversion.js
CHANGED
|
@@ -30,9 +30,49 @@ function formatExportDocument(doc) {
|
|
|
30
30
|
parts.push({ type: "text", text: `--- ${doc.filename} (json) ---\n${JSON.stringify(doc.jsonContent, null, 2)}` });
|
|
31
31
|
return parts;
|
|
32
32
|
}
|
|
33
|
+
const TERMINAL_STATUSES = new Set(["SUCCESS", "PARTIAL_SUCCESS", "FAILURE", "COMPLETED", "FAILED"]);
|
|
34
|
+
/**
|
|
35
|
+
* Poll an async task until terminal status, then fetch and return the result.
|
|
36
|
+
*/
|
|
37
|
+
async function waitForTask(client, taskId, pollSeconds = 30) {
|
|
38
|
+
// eslint-disable-next-line no-constant-condition
|
|
39
|
+
while (true) {
|
|
40
|
+
const { data } = await client.GET("/v1/convert/tasks/{taskId}", {
|
|
41
|
+
params: { path: { taskId }, query: { wait: pollSeconds } },
|
|
42
|
+
});
|
|
43
|
+
const status = data?.taskStatus;
|
|
44
|
+
if (status && TERMINAL_STATUSES.has(status.toUpperCase()))
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
const { data } = await client.GET("/v1/convert/tasks/{taskId}/result", {
|
|
48
|
+
params: { path: { taskId } },
|
|
49
|
+
});
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Format a ConvertResponse into MCP text content parts.
|
|
54
|
+
*/
|
|
55
|
+
function formatConvertResponse(result) {
|
|
56
|
+
const parts = [];
|
|
57
|
+
if (result.documents?.length) {
|
|
58
|
+
for (const doc of result.documents) {
|
|
59
|
+
parts.push(...formatExportDocument(doc));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (result.errors?.length) {
|
|
63
|
+
parts.push({
|
|
64
|
+
type: "text",
|
|
65
|
+
text: `Errors:\n${result.errors.map((e) => `- ${e.filename ?? "unknown"}: ${e.errorMessage}`).join("\n")}`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (parts.length === 0) {
|
|
69
|
+
parts.push({ type: "text", text: `Conversion completed with status: ${result.status}` });
|
|
70
|
+
}
|
|
71
|
+
return parts;
|
|
72
|
+
}
|
|
33
73
|
export function register(server, client) {
|
|
34
74
|
// ── convert_document ────────────────────────────────────────────────────
|
|
35
|
-
server.tool("backbone_convert_document", "Convert documents (PDF, DOCX, XLSX, images, etc.) to Markdown, text, HTML, or JSON. Accepts URLs, base64 data, or local file paths. Local files are automatically read and base64-encoded. Use this to convert binary files before passing them to backbone_create_extraction. Pipeline options: 'fast' (default, fast text extraction), 'ocr' (OCR with layout analysis), or 'vlm' (vision language model for image-heavy/complex layouts).
|
|
75
|
+
server.tool("backbone_convert_document", "Convert documents (PDF, DOCX, XLSX, images, etc.) to Markdown, text, HTML, or JSON. Accepts URLs, base64 data, or local file paths. Local files are automatically read and base64-encoded. Use this to convert binary files before passing them to backbone_create_extraction. Pipeline options: 'fast' (default, fast text extraction), 'ocr' (OCR with layout analysis), or 'vlm' (vision language model for image-heavy/complex layouts). Set async=true with waitForCompletion=true for long-running pipelines like VLM — this submits asynchronously and automatically polls until the result is ready.", {
|
|
36
76
|
sources: z
|
|
37
77
|
.array(z.object({
|
|
38
78
|
type: z.enum(["url", "base64", "file"]).describe("Source type: 'url' for HTTP URLs, 'base64' for base64-encoded data, 'file' for local file paths"),
|
|
@@ -64,8 +104,13 @@ export function register(server, client) {
|
|
|
64
104
|
.boolean()
|
|
65
105
|
.optional()
|
|
66
106
|
.default(false)
|
|
67
|
-
.describe("If true, submit as async task
|
|
68
|
-
|
|
107
|
+
.describe("If true, submit as async task. Combine with waitForCompletion=true to get the final result in a single call."),
|
|
108
|
+
waitForCompletion: z
|
|
109
|
+
.boolean()
|
|
110
|
+
.optional()
|
|
111
|
+
.default(false)
|
|
112
|
+
.describe("When async=true, automatically poll until done and return the final result instead of a task ID. Recommended for VLM pipeline."),
|
|
113
|
+
}, async ({ sources, pipeline, options: pipelineOptions, async: isAsync, waitForCompletion }) => {
|
|
69
114
|
try {
|
|
70
115
|
const apiSources = [];
|
|
71
116
|
for (const src of sources) {
|
|
@@ -122,36 +167,17 @@ export function register(server, client) {
|
|
|
122
167
|
};
|
|
123
168
|
if (isAsync) {
|
|
124
169
|
const { data } = await client.POST("/v1/convert/source/async", { body });
|
|
170
|
+
const taskId = data?.taskId;
|
|
171
|
+
if (waitForCompletion && taskId) {
|
|
172
|
+
const result = await waitForTask(client, taskId);
|
|
173
|
+
return { content: formatConvertResponse(result) };
|
|
174
|
+
}
|
|
125
175
|
return {
|
|
126
|
-
content: [
|
|
127
|
-
{
|
|
128
|
-
type: "text",
|
|
129
|
-
text: JSON.stringify(data, null, 2),
|
|
130
|
-
},
|
|
131
|
-
],
|
|
176
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
132
177
|
};
|
|
133
178
|
}
|
|
134
179
|
const { data } = await client.POST("/v1/convert/source", { body });
|
|
135
|
-
|
|
136
|
-
const parts = [];
|
|
137
|
-
if (convertResult.documents?.length) {
|
|
138
|
-
for (const doc of convertResult.documents) {
|
|
139
|
-
parts.push(...formatExportDocument(doc));
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (convertResult.errors?.length) {
|
|
143
|
-
parts.push({
|
|
144
|
-
type: "text",
|
|
145
|
-
text: `Errors:\n${convertResult.errors.map((e) => `- ${e.filename ?? "unknown"}: ${e.errorMessage}`).join("\n")}`,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
if (parts.length === 0) {
|
|
149
|
-
parts.push({
|
|
150
|
-
type: "text",
|
|
151
|
-
text: `Conversion completed with status: ${convertResult.status}`,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
return { content: parts };
|
|
180
|
+
return { content: formatConvertResponse(data) };
|
|
155
181
|
}
|
|
156
182
|
catch (error) {
|
|
157
183
|
return {
|
|
@@ -194,26 +220,7 @@ export function register(server, client) {
|
|
|
194
220
|
const { data } = await client.GET("/v1/convert/tasks/{taskId}/result", {
|
|
195
221
|
params: { path: { taskId } },
|
|
196
222
|
});
|
|
197
|
-
|
|
198
|
-
const parts = [];
|
|
199
|
-
if (result.documents?.length) {
|
|
200
|
-
for (const doc of result.documents) {
|
|
201
|
-
parts.push(...formatExportDocument(doc));
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
if (result.errors?.length) {
|
|
205
|
-
parts.push({
|
|
206
|
-
type: "text",
|
|
207
|
-
text: `Errors:\n${result.errors.map((e) => `- ${e.filename ?? "unknown"}: ${e.errorMessage}`).join("\n")}`,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
if (parts.length === 0) {
|
|
211
|
-
parts.push({
|
|
212
|
-
type: "text",
|
|
213
|
-
text: `Task result status: ${result.status}`,
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
return { content: parts };
|
|
223
|
+
return { content: formatConvertResponse(data) };
|
|
217
224
|
}
|
|
218
225
|
catch (error) {
|
|
219
226
|
return {
|