Merge pull request #15 from LukeSal88/feature/response-length-limits
feat: Add response length limiting with max_length and start_index parameters
This commit is contained in:
@ -4,6 +4,14 @@ import is_ip_private from "private-ip";
|
|||||||
import { RequestPayload } from "./types.js";
|
import { RequestPayload } from "./types.js";
|
||||||
|
|
||||||
export class Fetcher {
|
export class Fetcher {
|
||||||
|
private static applyLengthLimits(text: string, maxLength: number, startIndex: number): string {
|
||||||
|
if (startIndex >= text.length) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const end = Math.min(startIndex + maxLength, text.length);
|
||||||
|
return text.substring(startIndex, end);
|
||||||
|
}
|
||||||
private static async _fetch({
|
private static async _fetch({
|
||||||
url,
|
url,
|
||||||
headers,
|
headers,
|
||||||
@ -38,7 +46,15 @@ export class Fetcher {
|
|||||||
static async html(requestPayload: RequestPayload) {
|
static async html(requestPayload: RequestPayload) {
|
||||||
try {
|
try {
|
||||||
const response = await this._fetch(requestPayload);
|
const response = await this._fetch(requestPayload);
|
||||||
const html = await response.text();
|
let html = await response.text();
|
||||||
|
|
||||||
|
// Apply length limits
|
||||||
|
html = this.applyLengthLimits(
|
||||||
|
html,
|
||||||
|
requestPayload.max_length ?? 5000,
|
||||||
|
requestPayload.start_index ?? 0
|
||||||
|
);
|
||||||
|
|
||||||
return { content: [{ type: "text", text: html }], isError: false };
|
return { content: [{ type: "text", text: html }], isError: false };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
@ -52,8 +68,17 @@ export class Fetcher {
|
|||||||
try {
|
try {
|
||||||
const response = await this._fetch(requestPayload);
|
const response = await this._fetch(requestPayload);
|
||||||
const json = await response.json();
|
const json = await response.json();
|
||||||
|
let jsonString = JSON.stringify(json);
|
||||||
|
|
||||||
|
// Apply length limits
|
||||||
|
jsonString = this.applyLengthLimits(
|
||||||
|
jsonString,
|
||||||
|
requestPayload.max_length ?? 5000,
|
||||||
|
requestPayload.start_index ?? 0
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{ type: "text", text: JSON.stringify(json) }],
|
content: [{ type: "text", text: jsonString }],
|
||||||
isError: false,
|
isError: false,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -78,8 +103,14 @@ export class Fetcher {
|
|||||||
Array.from(styles).forEach((style) => style.remove());
|
Array.from(styles).forEach((style) => style.remove());
|
||||||
|
|
||||||
const text = document.body.textContent || "";
|
const text = document.body.textContent || "";
|
||||||
|
let normalizedText = text.replace(/\s+/g, " ").trim();
|
||||||
|
|
||||||
const normalizedText = text.replace(/\s+/g, " ").trim();
|
// Apply length limits
|
||||||
|
normalizedText = this.applyLengthLimits(
|
||||||
|
normalizedText,
|
||||||
|
requestPayload.max_length ?? 5000,
|
||||||
|
requestPayload.start_index ?? 0
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [{ type: "text", text: normalizedText }],
|
content: [{ type: "text", text: normalizedText }],
|
||||||
@ -98,7 +129,15 @@ export class Fetcher {
|
|||||||
const response = await this._fetch(requestPayload);
|
const response = await this._fetch(requestPayload);
|
||||||
const html = await response.text();
|
const html = await response.text();
|
||||||
const turndownService = new TurndownService();
|
const turndownService = new TurndownService();
|
||||||
const markdown = turndownService.turndown(html);
|
let markdown = turndownService.turndown(html);
|
||||||
|
|
||||||
|
// Apply length limits
|
||||||
|
markdown = this.applyLengthLimits(
|
||||||
|
markdown,
|
||||||
|
requestPayload.max_length ?? 5000,
|
||||||
|
requestPayload.start_index ?? 0
|
||||||
|
);
|
||||||
|
|
||||||
return { content: [{ type: "text", text: markdown }], isError: false };
|
return { content: [{ type: "text", text: markdown }], isError: false };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
|
32
src/index.ts
32
src/index.ts
@ -39,6 +39,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|||||||
type: "object",
|
type: "object",
|
||||||
description: "Optional headers to include in the request",
|
description: "Optional headers to include in the request",
|
||||||
},
|
},
|
||||||
|
max_length: {
|
||||||
|
type: "number",
|
||||||
|
description: "Maximum number of characters to return (default: 5000)",
|
||||||
|
},
|
||||||
|
start_index: {
|
||||||
|
type: "number",
|
||||||
|
description: "Start content from this character index (default: 0)",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: ["url"],
|
required: ["url"],
|
||||||
},
|
},
|
||||||
@ -57,6 +65,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|||||||
type: "object",
|
type: "object",
|
||||||
description: "Optional headers to include in the request",
|
description: "Optional headers to include in the request",
|
||||||
},
|
},
|
||||||
|
max_length: {
|
||||||
|
type: "number",
|
||||||
|
description: "Maximum number of characters to return (default: 5000)",
|
||||||
|
},
|
||||||
|
start_index: {
|
||||||
|
type: "number",
|
||||||
|
description: "Start content from this character index (default: 0)",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: ["url"],
|
required: ["url"],
|
||||||
},
|
},
|
||||||
@ -76,6 +92,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|||||||
type: "object",
|
type: "object",
|
||||||
description: "Optional headers to include in the request",
|
description: "Optional headers to include in the request",
|
||||||
},
|
},
|
||||||
|
max_length: {
|
||||||
|
type: "number",
|
||||||
|
description: "Maximum number of characters to return (default: 5000)",
|
||||||
|
},
|
||||||
|
start_index: {
|
||||||
|
type: "number",
|
||||||
|
description: "Start content from this character index (default: 0)",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: ["url"],
|
required: ["url"],
|
||||||
},
|
},
|
||||||
@ -94,6 +118,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|||||||
type: "object",
|
type: "object",
|
||||||
description: "Optional headers to include in the request",
|
description: "Optional headers to include in the request",
|
||||||
},
|
},
|
||||||
|
max_length: {
|
||||||
|
type: "number",
|
||||||
|
description: "Maximum number of characters to return (default: 5000)",
|
||||||
|
},
|
||||||
|
start_index: {
|
||||||
|
type: "number",
|
||||||
|
description: "Start content from this character index (default: 0)",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: ["url"],
|
required: ["url"],
|
||||||
},
|
},
|
||||||
|
10
src/types.ts
10
src/types.ts
@ -3,6 +3,14 @@ import { z } from "zod";
|
|||||||
export const RequestPayloadSchema = z.object({
|
export const RequestPayloadSchema = z.object({
|
||||||
url: z.string().url(),
|
url: z.string().url(),
|
||||||
headers: z.record(z.string()).optional(),
|
headers: z.record(z.string()).optional(),
|
||||||
|
max_length: z.number().int().min(1).optional().default(5000),
|
||||||
|
start_index: z.number().int().min(0).optional().default(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type RequestPayload = z.infer<typeof RequestPayloadSchema>;
|
// Make sure TypeScript treats the fields as optional with defaults
|
||||||
|
export type RequestPayload = {
|
||||||
|
url: string;
|
||||||
|
headers?: Record<string, string>;
|
||||||
|
max_length?: number;
|
||||||
|
start_index?: number;
|
||||||
|
};
|
||||||
|
Reference in New Issue
Block a user