@ontosdk/mcp 1.0.3 → 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/dist/index.d.ts +124 -6
- package/dist/index.js +283 -8
- package/dist/index.js.map +1 -1
- package/dist/server.js +373 -109
- package/dist/server.js.map +1 -1
- package/package.json +14 -6
- package/bin/onto-mcp.cjs +0 -15
- package/dist/lib/api-client.d.ts +0 -11
- package/dist/lib/api-client.js +0 -88
- package/dist/lib/api-client.js.map +0 -1
- package/dist/lib/errors.d.ts +0 -2
- package/dist/lib/errors.js +0 -24
- package/dist/lib/errors.js.map +0 -1
- package/dist/lib/types.d.ts +0 -76
- package/dist/lib/types.js +0 -4
- package/dist/lib/types.js.map +0 -1
- package/dist/lib/version.d.ts +0 -1
- package/dist/lib/version.js +0 -2
- package/dist/lib/version.js.map +0 -1
- package/dist/server.d.ts +0 -2
- package/dist/tools/read-and-score.d.ts +0 -14
- package/dist/tools/read-and-score.js +0 -43
- package/dist/tools/read-and-score.js.map +0 -1
- package/dist/tools/read.d.ts +0 -14
- package/dist/tools/read.js +0 -36
- package/dist/tools/read.js.map +0 -1
- package/dist/tools/score.d.ts +0 -13
- package/dist/tools/score.js +0 -71
- package/dist/tools/score.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,124 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
|
|
4
|
+
declare const readUrlInputSchema: z.ZodObject<{
|
|
5
|
+
url: z.ZodString;
|
|
6
|
+
fresh: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
url: string;
|
|
9
|
+
fresh: boolean;
|
|
10
|
+
}, {
|
|
11
|
+
url: string;
|
|
12
|
+
fresh?: boolean | undefined;
|
|
13
|
+
}>;
|
|
14
|
+
type ReadUrlInput = z.infer<typeof readUrlInputSchema>;
|
|
15
|
+
declare function readUrl(input: ReadUrlInput): Promise<CallToolResult>;
|
|
16
|
+
|
|
17
|
+
interface ReadResponse {
|
|
18
|
+
status: 'success';
|
|
19
|
+
url: string;
|
|
20
|
+
markdown: string;
|
|
21
|
+
metadata: {
|
|
22
|
+
title: string;
|
|
23
|
+
description: string;
|
|
24
|
+
language?: string;
|
|
25
|
+
};
|
|
26
|
+
stats: {
|
|
27
|
+
raw_html_size_kb: number;
|
|
28
|
+
markdown_size_kb: number;
|
|
29
|
+
reduction_percent: number;
|
|
30
|
+
extraction_time_ms: number;
|
|
31
|
+
};
|
|
32
|
+
cache: {
|
|
33
|
+
hit: boolean;
|
|
34
|
+
ttl_seconds: number;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
interface Recommendation {
|
|
38
|
+
title?: string;
|
|
39
|
+
description?: string;
|
|
40
|
+
priority?: 'High' | 'Medium' | 'Low' | string;
|
|
41
|
+
[key: string]: unknown;
|
|
42
|
+
}
|
|
43
|
+
interface ScoreResponse {
|
|
44
|
+
status: 'success';
|
|
45
|
+
url: string;
|
|
46
|
+
aio_score: number;
|
|
47
|
+
grade: string;
|
|
48
|
+
hallucination_risk: 'low' | 'medium' | 'high';
|
|
49
|
+
insights: Record<string, boolean>;
|
|
50
|
+
penalties: string[];
|
|
51
|
+
benefits: string[];
|
|
52
|
+
recommendations: Recommendation[];
|
|
53
|
+
stats: {
|
|
54
|
+
raw_size: string;
|
|
55
|
+
efficiency: string;
|
|
56
|
+
extraction_time_ms: number;
|
|
57
|
+
};
|
|
58
|
+
bot_preview?: unknown;
|
|
59
|
+
}
|
|
60
|
+
interface ReadAndScoreResponse {
|
|
61
|
+
status: 'success';
|
|
62
|
+
url: string;
|
|
63
|
+
markdown: string;
|
|
64
|
+
metadata: {
|
|
65
|
+
title: string;
|
|
66
|
+
description: string;
|
|
67
|
+
language?: string;
|
|
68
|
+
};
|
|
69
|
+
aio_score: number;
|
|
70
|
+
grade: string;
|
|
71
|
+
hallucination_risk: 'low' | 'medium' | 'high';
|
|
72
|
+
insights: Record<string, boolean>;
|
|
73
|
+
penalties: string[];
|
|
74
|
+
benefits: string[];
|
|
75
|
+
recommendations: Recommendation[];
|
|
76
|
+
stats: {
|
|
77
|
+
raw_html_size_kb: number;
|
|
78
|
+
markdown_size_kb: number;
|
|
79
|
+
reduction_percent: number;
|
|
80
|
+
extraction_time_ms: number;
|
|
81
|
+
};
|
|
82
|
+
cache: {
|
|
83
|
+
hit: boolean;
|
|
84
|
+
ttl_seconds: number;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
declare const scoreUrlInputSchema: z.ZodObject<{
|
|
89
|
+
url: z.ZodString;
|
|
90
|
+
}, "strip", z.ZodTypeAny, {
|
|
91
|
+
url: string;
|
|
92
|
+
}, {
|
|
93
|
+
url: string;
|
|
94
|
+
}>;
|
|
95
|
+
type ScoreUrlInput = z.infer<typeof scoreUrlInputSchema>;
|
|
96
|
+
declare function scoreUrl(input: ScoreUrlInput): Promise<CallToolResult>;
|
|
97
|
+
|
|
98
|
+
declare const readAndScoreInputSchema: z.ZodObject<{
|
|
99
|
+
url: z.ZodString;
|
|
100
|
+
fresh: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
101
|
+
}, "strip", z.ZodTypeAny, {
|
|
102
|
+
url: string;
|
|
103
|
+
fresh: boolean;
|
|
104
|
+
}, {
|
|
105
|
+
url: string;
|
|
106
|
+
fresh?: boolean | undefined;
|
|
107
|
+
}>;
|
|
108
|
+
type ReadAndScoreInput = z.infer<typeof readAndScoreInputSchema>;
|
|
109
|
+
declare function readAndScore(input: ReadAndScoreInput): Promise<CallToolResult>;
|
|
110
|
+
|
|
111
|
+
declare class OntoApiError extends Error {
|
|
112
|
+
readonly status: number;
|
|
113
|
+
readonly code?: string | undefined;
|
|
114
|
+
constructor(message: string, status: number, code?: string | undefined);
|
|
115
|
+
}
|
|
116
|
+
interface CallOptions {
|
|
117
|
+
body: unknown;
|
|
118
|
+
signal?: AbortSignal;
|
|
119
|
+
}
|
|
120
|
+
declare function callOntoApi<T>(endpoint: string, options: CallOptions): Promise<T>;
|
|
121
|
+
|
|
122
|
+
declare const version = "1.1.1";
|
|
123
|
+
|
|
124
|
+
export { OntoApiError, type ReadAndScoreInput, type ReadAndScoreResponse, type ReadResponse, type ReadUrlInput, type Recommendation, type ScoreResponse, type ScoreUrlInput, callOntoApi, readAndScore, readAndScoreInputSchema, readUrl, readUrlInputSchema, scoreUrl, scoreUrlInputSchema, version };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,284 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
// src/tools/read.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
// src/lib/version.ts
|
|
5
|
+
var version = "1.1.1";
|
|
6
|
+
|
|
7
|
+
// src/lib/api-client.ts
|
|
8
|
+
var DEFAULT_BASE = "https://api.buildonto.dev";
|
|
9
|
+
var REQUEST_TIMEOUT_MS = 15e3;
|
|
10
|
+
var OntoApiError = class extends Error {
|
|
11
|
+
constructor(message, status, code) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.status = status;
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.name = "OntoApiError";
|
|
16
|
+
}
|
|
17
|
+
status;
|
|
18
|
+
code;
|
|
19
|
+
};
|
|
20
|
+
async function callOntoApi(endpoint, options) {
|
|
21
|
+
const apiKey = process.env.ONTO_API_KEY;
|
|
22
|
+
if (!apiKey) {
|
|
23
|
+
throw new OntoApiError(
|
|
24
|
+
"ONTO_API_KEY environment variable is not set. Get a key at https://app.buildonto.dev/read/keys",
|
|
25
|
+
0,
|
|
26
|
+
"NO_API_KEY"
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
const base = process.env.ONTO_API_BASE ?? DEFAULT_BASE;
|
|
30
|
+
const url = `${base}${endpoint}`;
|
|
31
|
+
const timeout = AbortSignal.timeout(REQUEST_TIMEOUT_MS);
|
|
32
|
+
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
33
|
+
let response;
|
|
34
|
+
try {
|
|
35
|
+
response = await fetch(url, {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: {
|
|
38
|
+
Authorization: `Bearer ${apiKey}`,
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
"User-Agent": `@ontosdk/mcp/${version}`
|
|
41
|
+
},
|
|
42
|
+
body: JSON.stringify(options.body),
|
|
43
|
+
signal
|
|
44
|
+
});
|
|
45
|
+
} catch (err) {
|
|
46
|
+
if (err instanceof DOMException && err.name === "TimeoutError") {
|
|
47
|
+
throw new OntoApiError(
|
|
48
|
+
`Onto API request timed out after ${REQUEST_TIMEOUT_MS / 1e3}s. The target site may be slow or unreachable.`,
|
|
49
|
+
0,
|
|
50
|
+
"TIMEOUT"
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
throw new OntoApiError(
|
|
54
|
+
`Failed to reach Onto API at ${base}: ${err.message}`,
|
|
55
|
+
0,
|
|
56
|
+
"NETWORK_ERROR"
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
const rawBody = await response.text();
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
let parsed = {};
|
|
62
|
+
try {
|
|
63
|
+
parsed = JSON.parse(rawBody);
|
|
64
|
+
} catch {
|
|
65
|
+
}
|
|
66
|
+
const message = humanizeError(response.status, parsed);
|
|
67
|
+
throw new OntoApiError(message, response.status, parsed.error);
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
return JSON.parse(rawBody);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
throw new OntoApiError(
|
|
73
|
+
`Onto API returned invalid JSON: ${err.message}`,
|
|
74
|
+
response.status,
|
|
75
|
+
"INVALID_RESPONSE"
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function humanizeError(status, body) {
|
|
80
|
+
if (status === 401) {
|
|
81
|
+
return "Invalid Onto API key. Verify your key at https://app.buildonto.dev/read/keys";
|
|
82
|
+
}
|
|
83
|
+
if (status === 402) {
|
|
84
|
+
return body.message ?? "Monthly plan quota exceeded and credit balance is empty. Top up credits at https://app.buildonto.dev/read/billing";
|
|
85
|
+
}
|
|
86
|
+
if (status === 403) {
|
|
87
|
+
if (body.error === "ROBOTS_BLOCKED") {
|
|
88
|
+
return body.message ?? "The target site blocks AI crawlers via robots.txt.";
|
|
89
|
+
}
|
|
90
|
+
return body.message ?? "Forbidden.";
|
|
91
|
+
}
|
|
92
|
+
if (status === 429) {
|
|
93
|
+
return body.message ?? "Onto API rate limit exceeded. Upgrade your tier at https://app.buildonto.dev/read/billing or wait for the monthly reset.";
|
|
94
|
+
}
|
|
95
|
+
if (status >= 500) {
|
|
96
|
+
return body.message ?? `Onto API server error (${status}). Try again in a moment.`;
|
|
97
|
+
}
|
|
98
|
+
return body.message ?? `Onto API returned ${status}.`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/lib/errors.ts
|
|
102
|
+
function formatToolError(error) {
|
|
103
|
+
if (error instanceof OntoApiError) {
|
|
104
|
+
return {
|
|
105
|
+
content: [{ type: "text", text: error.message }],
|
|
106
|
+
isError: true
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
+
return {
|
|
111
|
+
content: [
|
|
112
|
+
{
|
|
113
|
+
type: "text",
|
|
114
|
+
text: `Onto MCP error: ${message}
|
|
115
|
+
|
|
116
|
+
Troubleshooting:
|
|
117
|
+
- Verify ONTO_API_KEY is set and valid (https://app.buildonto.dev/read/keys)
|
|
118
|
+
- Check the target URL is publicly accessible
|
|
119
|
+
- Check your monthly quota at https://app.buildonto.dev/read/usage`
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
isError: true
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/tools/read.ts
|
|
127
|
+
var readUrlInputSchema = z.object({
|
|
128
|
+
url: z.string().url(),
|
|
129
|
+
fresh: z.boolean().optional().default(false)
|
|
130
|
+
});
|
|
131
|
+
async function readUrl(input) {
|
|
132
|
+
try {
|
|
133
|
+
const result = await callOntoApi("/v1/read", {
|
|
134
|
+
body: { url: input.url, fresh: input.fresh }
|
|
135
|
+
});
|
|
136
|
+
const metaLines = [
|
|
137
|
+
`- URL: ${result.url}`,
|
|
138
|
+
`- Title: ${result.metadata.title || "(none)"}`,
|
|
139
|
+
`- Original size: ${result.stats.raw_html_size_kb} KB`,
|
|
140
|
+
`- Cleaned size: ${result.stats.markdown_size_kb} KB`,
|
|
141
|
+
`- Reduction: ${result.stats.reduction_percent}%`,
|
|
142
|
+
`- Extraction time: ${result.stats.extraction_time_ms} ms`,
|
|
143
|
+
`- Cache: ${result.cache.hit ? "HIT" : "MISS"}`
|
|
144
|
+
].join("\n");
|
|
145
|
+
return {
|
|
146
|
+
content: [
|
|
147
|
+
{ type: "text", text: result.markdown },
|
|
148
|
+
{
|
|
149
|
+
type: "text",
|
|
150
|
+
text: `
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
**Source metadata (from Onto):**
|
|
155
|
+
${metaLines}`
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
};
|
|
159
|
+
} catch (error) {
|
|
160
|
+
return formatToolError(error);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/tools/score.ts
|
|
165
|
+
import { z as z2 } from "zod";
|
|
166
|
+
var scoreUrlInputSchema = z2.object({
|
|
167
|
+
url: z2.string().url()
|
|
168
|
+
});
|
|
169
|
+
async function scoreUrl(input) {
|
|
170
|
+
try {
|
|
171
|
+
const result = await callOntoApi("/v1/score", {
|
|
172
|
+
body: { url: input.url }
|
|
173
|
+
});
|
|
174
|
+
return {
|
|
175
|
+
content: [{ type: "text", text: formatScoreSummary(result) }]
|
|
176
|
+
};
|
|
177
|
+
} catch (error) {
|
|
178
|
+
return formatToolError(error);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function formatScoreSummary(result) {
|
|
182
|
+
const lines = [
|
|
183
|
+
`**AIO Score:** ${result.aio_score}/100 (${result.grade})`,
|
|
184
|
+
`**Hallucination risk:** ${result.hallucination_risk}`,
|
|
185
|
+
`**URL:** ${result.url}`,
|
|
186
|
+
""
|
|
187
|
+
];
|
|
188
|
+
if (result.benefits.length > 0) {
|
|
189
|
+
lines.push("**What works well:**");
|
|
190
|
+
for (const item of result.benefits) lines.push(`- ${item}`);
|
|
191
|
+
lines.push("");
|
|
192
|
+
}
|
|
193
|
+
if (result.penalties.length > 0) {
|
|
194
|
+
lines.push("**What hurts AI readability:**");
|
|
195
|
+
for (const item of result.penalties) lines.push(`- ${item}`);
|
|
196
|
+
lines.push("");
|
|
197
|
+
}
|
|
198
|
+
const insightEntries = Object.entries(result.insights ?? {});
|
|
199
|
+
if (insightEntries.length > 0) {
|
|
200
|
+
lines.push("**Insights:**");
|
|
201
|
+
for (const [key, value] of insightEntries) {
|
|
202
|
+
lines.push(`- ${key}: ${value ? "yes" : "no"}`);
|
|
203
|
+
}
|
|
204
|
+
lines.push("");
|
|
205
|
+
}
|
|
206
|
+
if (result.recommendations.length > 0) {
|
|
207
|
+
lines.push("**Recommendations:**");
|
|
208
|
+
for (const rec of result.recommendations) {
|
|
209
|
+
lines.push(describeRecommendation(rec));
|
|
210
|
+
}
|
|
211
|
+
lines.push("");
|
|
212
|
+
}
|
|
213
|
+
lines.push("**Stats:**");
|
|
214
|
+
lines.push(`- Raw size: ${result.stats.raw_size}`);
|
|
215
|
+
lines.push(`- Efficiency: ${result.stats.efficiency}`);
|
|
216
|
+
lines.push(`- Extraction time: ${result.stats.extraction_time_ms} ms`);
|
|
217
|
+
return lines.join("\n");
|
|
218
|
+
}
|
|
219
|
+
function describeRecommendation(rec) {
|
|
220
|
+
if (typeof rec === "string") return `- ${rec}`;
|
|
221
|
+
if (rec.title) {
|
|
222
|
+
const head = rec.priority ? `**${rec.title}** _(priority: ${rec.priority})_` : `**${rec.title}**`;
|
|
223
|
+
return rec.description ? `- ${head} \u2014 ${rec.description}` : `- ${head}`;
|
|
224
|
+
}
|
|
225
|
+
if (rec.description) return `- ${rec.description}`;
|
|
226
|
+
return `- ${JSON.stringify(rec)}`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/tools/read-and-score.ts
|
|
230
|
+
import { z as z3 } from "zod";
|
|
231
|
+
var readAndScoreInputSchema = z3.object({
|
|
232
|
+
url: z3.string().url(),
|
|
233
|
+
fresh: z3.boolean().optional().default(false)
|
|
234
|
+
});
|
|
235
|
+
async function readAndScore(input) {
|
|
236
|
+
try {
|
|
237
|
+
const result = await callOntoApi("/v1/read-and-score", {
|
|
238
|
+
body: { url: input.url, fresh: input.fresh }
|
|
239
|
+
});
|
|
240
|
+
const trustHint = trustLine(result.aio_score, result.hallucination_risk);
|
|
241
|
+
const summaryLines = [
|
|
242
|
+
`**Source quality assessment (from Onto):**`,
|
|
243
|
+
`- AIO Score: ${result.aio_score}/100 (${result.grade})`,
|
|
244
|
+
`- Hallucination risk: ${result.hallucination_risk}`,
|
|
245
|
+
`- Reduction: ${result.stats.reduction_percent}% (${result.stats.raw_html_size_kb} KB \u2192 ${result.stats.markdown_size_kb} KB)`,
|
|
246
|
+
`- Cache: ${result.cache.hit ? "HIT" : "MISS"}`,
|
|
247
|
+
"",
|
|
248
|
+
trustHint
|
|
249
|
+
];
|
|
250
|
+
return {
|
|
251
|
+
content: [
|
|
252
|
+
{ type: "text", text: result.markdown },
|
|
253
|
+
{ type: "text", text: `
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
${summaryLines.join("\n")}` }
|
|
258
|
+
]
|
|
259
|
+
};
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return formatToolError(error);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function trustLine(score, risk) {
|
|
265
|
+
if (risk === "high" || score < 40) {
|
|
266
|
+
return "Trust signal: low \u2014 this source is poorly structured for AI consumption. Verify any facts before relying on them.";
|
|
267
|
+
}
|
|
268
|
+
if (risk === "medium" || score < 70) {
|
|
269
|
+
return "Trust signal: medium \u2014 source is partially AI-readable. Cross-check critical claims.";
|
|
270
|
+
}
|
|
271
|
+
return "Trust signal: high \u2014 source is well-structured for AI consumption.";
|
|
272
|
+
}
|
|
273
|
+
export {
|
|
274
|
+
OntoApiError,
|
|
275
|
+
callOntoApi,
|
|
276
|
+
readAndScore,
|
|
277
|
+
readAndScoreInputSchema,
|
|
278
|
+
readUrl,
|
|
279
|
+
readUrlInputSchema,
|
|
280
|
+
scoreUrl,
|
|
281
|
+
scoreUrlInputSchema,
|
|
282
|
+
version
|
|
283
|
+
};
|
|
9
284
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;8DAE8D;AAE9D,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAqB,MAAM,iBAAiB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAsB,MAAM,kBAAkB,CAAC;AACrF,OAAO,EACL,YAAY,EACZ,uBAAuB,GAExB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAOhE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"sources":["../src/tools/read.ts","../src/lib/version.ts","../src/lib/api-client.ts","../src/lib/errors.ts","../src/tools/score.ts","../src/tools/read-and-score.ts"],"sourcesContent":["import { z } from 'zod';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { callOntoApi } from '../lib/api-client.js';\nimport { formatToolError } from '../lib/errors.js';\nimport type { ReadResponse } from '../lib/types.js';\n\nexport const readUrlInputSchema = z.object({\n url: z.string().url(),\n fresh: z.boolean().optional().default(false),\n});\n\nexport type ReadUrlInput = z.infer<typeof readUrlInputSchema>;\n\nexport async function readUrl(input: ReadUrlInput): Promise<CallToolResult> {\n try {\n const result = await callOntoApi<ReadResponse>('/v1/read', {\n body: { url: input.url, fresh: input.fresh },\n });\n\n const metaLines = [\n `- URL: ${result.url}`,\n `- Title: ${result.metadata.title || '(none)'}`,\n `- Original size: ${result.stats.raw_html_size_kb} KB`,\n `- Cleaned size: ${result.stats.markdown_size_kb} KB`,\n `- Reduction: ${result.stats.reduction_percent}%`,\n `- Extraction time: ${result.stats.extraction_time_ms} ms`,\n `- Cache: ${result.cache.hit ? 'HIT' : 'MISS'}`,\n ].join('\\n');\n\n return {\n content: [\n { type: 'text' as const, text: result.markdown },\n {\n type: 'text' as const,\n text: `\\n\\n---\\n\\n**Source metadata (from Onto):**\\n${metaLines}`,\n },\n ],\n };\n } catch (error) {\n return formatToolError(error);\n }\n}\n","export const version = '1.1.1';\n","/* Thin HTTP wrapper around the Onto Read API. Reads ONTO_API_KEY at call time\n * (not module-load) so server.ts can fail with a clean error message first. */\n\nimport { version as PACKAGE_VERSION } from './version.js';\nimport type { ApiErrorBody } from './types.js';\n\nconst DEFAULT_BASE = 'https://api.buildonto.dev';\nconst REQUEST_TIMEOUT_MS = 15_000;\n\nexport class OntoApiError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly code?: string,\n ) {\n super(message);\n this.name = 'OntoApiError';\n }\n}\n\ninterface CallOptions {\n body: unknown;\n signal?: AbortSignal;\n}\n\nexport async function callOntoApi<T>(endpoint: string, options: CallOptions): Promise<T> {\n const apiKey = process.env.ONTO_API_KEY;\n if (!apiKey) {\n throw new OntoApiError(\n 'ONTO_API_KEY environment variable is not set. Get a key at https://app.buildonto.dev/read/keys',\n 0,\n 'NO_API_KEY',\n );\n }\n\n const base = process.env.ONTO_API_BASE ?? DEFAULT_BASE;\n const url = `${base}${endpoint}`;\n\n const timeout = AbortSignal.timeout(REQUEST_TIMEOUT_MS);\n const signal = options.signal\n ? AbortSignal.any([options.signal, timeout])\n : timeout;\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n 'User-Agent': `@ontosdk/mcp/${PACKAGE_VERSION}`,\n },\n body: JSON.stringify(options.body),\n signal,\n });\n } catch (err) {\n if (err instanceof DOMException && err.name === 'TimeoutError') {\n throw new OntoApiError(\n `Onto API request timed out after ${REQUEST_TIMEOUT_MS / 1000}s. The target site may be slow or unreachable.`,\n 0,\n 'TIMEOUT',\n );\n }\n throw new OntoApiError(\n `Failed to reach Onto API at ${base}: ${(err as Error).message}`,\n 0,\n 'NETWORK_ERROR',\n );\n }\n\n const rawBody = await response.text();\n\n if (!response.ok) {\n let parsed: Partial<ApiErrorBody> = {};\n try {\n parsed = JSON.parse(rawBody) as Partial<ApiErrorBody>;\n } catch {\n // Body wasn't JSON; fall through with status-code-only error\n }\n\n const message = humanizeError(response.status, parsed);\n throw new OntoApiError(message, response.status, parsed.error);\n }\n\n try {\n return JSON.parse(rawBody) as T;\n } catch (err) {\n throw new OntoApiError(\n `Onto API returned invalid JSON: ${(err as Error).message}`,\n response.status,\n 'INVALID_RESPONSE',\n );\n }\n}\n\nfunction humanizeError(status: number, body: Partial<ApiErrorBody>): string {\n if (status === 401) {\n return 'Invalid Onto API key. Verify your key at https://app.buildonto.dev/read/keys';\n }\n if (status === 402) {\n return (\n body.message ??\n 'Monthly plan quota exceeded and credit balance is empty. Top up credits at https://app.buildonto.dev/read/billing'\n );\n }\n if (status === 403) {\n if (body.error === 'ROBOTS_BLOCKED') {\n return body.message ?? 'The target site blocks AI crawlers via robots.txt.';\n }\n return body.message ?? 'Forbidden.';\n }\n if (status === 429) {\n return (\n body.message ??\n 'Onto API rate limit exceeded. Upgrade your tier at https://app.buildonto.dev/read/billing or wait for the monthly reset.'\n );\n }\n if (status >= 500) {\n return body.message ?? `Onto API server error (${status}). Try again in a moment.`;\n }\n return body.message ?? `Onto API returned ${status}.`;\n}\n","/* Format errors as MCP tool responses. We don't throw McpError for\n * tool-call failures — the AI host shows tool errors to the user as\n * unhelpful internal-error messages. Returning isError: true with a\n * text body lets the model see what went wrong and recover. */\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { OntoApiError } from './api-client.js';\n\nexport function formatToolError(error: unknown): CallToolResult {\n if (error instanceof OntoApiError) {\n return {\n content: [{ type: 'text', text: error.message }],\n isError: true,\n };\n }\n\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [\n {\n type: 'text',\n text: `Onto MCP error: ${message}\\n\\nTroubleshooting:\\n- Verify ONTO_API_KEY is set and valid (https://app.buildonto.dev/read/keys)\\n- Check the target URL is publicly accessible\\n- Check your monthly quota at https://app.buildonto.dev/read/usage`,\n },\n ],\n isError: true,\n };\n}\n","import { z } from 'zod';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { callOntoApi } from '../lib/api-client.js';\nimport { formatToolError } from '../lib/errors.js';\nimport type { ScoreResponse, Recommendation } from '../lib/types.js';\n\nexport const scoreUrlInputSchema = z.object({\n url: z.string().url(),\n});\n\nexport type ScoreUrlInput = z.infer<typeof scoreUrlInputSchema>;\n\nexport async function scoreUrl(input: ScoreUrlInput): Promise<CallToolResult> {\n try {\n const result = await callOntoApi<ScoreResponse>('/v1/score', {\n body: { url: input.url },\n });\n\n return {\n content: [{ type: 'text' as const, text: formatScoreSummary(result) }],\n };\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport function formatScoreSummary(result: ScoreResponse): string {\n const lines: string[] = [\n `**AIO Score:** ${result.aio_score}/100 (${result.grade})`,\n `**Hallucination risk:** ${result.hallucination_risk}`,\n `**URL:** ${result.url}`,\n '',\n ];\n\n if (result.benefits.length > 0) {\n lines.push('**What works well:**');\n for (const item of result.benefits) lines.push(`- ${item}`);\n lines.push('');\n }\n\n if (result.penalties.length > 0) {\n lines.push('**What hurts AI readability:**');\n for (const item of result.penalties) lines.push(`- ${item}`);\n lines.push('');\n }\n\n const insightEntries = Object.entries(result.insights ?? {});\n if (insightEntries.length > 0) {\n lines.push('**Insights:**');\n for (const [key, value] of insightEntries) {\n lines.push(`- ${key}: ${value ? 'yes' : 'no'}`);\n }\n lines.push('');\n }\n\n if (result.recommendations.length > 0) {\n lines.push('**Recommendations:**');\n for (const rec of result.recommendations) {\n lines.push(describeRecommendation(rec));\n }\n lines.push('');\n }\n\n lines.push('**Stats:**');\n lines.push(`- Raw size: ${result.stats.raw_size}`);\n lines.push(`- Efficiency: ${result.stats.efficiency}`);\n lines.push(`- Extraction time: ${result.stats.extraction_time_ms} ms`);\n\n return lines.join('\\n');\n}\n\nfunction describeRecommendation(rec: Recommendation): string {\n if (typeof rec === 'string') return `- ${rec}`;\n if (rec.title) {\n const head = rec.priority ? `**${rec.title}** _(priority: ${rec.priority})_` : `**${rec.title}**`;\n return rec.description ? `- ${head} — ${rec.description}` : `- ${head}`;\n }\n if (rec.description) return `- ${rec.description}`;\n return `- ${JSON.stringify(rec)}`;\n}\n","import { z } from 'zod';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport { callOntoApi } from '../lib/api-client.js';\nimport { formatToolError } from '../lib/errors.js';\nimport type { ReadAndScoreResponse } from '../lib/types.js';\n\nexport const readAndScoreInputSchema = z.object({\n url: z.string().url(),\n fresh: z.boolean().optional().default(false),\n});\n\nexport type ReadAndScoreInput = z.infer<typeof readAndScoreInputSchema>;\n\nexport async function readAndScore(input: ReadAndScoreInput): Promise<CallToolResult> {\n try {\n const result = await callOntoApi<ReadAndScoreResponse>('/v1/read-and-score', {\n body: { url: input.url, fresh: input.fresh },\n });\n\n const trustHint = trustLine(result.aio_score, result.hallucination_risk);\n const summaryLines = [\n `**Source quality assessment (from Onto):**`,\n `- AIO Score: ${result.aio_score}/100 (${result.grade})`,\n `- Hallucination risk: ${result.hallucination_risk}`,\n `- Reduction: ${result.stats.reduction_percent}% (${result.stats.raw_html_size_kb} KB → ${result.stats.markdown_size_kb} KB)`,\n `- Cache: ${result.cache.hit ? 'HIT' : 'MISS'}`,\n '',\n trustHint,\n ];\n\n return {\n content: [\n { type: 'text' as const, text: result.markdown },\n { type: 'text' as const, text: `\\n\\n---\\n\\n${summaryLines.join('\\n')}` },\n ],\n };\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nfunction trustLine(score: number, risk: 'low' | 'medium' | 'high'): string {\n if (risk === 'high' || score < 40) {\n return 'Trust signal: low — this source is poorly structured for AI consumption. Verify any facts before relying on them.';\n }\n if (risk === 'medium' || score < 70) {\n return 'Trust signal: medium — source is partially AI-readable. Cross-check critical claims.';\n }\n return 'Trust signal: high — source is well-structured for AI consumption.';\n}\n"],"mappings":"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","names":["z","z","z","z"]}
|