"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
const zod_1 = require("zod");
const pdfLoader_1 = require("./pdfLoader");
const vectorStore_1 = require("./vectorStore");
const server = new mcp_js_1.McpServer({
name: "antigravity-pdf-mcp",
version: "1.0.0"
});
const CHUNK_SIZE = 1000;
const CHUNK_OVERLAP = 200;
const TOP_K = 5;
// Helper to check if we are using embeddings
const usingEmbeddings = !!process.env.OPENAI_API_KEY;
server.tool("ingest_pdf", "Ingest a PDF file into the knowledge base. This clears the previous knowledge base.", {
path: zod_1.z.string().describe("Absolute path to the PDF file to ingest")
}, async ({ path }) => {
try {
const text = await (0, pdfLoader_1.extractTextFromPdf)(path);
const chunks = (0, pdfLoader_1.chunkText)(text, CHUNK_SIZE, CHUNK_OVERLAP);
// reset store
(0, vectorStore_1.clearStore)();
(0, vectorStore_1.addChunks)(chunks);
if (usingEmbeddings) {
const apiKey = process.env.OPENAI_API_KEY;
const texts = chunks.map((c) => c.text);
// Embed in batches if needed, but for now simple batch
const embeddings = await (0, vectorStore_1.embedTextsOpenAI)(texts, apiKey);
embeddings.forEach((e, i) => (vectorStore_1.store[i].embedding = e));
}
else {
// compute tf-idf vectors
(0, vectorStore_1.computeTfIdfVectors)(vectorStore_1.store);
}
return {
content: [
{
type: "text",
text: `Successfully ingested PDF. Total chunks: ${chunks.length}. Using embeddings: ${usingEmbeddings}`
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: `Error ingesting PDF: ${error.message}`
}
],
isError: true
};
}
});
server.tool("query_knowledge_base", "Query the ingested knowledge base for relevant information.", {
query: zod_1.z.string().describe("The query string to search for")
}, async ({ query }) => {
try {
if ((0, vectorStore_1.getStoreSize)() === 0) {
return {
content: [
{
type: "text",
text: "Knowledge base is empty. Please ingest a PDF first."
}
],
isError: true
};
}
let passages;
if (usingEmbeddings) {
const apiKey = process.env.OPENAI_API_KEY;
const embed = (await (0, vectorStore_1.embedTextsOpenAI)([query], apiKey))[0];
const hits = (0, vectorStore_1.searchByEmbedding)(embed, TOP_K);
passages = hits.map((h) => ({ id: h.c.id, text: h.c.text, score: h.score }));
}
else {
const hits = (0, vectorStore_1.searchByTfIdf)(query, TOP_K);
passages = hits.map((h) => ({ id: h.c.id, text: h.c.text, score: h.score }));
}
const answer = passages.map((p) => `[Score: ${p.score.toFixed(2)}] ${p.text.trim()}`).join('\n\n---\n\n');
return {
content: [
{
type: "text",
text: answer || "No relevant matches found."
}
]
};
}
catch (error) {
return {
content: [
{
type: "text",
text: `Error querying knowledge base: ${error.message}`
}
],
isError: true
};
}
});
async function main() {
const transport = new stdio_js_1.StdioServerTransport();
await server.connect(transport);
console.error("Antigravity PDF MCP Server running on Stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});