@pinkpixel/prysm-mcp
Version:
MCP server for the Prysm web scraper - enabling AI assistants to scrape web content
994 lines (982 loc) • 32.9 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// node_modules/tsup/assets/esm_shims.js
var init_esm_shims = __esm({
"node_modules/tsup/assets/esm_shims.js"() {
"use strict";
}
});
// node_modules/json2md/lib/converters.js
var require_converters = __commonJS({
"node_modules/json2md/lib/converters.js"(exports, module) {
"use strict";
init_esm_shims();
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) {
return typeof obj;
} : function(obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true });
} else {
obj[key] = value;
}
return obj;
}
var converters = module.exports = {};
var generateHeader = function generateHeader2(repeat) {
return function(input, json2md2) {
return "#".repeat(repeat) + " " + json2md2(input);
};
};
var indent = function indent2(content, spaces, ignoreFirst) {
var lines = content;
if (typeof content === "string") {
lines = content.split("\n");
}
if (ignoreFirst) {
if (lines.length <= 1) {
return lines.join("\n");
}
return lines[0] + "\n" + indent2(lines.slice(1), spaces, false);
}
return lines.map(function(c) {
return " ".repeat(spaces) + c;
}).join("\n");
};
var parseTextFormat = function parseTextFormat2(text) {
var formats = {
strong: "**",
italic: "*",
underline: "_",
strikethrough: "~~"
};
return text.replace(/<\/?strong\>/gi, formats.strong).replace(/<\/?bold\>/gi, formats.strong).replace(/<\/?em\>/gi, formats.italic).replace(/<\/?italic\>/gi, formats.italic).replace(/<\/?u\>/gi, formats.underline).replace(/<\/?strike\>/gi, formats.strikethrough);
};
converters.h1 = generateHeader(1);
converters.h2 = generateHeader(2);
converters.h3 = generateHeader(3);
converters.h4 = generateHeader(4);
converters.h5 = generateHeader(5);
converters.h6 = generateHeader(6);
converters.blockquote = function(input, json2md2) {
return json2md2(input, "> ");
};
converters.img = function(input, json2md2) {
debugger;
if (Array.isArray(input)) {
return json2md2(input, "", "img");
}
if (typeof input === "string") {
return converters.img({ source: input, title: "", alt: "" });
}
input.title = input.title || "";
input.alt = input.alt || "";
return "';
};
converters.ul = function(input, json2md2) {
var c = "";
for (var i = 0; i < input.length; ++i) {
var marker = "";
var type2 = Object.keys(input[i])[0];
if (type2 !== "ul" && type2 !== "ol" && type2 !== "taskLists") {
marker += "\n - ";
}
c += marker + parseTextFormat(indent(json2md2(input[i]), 4, true));
}
return c;
};
converters.ol = function(input, json2md2) {
var c = "";
var jumpCount = 0;
for (var i = 0; i < input.length; ++i) {
var marker = "";
var type2 = Object.keys(input[i])[0];
if (type2 !== "ul" && type2 !== "ol" && type2 !== "taskLists") {
marker = "\n " + (i + 1 - jumpCount) + ". ";
} else {
jumpCount++;
}
c += marker + parseTextFormat(indent(json2md2(input[i]), 4, true));
}
return c;
};
converters.taskLists = function(input, json2md2) {
var c = "";
for (var i = 0; i < input.length; ++i) {
var marker = "";
var type2 = Object.keys(input[i])[0];
if (type2 !== "ul" && type2 !== "ol" && type2 !== "taskLists") {
marker += input[i].isDone ? "\n - [x] " : "\n - [ ] ";
}
c += marker + parseTextFormat(indent(json2md2(input[i].title || input[i]), 4, true));
}
return c;
};
converters.code = function(input, json2md2) {
var c = "```" + (input.language || "") + "\n";
if (Array.isArray(input.content)) {
c += input.content.join("\n");
} else {
c += input.content;
}
c += "\n```";
return c;
};
converters.p = function(input, json2md2) {
return parseTextFormat(json2md2(input, "\n"));
};
converters.table = function(input, json2md2) {
var _PREFERRED_LENGTH_PER;
var ALIGNMENT = {
CENTER: "center",
RIGHT: "right",
LEFT: "left",
NONE: "none"
};
var PREFERRED_LENGTH_PER_ALIGNMENT = (_PREFERRED_LENGTH_PER = {}, _defineProperty(_PREFERRED_LENGTH_PER, ALIGNMENT.CENTER, 3), _defineProperty(_PREFERRED_LENGTH_PER, ALIGNMENT.RIGHT, 2), _defineProperty(_PREFERRED_LENGTH_PER, ALIGNMENT.LEFT, 2), _defineProperty(_PREFERRED_LENGTH_PER, ALIGNMENT.NONE, 1), _PREFERRED_LENGTH_PER);
if ((typeof input === "undefined" ? "undefined" : _typeof(input)) !== "object" || !input.hasOwnProperty("headers") || !input.hasOwnProperty("rows")) {
return "";
}
var alignment = input.headers.map(function(_, index) {
return input.aligns && input.aligns[index] ? input.aligns[index] : ALIGNMENT.NONE;
});
var preferred_lengths = input.headers.map(function(header2, index) {
return Math.max(PREFERRED_LENGTH_PER_ALIGNMENT[alignment[index]], header2.length - 2);
});
if (input.pretty === true) {
input.rows.forEach(function(row) {
(Array.isArray(row) ? row : input.headers.map(function(col_id) {
return row[col_id];
})).forEach(function(cell, index) {
preferred_lengths[index] = Math.max(preferred_lengths[index], cell.length - 2);
});
});
}
var fill_right = function fill_right2(diff, header2) {
return " ".repeat(diff) + header2;
};
var fill_left = function fill_left2(diff, header2) {
return header2 + " ".repeat(diff);
};
var fill_center = function fill_center2(diff, header2) {
return " ".repeat(Math.floor(diff / 2)) + header2 + " ".repeat(Math.ceil(diff / 2));
};
var fill_th = function fill_th2(header2, index) {
var diff = preferred_lengths[index] + 2 - header2.length;
switch (alignment[index]) {
case ALIGNMENT.RIGHT:
return fill_right(diff, header2);
case ALIGNMENT.LEFT:
return fill_left(diff, header2);
case ALIGNMENT.CENTER:
case ALIGNMENT.NONE:
default:
return fill_center(diff, header2);
}
};
var fill_td = function fill_td2(header2, index) {
var diff = preferred_lengths[index] + 2 - header2.length;
switch (alignment[index]) {
case ALIGNMENT.RIGHT:
return fill_right(diff, header2);
case ALIGNMENT.NONE:
case ALIGNMENT.LEFT:
return fill_left(diff, header2);
case ALIGNMENT.CENTER:
default:
return fill_center(diff, header2);
}
};
var column_names = input.headers.map(fill_th);
var header = "| " + column_names.join(" | ") + " |";
var spaces = "| " + input.headers.map(function(_, index) {
var inner = "-".repeat(preferred_lengths[index]);
switch (alignment[index]) {
case ALIGNMENT.CENTER:
return ":" + inner + ":";
case ALIGNMENT.RIGHT:
return "-" + inner + ":";
case ALIGNMENT.LEFT:
return ":" + inner + "-";
case ALIGNMENT.NONE:
default:
return "-" + inner + "-";
}
}).join(" | ") + " |";
var fill_tbody_cell = function fill_tbody_cell2(cell, index) {
if (input.pretty !== true) return cell;
return fill_td(cell, index);
};
var data = input.rows.map(function(row) {
return "| " + (Array.isArray(row) ? row : input.headers.map(function(col_id) {
return row[col_id];
})).map(function(cell) {
return json2md2(cell);
}).map(function(cell) {
return parseTextFormat(cell);
}).map(function(cell) {
return cell.replace(/([^\\])\|/, "$1\\|");
}).map(function(cell) {
return cell.trim();
}).map(fill_tbody_cell).join(" | ") + " |";
}).join("\n");
return [header, spaces, data].join("\n");
};
converters.link = function(input, json2md2) {
if (Array.isArray(input)) {
return json2md2(input, "", "link");
}
if (typeof input === "string") {
return converters.link({ source: input, title: "" });
}
return "[" + input.title + "](" + input.source + ")";
};
converters.hr = function(input, json2md2) {
return "---";
};
}
});
// node_modules/indento/lib/index.js
var require_lib = __commonJS({
"node_modules/indento/lib/index.js"(exports, module) {
"use strict";
init_esm_shims();
function indento(input, width, char) {
char = typeof char !== "string" ? " " : char;
return String(input).replace(/^/gm, char.repeat(width));
}
module.exports = indento;
}
});
// node_modules/json2md/lib/index.js
var require_lib2 = __commonJS({
"node_modules/json2md/lib/index.js"(exports, module) {
"use strict";
init_esm_shims();
var converters = require_converters();
var indento = require_lib();
function json2md2(data, prefix, _type) {
prefix = prefix || "";
if (typeof data === "string" || typeof data === "number") {
return indento(data, 1, prefix);
}
var content = [];
if (Array.isArray(data)) {
for (var i = 0; i < data.length; ++i) {
content.push(indento(json2md2(data[i], "", _type), 1, prefix));
}
return content.join("\n");
} else if (_type) {
var mdText = "";
var func = converters[_type || type];
if (typeof func === "function") {
mdText += indento(func(_type ? data : data[type], json2md2), 1, prefix) + "\n";
} else {
throw new Error("There is no such converter: " + type);
}
return mdText;
} else {
var _mdText = "";
Object.keys(data).forEach(function(type2, index, array) {
var func2 = converters[_type || type2];
if (typeof func2 === "function") {
_mdText += indento(func2(_type ? data : data[type2], json2md2), 1, prefix) + "\n";
} else {
throw new Error("There is no such converter: " + type2);
}
});
return _mdText;
}
}
json2md2.async = function(data, prefix, _type) {
return Promise.resolve().then(function() {
prefix = prefix || "";
if (typeof data === "string" || typeof data === "number") {
return indento(data, 1, prefix);
}
var content = [];
if (Array.isArray(data)) {
var promises = data.map(function(d, index) {
return Promise.resolve().then(function() {
return json2md2.async(d, "", _type);
}).then(function(result) {
return indento(result, 1, prefix);
}).then(function(result) {
content[index] = result;
});
});
return Promise.all(promises).then(function() {
return content.join("\n");
});
} else {
var _type2 = Object.keys(data)[0], func = converters[_type || _type2];
if (typeof func === "function") {
return Promise.resolve().then(function() {
return func(_type ? data : data[_type2], json2md2);
}).then(function(result) {
return indento(result, 1, prefix) + "\n";
});
}
throw new Error("There is no such converter: " + _type2);
}
});
};
json2md2.converters = converters;
module.exports = json2md2;
}
});
// src/index.ts
init_esm_shims();
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
// src/config.ts
init_esm_shims();
// src/tools/scrapeFocused.ts
init_esm_shims();
import * as prysm from "@pinkpixel/prysm-llm";
var scrapeFocused = {
name: "scrapeFocused",
description: "Fast web scraping optimized for speed (fewer scrolls, main content only)",
parameters: {
type: "object",
properties: {
url: {
type: "string",
description: "URL of the webpage to scrape"
},
maxScrolls: {
type: "number",
description: "Maximum number of scroll attempts (default: 5)"
},
scrollDelay: {
type: "number",
description: "Delay between scrolls in ms (default: 1000)"
},
pages: {
type: "number",
description: "Number of pages to scrape (if pagination is present)"
},
scrapeImages: {
type: "boolean",
description: "Whether to include images in the scrape result"
},
downloadImages: {
type: "boolean",
description: "Whether to download images locally"
},
maxImages: {
type: "number",
description: "Maximum number of images to extract"
},
minImageSize: {
type: "number",
description: "Minimum width/height for images in pixels"
},
output: {
type: "string",
description: "Output directory for general results"
},
imageOutput: {
type: "string",
description: "Output directory for downloaded images"
}
},
required: ["url"]
},
handler: async (params) => {
const {
url,
maxScrolls = 5,
scrollDelay = 1e3,
pages = 1,
scrapeImages = false,
downloadImages = false,
maxImages = 20,
minImageSize = 100,
output,
imageOutput
} = params;
try {
const options = {
maxScrolls,
scrollDelay,
pages,
focused: true,
// Use focused mode for faster extraction
standard: false,
deep: false,
scrapeImages: scrapeImages || downloadImages,
downloadImages,
maxImages,
minImageSize,
output: output || config.serverOptions.defaultOutputDir,
// Use configured default if not provided
imageOutput: imageOutput || config.serverOptions.defaultImageOutputDir
// Use configured default if not provided
};
const result = await prysm.scrape(url, options);
if (result.content && result.content.length > 0) {
if (result.content.length > 10) {
result.content = result.content.slice(0, 10);
result.content.push("(Content truncated due to size limitations)");
}
result.content = result.content.map((section) => {
if (section.length > 3e3) {
return section.substring(0, 3e3) + "... (truncated)";
}
return section;
});
}
if (result.images && result.images.length > 10) {
result.images = result.images.slice(0, 10);
}
return result;
} catch (error) {
console.error(`Error scraping ${url}:`, error);
return {
title: "Scraping Error",
content: [`Failed to scrape ${url}: ${error instanceof Error ? error.message : String(error)}`],
images: [],
metadata: { error: true },
url,
structureType: "error",
paginationType: "none",
extractionMethod: "none"
};
}
}
};
// src/tools/scrapeBalanced.ts
init_esm_shims();
import * as prysm2 from "@pinkpixel/prysm-llm";
var scrapeBalanced = {
name: "scrapeBalanced",
description: "Balanced web scraping approach with good coverage and reasonable speed",
parameters: {
type: "object",
properties: {
url: {
type: "string",
description: "URL of the webpage to scrape"
},
maxScrolls: {
type: "number",
description: "Maximum number of scroll attempts (default: 10)"
},
scrollDelay: {
type: "number",
description: "Delay between scrolls in ms (default: 2000)"
},
pages: {
type: "number",
description: "Number of pages to scrape (if pagination is present)"
},
scrapeImages: {
type: "boolean",
description: "Whether to include images in the scrape result"
},
downloadImages: {
type: "boolean",
description: "Whether to download images locally"
},
maxImages: {
type: "number",
description: "Maximum number of images to extract"
},
minImageSize: {
type: "number",
description: "Minimum width/height for images in pixels"
},
timeout: {
type: "number",
description: "Maximum time in ms for the scrape operation (default: 30000)"
},
output: {
type: "string",
description: "Output directory for general results"
},
imageOutput: {
type: "string",
description: "Output directory for downloaded images"
}
},
required: ["url"]
},
handler: async (params) => {
const {
url,
maxScrolls = 10,
scrollDelay = 2e3,
pages = 1,
scrapeImages = false,
downloadImages = false,
maxImages = 50,
minImageSize = 100,
timeout = 3e4,
output,
imageOutput
} = params;
try {
const options = {
maxScrolls,
scrollDelay,
pages,
focused: false,
standard: true,
// Use standard mode for balanced extraction
deep: false,
scrapeImages: scrapeImages || downloadImages,
downloadImages,
maxImages,
minImageSize,
timeout,
// Add timeout option
output: output || config.serverOptions.defaultOutputDir,
// Use configured default if not provided
imageOutput: imageOutput || config.serverOptions.defaultImageOutputDir
// Use configured default if not provided
};
const scrapePromise = prysm2.scrape(url, options);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error(`Scraping timed out after ${timeout}ms`)), timeout);
});
const result = await Promise.race([scrapePromise, timeoutPromise]);
if (result.content && result.content.length > 0) {
if (result.content.length > 20) {
result.content = result.content.slice(0, 20);
result.content.push("(Content truncated due to size limitations)");
}
result.content = result.content.map((section) => {
if (section.length > 5e3) {
return section.substring(0, 5e3) + "... (truncated)";
}
return section;
});
}
if (result.images && result.images.length > 20) {
result.images = result.images.slice(0, 20);
}
return result;
} catch (error) {
console.error(`Error scraping ${url}:`, error);
return {
title: "Scraping Error",
content: [`Failed to scrape ${url}: ${error instanceof Error ? error.message : String(error)}`],
images: [],
metadata: { error: true },
url,
structureType: "error",
paginationType: "none",
extractionMethod: "none"
};
}
}
};
// src/tools/scrapeDeep.ts
init_esm_shims();
import * as prysm3 from "@pinkpixel/prysm-llm";
var scrapeDeep = {
name: "scrapeDeep",
description: "Maximum extraction web scraping (slower but thorough)",
parameters: {
type: "object",
properties: {
url: {
type: "string",
description: "URL of the webpage to scrape"
},
maxScrolls: {
type: "number",
description: "Maximum number of scroll attempts (default: 20)"
},
scrollDelay: {
type: "number",
description: "Delay between scrolls in ms (default: 3000)"
},
pages: {
type: "number",
description: "Number of pages to scrape (if pagination is present)"
},
scrapeImages: {
type: "boolean",
description: "Whether to include images in the scrape result"
},
downloadImages: {
type: "boolean",
description: "Whether to download images locally"
},
maxImages: {
type: "number",
description: "Maximum number of images to extract"
},
minImageSize: {
type: "number",
description: "Minimum width/height for images in pixels"
},
output: {
type: "string",
description: "Output directory for general results"
},
imageOutput: {
type: "string",
description: "Output directory for downloaded images"
}
},
required: ["url"]
},
handler: async (params) => {
const {
url,
maxScrolls = 20,
scrollDelay = 3e3,
pages = 1,
scrapeImages = false,
downloadImages = false,
maxImages = 100,
minImageSize = 100,
output,
imageOutput
} = params;
try {
const options = {
maxScrolls,
scrollDelay,
pages,
focused: false,
standard: false,
deep: true,
// Use deep mode for thorough extraction
scrapeImages: scrapeImages || downloadImages,
downloadImages,
maxImages,
minImageSize,
output: output || config.serverOptions.defaultOutputDir,
// Use configured default if not provided
imageOutput: imageOutput || config.serverOptions.defaultImageOutputDir
// Use configured default if not provided
};
const result = await prysm3.scrape(url, options);
if (result.content && result.content.length > 0) {
if (result.content.length > 30) {
result.content = result.content.slice(0, 30);
result.content.push("(Content truncated due to size limitations)");
}
result.content = result.content.map((section) => {
if (section.length > 1e4) {
return section.substring(0, 1e4) + "... (truncated)";
}
return section;
});
}
if (result.images && result.images.length > 30) {
result.images = result.images.slice(0, 30);
}
return result;
} catch (error) {
console.error(`Error scraping ${url}:`, error);
return {
title: "Scraping Error",
content: [`Failed to scrape ${url}: ${error instanceof Error ? error.message : String(error)}`],
images: [],
metadata: { error: true },
url,
structureType: "error",
paginationType: "none",
extractionMethod: "none"
};
}
}
};
// src/tools/formatResult.ts
init_esm_shims();
import fs from "node:fs/promises";
import path from "node:path";
var import_json2md = __toESM(require_lib2(), 1);
function formatAsMarkdown(data, includeImages) {
const mdData = [];
mdData.push({ h1: data.title });
if (data.metadata && Object.keys(data.metadata).length > 0) {
mdData.push({ h2: "Metadata" });
const metadataItems = [];
for (const [key, value] of Object.entries(data.metadata)) {
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
metadataItems.push(`**${key}**: ${value}`);
}
}
if (metadataItems.length > 0) {
mdData.push({ ul: metadataItems });
}
}
mdData.push({ h2: "Content" });
if (data.content && data.content.length > 0) {
data.content.forEach((section) => {
mdData.push({ p: section });
});
}
if (includeImages && data.images && data.images.length > 0) {
mdData.push({ h2: "Images" });
data.images.forEach((image) => {
const alt = image.alt || "Image";
mdData.push({ img: {
title: alt,
source: image.url
} });
});
}
return (0, import_json2md.default)(mdData);
}
function formatAsHTML(data, includeImages) {
let html = `<h1>${data.title}</h1>`;
if (data.metadata && Object.keys(data.metadata).length > 0) {
html += "<h2>Metadata</h2><ul>";
for (const [key, value] of Object.entries(data.metadata)) {
if (typeof value === "string" || typeof value === "number") {
html += `<li><strong>${key}</strong>: ${value}</li>`;
}
}
html += "</ul>";
}
html += "<h2>Content</h2>";
for (const section of data.content) {
html += `<p>${section}</p>`;
}
if (includeImages && data.images && data.images.length > 0) {
html += "<h2>Images</h2>";
for (const image of data.images) {
const alt = image.alt || "Image";
html += `<img src="${image.url}" alt="${alt}" />`;
}
}
return html;
}
function generateDefaultFilename(data, format) {
const safeTitle = data.title ? data.title.replace(/[^a-z0-9]/gi, "_").toLowerCase().substring(0, 50) : "scrape_result";
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.-]/g, "_");
const extension = format === "markdown" ? ".md" : format === "html" ? ".html" : ".json";
return `${safeTitle}_${timestamp}${extension}`;
}
async function saveToFile(content, outputPath, format, data) {
try {
let resolvedPath = outputPath;
if (!path.isAbsolute(outputPath)) {
resolvedPath = path.join(config.serverOptions.defaultOutputDir, outputPath);
}
const stats = await fs.stat(resolvedPath).catch(() => null);
if (stats && stats.isDirectory()) {
resolvedPath = path.join(resolvedPath, generateDefaultFilename(data, format));
} else if (!path.extname(resolvedPath)) {
const extension = format === "markdown" ? ".md" : format === "html" ? ".html" : ".json";
resolvedPath = `${resolvedPath}${extension}`;
}
const dir = path.dirname(resolvedPath);
await fs.mkdir(dir, { recursive: true });
await fs.writeFile(resolvedPath, content, "utf-8");
return resolvedPath;
} catch (error) {
throw new Error(`Failed to save formatted result: ${error instanceof Error ? error.message : String(error)}`);
}
}
var formatResult = {
name: "formatResult",
description: "Format scraped data into different structured formats (markdown, HTML, JSON)",
parameters: {
type: "object",
properties: {
data: {
type: "object",
description: "The scraped data to format"
},
format: {
type: "string",
enum: ["markdown", "html", "json"],
description: "The format to convert the data to"
},
includeImages: {
type: "boolean",
description: "Whether to include images in the formatted output (default: true)"
},
output: {
type: "string",
description: "File path to save the formatted result. If not provided, will use the default directory."
}
},
required: ["data", "format"]
},
handler: async (params) => {
const { data, format, includeImages = true, output } = params;
try {
let formatted;
switch (format) {
case "markdown":
formatted = formatAsMarkdown(data, includeImages);
break;
case "html":
formatted = formatAsHTML(data, includeImages);
break;
case "json":
formatted = JSON.stringify(data, null, 2);
break;
default:
throw new Error(`Unsupported format: ${format}`);
}
let savedTo;
if (output) {
savedTo = await saveToFile(formatted, output, format, data);
} else {
const defaultFilename = generateDefaultFilename(data, format);
savedTo = await saveToFile(formatted, defaultFilename, format, data);
}
if (format === "json" && formatted.length > 1e4) {
formatted = formatted.substring(0, 1e4) + "... (truncated for display, full content saved to file)";
}
return {
formatted,
format,
...savedTo && { savedTo }
};
} catch (error) {
throw new Error(`Failed to format result: ${error instanceof Error ? error.message : String(error)}`);
}
}
};
// src/config.ts
import path2 from "node:path";
import os from "node:os";
var defaultOutputDir = process.env.PRYSM_OUTPUT_DIR || path2.join(os.homedir(), "prysm-mcp", "output");
var defaultImageOutputDir = process.env.PRYSM_IMAGE_OUTPUT_DIR || path2.join(defaultOutputDir, "images");
console.debug(`Setting default output directory to: ${defaultOutputDir}`);
console.debug(`Setting default image output directory to: ${defaultImageOutputDir}`);
var config = {
name: "prysm-mcp-server",
description: "Prysm web scraper MCP server for AI assistants",
version: "1.1.2",
model: {
provider: "Pink Pixel",
name: "Prysm",
version: "1.1.2"
},
tools: [
scrapeFocused,
scrapeBalanced,
scrapeDeep,
// analyzeUrl,
formatResult
],
serverOptions: {
defaultOutputDir,
defaultImageOutputDir
}
};
// src/index.ts
var server = new Server(
{
name: config.name,
version: config.version
},
{
capabilities: {
tools: {}
}
}
);
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: config.tools.map((tool) => ({
name: tool.name,
description: tool.description,
inputSchema: tool.parameters
}))
};
});
server.setRequestHandler(
CallToolRequestSchema,
async (request) => {
try {
if (!request.params.arguments) {
throw new Error("Arguments are required");
}
const tool = config.tools.find((t) => t.name === request.params.name);
if (!tool) {
throw new Error(`Unknown tool: ${request.params.name}`);
}
const result = await tool.handler(request.params.arguments);
return {
content: [{ type: "text", text: JSON.stringify(result) }]
};
} catch (error) {
console.error("Error executing tool:", error);
return {
content: [
{
type: "text",
text: JSON.stringify({
error: error instanceof Error ? error.message : String(error)
})
}
]
};
}
}
);
async function startServer() {
try {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error(`Prysm MCP Server initialized and ready`);
console.error(`Available tools: ${config.tools.map((t) => t.name).join(", ")}`);
const signals = ["SIGINT", "SIGTERM"];
for (const signal of signals) {
process.on(signal, async () => {
console.error(`
${signal} received, shutting down server...`);
process.exit(0);
});
}
} catch (error) {
console.error("Failed to start MCP server:", error);
if (error instanceof Error) {
console.error("Error details:", error.message);
console.error("Stack trace:", error.stack);
}
console.error("Configuration:", JSON.stringify({
toolCount: config.tools.length,
toolNames: config.tools.map((t) => t.name)
}, null, 2));
process.exit(1);
}
}
startServer();
//# sourceMappingURL=index.js.map