MCP Research Friend Server
ทางการเครื่องมือวิจัย รวมถึงที่เก็บเอกสารที่ใช้ Sqlite เป็นฐานข้อมูล
เอกสาร
Research Friend
ผู้ช่วยที่เป็นมิตรสำหรับผู้ช่วย AI ที่ต้องการค้นหาข้อมูลบนเว็บและจัดการคลังเอกสารวิจัยในเครื่อง
Research Friend คือเซิร์ฟเวอร์ MCP ที่มอบความสามารถในการดึงหน้าเว็บและค้นหาอินเทอร์เน็ตให้กับเครื่องมือ AI ของคุณ โดยใช้เบราว์เซอร์จริงเบื้องหลัง จึงทำงานได้แม้กับเว็บไซต์สมัยใหม่ที่พึ่งพา JavaScript อย่างมาก นอกจากนี้ยังมี "คลังเอกสาร" ในเครื่องสำหรับจัดเก็บเอกสาร แยกข้อความ และค้นหาทั่วทั้งคลังของคุณ
เพื่อใช้ประโยชน์จากฟีเจอร์ทั้งหมด คุณจะต้องมีไคลเอนต์ MCP ที่รองรับ prompts (ทั่วไป) และ sampling (พบได้น้อยกว่า) เรากำลังพัฒนา Research Friend ควบคู่ไปกับ Chabeau ซึ่งรองรับทั้งสองอย่าง
ทำอะไรได้บ้าง?
- ดึงหน้าเว็บ ด้วยเบราว์เซอร์จริง (รวมถึงไซต์ที่ใช้ JS จำนวนมาก)
- ดึงไฟล์ PDF และแยกเนื้อหาข้อความ
- ค้นหาเว็บ ผ่าน DuckDuckGo หรือ Google
- ดูแลคลังเอกสารในเครื่อง สำหรับการค้นหา แสดงรายการ และแยกข้อมูล
เริ่มต้นใช้งาน
คุณต้องติดตั้ง Node.js เวอร์ชัน 20 หรือใหม่กว่าบนคอมพิวเตอร์ของคุณ
1. ติดตั้งส่วนประกอบที่จำเป็น
เปิดเทอร์มินัลในโฟลเดอร์นี้แล้วรัน:
npm install
2. ติดตั้งส่วนสนับสนุนเบราว์เซอร์
Research Friend ใช้ Playwright เพื่อควบคุมเว็บเบราว์เซอร์ หลังจากติดตั้งส่วนประกอบที่จำเป็นแล้ว คุณต้องติดตั้งเบราว์เซอร์:
npx playwright install chromium
คำสั่งนี้จะดาวน์โหลดสำเนาของ Chromium ที่ Playwright จะใช้ ซึ่งแยกจากเบราว์เซอร์ใดๆ ที่คุณติดตั้งไว้แล้ว
3. เริ่มเซิร์ฟเวอร์
node src/index.js
เซิร์ฟเวอร์สื่อสารผ่าน stdio (อินพุต/เอาต์พุตมาตรฐาน) ซึ่งเป็นวิธีที่ไคลเอนต์ MCP เชื่อมต่อ
การเพิ่มลงในไคลเอนต์ MCP ของคุณ
วิธีการเพิ่ม Research Friend ขึ้นอยู่กับไคลเอนต์ MCP ที่คุณใช้ นี่คือตัวอย่างทั่วไปของการกำหนดค่า:
[[mcp_servers]]
id = "research-friend"
command = "node"
args = ["/path/to/mcp-research-friend/src"]
transport = "stdio"
แทนที่ /path/to/mcp-research-friend ด้วยพาธจริงไปยังโฟลเดอร์นี้บนคอมพิวเตอร์ของคุณ
เครื่องมือ
เครื่องมือเว็บ
friendly_web_fetch
ดึงหน้าเว็บและส่งคืนเนื้อหา โดยค่าเริ่มต้นจะส่งคืนเป็น markdown พร้อมรักษาลิงก์ไว้ — เหมาะสำหรับ LLM ใช้ Readability เพื่อแยกเนื้อหาหลัก (ตัดการนำทาง โฆษณา ฯลฯ) สำหรับ PDF การแบ่งหน้า หรือการค้นหาภายในเนื้อหา ให้ใช้ friendly_web_extract แทน
พารามิเตอร์:
url(จำเป็น) - ที่อยู่เว็บที่จะดึงoutputFormat- รูปแบบผลลัพธ์:markdown(ค่าเริ่มต้น),text, หรือhtmlwaitMs- เวลาพิเศษที่รอหลังจากโหลดหน้าเสร็จ ในกรณีที่เนื้อหาปรากฏช้าtimeoutMs- ระยะเวลารอก่อนยกเลิก (ค่าเริ่มต้น: 15 วินาที)maxChars- จำนวนเนื้อหาสูงสุดที่จะส่งคืน (ค่าเริ่มต้น: 40,000 ตัวอักษร)includeHtml- ตั้งเป็นtrueเพื่อส่งคืน HTML ดิบพร้อมกับเนื้อหาด้วยheadless- ตั้งเป็นfalseเพื่อดูหน้าต่างเบราว์เซอร์ (มีประโยชน์สำหรับการดีบัก)
ส่งคืน:
url- URL ที่ถูกร้องขอfinalUrl- URL หลังจากการเปลี่ยนเส้นทางใดๆtitle- ชื่อหน้าcontent- เนื้อหาที่แยกออกมา (ในรูปแบบที่ร้องขอ)html- HTML ดิบ (เฉพาะเมื่อincludeHtmlเป็นจริง)meta- ข้อมูลเมตาของหน้า (คำอธิบาย, ผู้เขียน, เวลาที่เผยแพร่, ฯลฯ)fetchedAt- เวลาประทับ ISO เมื่อดึงหน้าtruncated- ว่าเนื้อหาถูกตัดทอนให้พอดีกับmaxCharsหรือไม่
friendly_search
ค้นหาเว็บและส่งคืนรายการผลลัพธ์
พารามิเตอร์:
query(จำเป็น) - สิ่งที่ต้องการค้นหาengine- เครื่องมือค้นหาที่จะใช้ (duckduckgoหรือgoogle)maxResults- จำนวนผลลัพธ์ที่จะส่งคืน (ค่าเริ่มต้น: 10, สูงสุด: 50)timeoutMs- ระยะเวลารอก่อนยกเลิก (ค่าเริ่มต้น: 15 วินาที)headless- ตั้งเป็นfalseเพื่อดูหน้าต่างเบราว์เซอร์
ส่งคืน:
query- คำค้นหาที่ใช้engine- เครื่องมือค้นหาที่ใช้results- อาร์เรย์ของผลลัพธ์ แต่ละรายการมีtitle,url, และsnippetsearchedAt- เวลาประทับ ISO เมื่อทำการค้นหาfallback_result_html- HTML ดิบของหน้า (รวมเฉพาะเมื่อไม่พบผลลัพธ์)debug_info- ข้อมูลการวินิจฉัยเกี่ยวกับความพยายามค้นหา
การจัดการ CAPTCHA:
หากตรวจพบ CAPTCHA ขณะทำงานในโหมด headless เครื่องมือจะลองใหม่โดยอัตโนมัติด้วยหน้าต่างเบราว์เซอร์ที่มองเห็นได้ ซึ่งเปิดโอกาสให้คุณแก้ CAPTCHA ด้วยตนเอง ฟิลด์ debug_info.retried ระบุว่ามีการใช้ทางเลือกสำรองนี้หรือไม่
friendly_web_extract
แยกเนื้อหาจาก URL ตรวจจับอัตโนมัติว่า URL ชี้ไปยัง PDF หรือหน้าเว็บ และจัดการแต่ละอย่างอย่างเหมาะสม
พารามิเตอร์:
url(จำเป็น) - URL ที่จะดึง (PDF หรือหน้าเว็บ)maxChars- จำนวนข้อความสูงสุดที่จะส่งคืน (ค่าเริ่มต้น: 40,000 ตัวอักษร)offset- ตำแหน่งตัวอักษรที่จะเริ่มต้น (ค่าเริ่มต้น: 0) ใช้เพื่อแบ่งหน้าผ่านเนื้อหาขนาดใหญ่search- ค้นหาวลีและส่งคืนรายการที่ตรงกันพร้อมบริบทรอบๆ แทนเนื้อหาทั้งหมดcontextChars- จำนวนตัวอักษรของบริบทรอบๆ แต่ละรายการที่ค้นหาพบ (ค่าเริ่มต้น: 200)waitMs- เวลาพิเศษที่รอหลังจากโหลดหน้า สำหรับเนื้อหาแบบไดนามิก (เฉพาะหน้าเว็บ)timeoutMs- ระยะเวลารอก่อนยกเลิก (ค่าเริ่มต้น: 15 วินาที, เฉพาะหน้าเว็บ)headless- ตั้งเป็นfalseเพื่อดูหน้าต่างเบราว์เซอร์ (เฉพาะหน้าเว็บ)
ส่งคืน (โหมดปกติ):
url- URL ที่ถูกร้องขอcontentType- อย่างใดอย่างหนึ่งระหว่างpdfหรือhtmltitle- ชื่อหน้า/เอกสารauthor- ผู้เขียน PDF (เฉพาะ PDF, หากมี)creationDate- เวลาที่สร้าง PDF (เฉพาะ PDF, หากมี)pageCount- จำนวนหน้า (เฉพาะ PDF)totalChars- จำนวนตัวอักษรทั้งหมด (ใช้กับoffsetเพื่อแบ่งหน้า)offset- ออฟเซ็ตที่ใช้content- เนื้อหาข้อความที่แยกออกมาfetchedAt- เวลาประทับ ISOtruncated- ว่ายังมีเนื้อหาเหลือหลังจากส่วนนี้หรือไม่
ส่งคืน (โหมดค้นหา):
url,contentType,title,totalChars,fetchedAt- เช่นเดียวกับข้างต้นsearch- วลีค้นหาที่ใช้matchCount- จำนวนรายการที่พบmatches- อาร์เรย์ของรายการที่ตรงกัน แต่ละรายการมีposition,context,prefix, และsuffix
friendly_web_ask
ดึง URL (PDF หรือหน้าเว็บ) และให้ LLM ตอบคำถามเกี่ยวกับเนื้อหานั้น ตรวจจับประเภทเนื้อหาอัตโนมัติ เอกสารถูกประมวลผลในบริบทแยกต่างหาก ทำให้การสนทนาหลักของคุณกระชับ
พารามิเตอร์:
url(จำเป็น) - URL ที่จะดึง (PDF หรือหน้าเว็บ)ask(จำเป็น) - คำถามหรือคำสั่งสำหรับ LLM (สรุป, แยกข้อมูล, ตอบคำถาม, ฯลฯ)askMaxInputTokens- โทเค็นอินพุตสูงสุดต่อการเรียก LLM (ค่าเริ่มต้น: 150,000)askMaxOutputTokens- โทเค็นเอาต์พุตสูงสุดต่อการเรียก LLM (ค่าเริ่มต้น: 4,096)askTimeout- หมดเวลาในหน่วยมิลลิวินาที (ค่าเริ่มต้น: 300,000 = 5 นาที)askSplitAndSynthesize- สำหรับเอกสารขนาดใหญ่: แบ่งเป็นส่วนๆ ประมวลผลแต่ละส่วน แล้วสังเคราะห์ผลลัพธ์ (ค่าเริ่มต้น: false) คำเตือน: ใช้โทเค็นจำนวนมากwaitMs- เวลาพิเศษที่รอหลังจากโหลดหน้า สำหรับเนื้อหาแบบไดนามิก (เฉพาะหน้าเว็บ)timeoutMs- ระยะเวลารอก่อนยกเลิก (ค่าเริ่มต้น: 15 วินาที, เฉพาะหน้าเว็บ)headless- ตั้งเป็นfalseเพื่อดูหน้าต่างเบราว์เซอร์ (เฉพาะหน้าเว็บ)
ส่งคืน:
url- URL ที่ถูกร้องขอcontentType- อย่างใดอย่างหนึ่งระหว่างpdfหรือhtmltitle- ชื่อหน้า/เอกสารtotalChars- จำนวนตัวอักษรทั้งหมดในเอกสารask- คำสั่งที่ให้ไว้answer- การตอบกลับของ LLMmodel- โมเดลที่สร้างการตอบกลับchunksProcessed- จำนวนส่วนที่ประมวลผล (1 สำหรับเอกสารขนาดเล็ก, มากกว่าเมื่อใช้askSplitAndSynthesize)fetchedAt- เวลาประทับ ISO
โหมดถาม ใช้การสุ่มตัวอย่าง MCP เพื่อให้ LLM ประมวลผลเอกสารด้วยคำสั่งใดๆ มีประโยชน์สำหรับ:
- เอกสารขนาดใหญ่ที่อาจล้นบริบท
- ลดต้นทุนโทเค็นในการสนทนาหลัก
เมื่อเปิดใช้งาน askSplitAndSynthesize เอกสารที่เกิน askMaxInputTokens จะถูกแบ่งเป็นส่วนที่ซ้อนทับกันโดยอัตโนมัติ แต่ละส่วนถูกประมวลผลแยกกัน และผลลัพธ์จะถูกสังเคราะห์เป็นคำตอบเดียวที่สอดคล้องกัน การตอบกลับสุดท้ายจะให้ในภาษาเดียวกับคำขอของคุณ โดยไม่คำนึงถึงภาษาของเอกสาร
คลังเอกสาร
คลังเอกสารคือคลังเอกสารในเครื่องที่ค้นหาได้ รองรับ PDF, ไฟล์ HTML, และข้อความธรรมดา (Markdown/TXT) เมื่อคุณเพิ่มเอกสาร Research Friend จะจัดเก็บไฟล์ต้นฉบับ แยกข้อความ (สำหรับ PDF/HTML) และบันทึกข้อมูลเมตาในฐานข้อมูลในเครื่อง การค้นหาใช้ ripgrep เบื้องหลังเพื่อการจับคู่ที่รวดเร็วและรับรู้วลี
ตำแหน่งคลังเอกสาร
คลังเอกสารอยู่ภายใต้ ~/.research-friend/:
inbox/- วางไฟล์ที่นี่เพื่อประมวลผลstore/- การจัดเก็บเอกสารที่เป็นระเบียบและข้อความที่แยกออกมาstash.db- ฐานข้อมูลเมตาดาต้า
ประเภทไฟล์ที่รองรับ
- PDF:
.pdf(แยกข้อความ) - HTML:
.html,.htm(แยกข้อความ) - Markdown:
.md,.markdown(จัดเก็บเป็นข้อความธรรมดา) - ข้อความ:
.txt(จัดเก็บเป็นข้อความธรรมดา)
เครื่องมือคลังเอกสาร
stash_open_inbox
เปิดโฟลเดอร์กล่องขาเข้าของคลังเอกสารในตัวจัดการไฟล์ของคุณเพื่อการลากและวางที่ง่ายขึ้น
ส่งคืน:
opened- ว่าคำขอเปิดโฟลเดอร์ถูกส่งหรือไม่inboxPath- พาธสัมบูรณ์ไปยังกล่องขาเข้าcommand- คำสั่ง OS ที่ใช้args- อาร์กิวเมนต์คำสั่งที่ใช้
stash_process_inbox
ประมวลผลไฟล์ใน inbox/ จัดประเภทเป็นหัวข้อ แยกข้อความ และจัดเก็บผลลัพธ์
สำหรับเอกสารขนาดยาว การจัดประเภทใช้ส่วนที่สุ่มตัวอย่าง (เริ่มต้น/กลาง/สิ้นสุด บวกกับส่วนสุ่มอีกสองสามส่วน) เพื่อปรับปรุงความแม่นยำของหัวข้อ
ส่งคืน:
processed- อาร์เรย์ของชื่อไฟล์ที่ประมวลผลสำเร็จerrors- ข้อผิดพลาดใดๆ ที่พบdocuments- อาร์เรย์ของระเบียนเอกสารที่สร้างขึ้น
reindex_stash
สร้างบทสรุปใหม่ จัดสรรหัวข้อใหม่ และอัปเดตข้อมูลเมตาของคลังสำหรับเอกสารในคลัง หากละเว้น ids หรือเว้นว่าง เอกสารทั้งหมดจะถูกทำดัชนีใหม่
พารามิเตอร์:
ids- ID เอกสารที่จะทำดัชนีใหม่ (ไม่บังคับ)
ส่งคืน:
reindexed- ID เอกสารที่ทำดัชนีใหม่errors- ข้อผิดพลาดใดๆ ที่พบdocuments- อาร์เรย์ของระเบียนเอกสารที่อัปเดต
stash_list
แสดงรายการเอกสารในคลัง
พารามิเตอร์:
topic- กรองตามหัวข้อ (ไม่บังคับ)limit- ผลลัพธ์สูงสุด (ค่าเริ่มต้น: 50)offset- ออฟเซ็ตการแบ่งหน้า (ค่าเริ่มต้น: 0)
ส่งคืน:
type-allหรือtopictotalDocuments- เอกสารทั้งหมด (เฉพาะเมื่อtypeเป็นall)count- ผลลัพธ์ที่ส่งคืนหลังการแบ่งหน้าoffset- ออฟเซ็ตการแบ่งหน้าที่ใช้limit- ขีดจำกัดการแบ่งหน้าที่ใช้topics- สรุปหัวข้อที่รู้จักและจำนวนเอกสารdocuments- รายการเอกสารพร้อมข้อมูลเมตา (รวมisPrimaryเมื่อแสดงรายการหัวข้อ)
stash_search
ค้นหาชื่อไฟล์และเนื้อหาทั่วทั้งคลัง คำค้นหาทั้งหมดต้องมีอยู่ (ตรรกะ AND) การจับคู่ชื่อไฟล์จะแสดงก่อน ใช้เครื่องหมายคำพูดสำหรับวลีที่ตรงทั้งหมด
พารามิเตอร์:
query(จำเป็น) - คำค้นหา ใช้เครื่องหมายคำพูดสำหรับวลี:"sparkling wine"topic- กรองตามหัวข้อ (ไม่บังคับ)ids- กรองตาม ID เอกสารเฉพาะ (ไม่บังคับ)limit- เอกสารสูงสุดที่จะส่งคืน (ค่าเริ่มต้น: 20)offset- ออฟเซ็ตการแบ่งหน้า (ค่าเริ่มต้น: 0)maxMatchesPerDoc- การจับคู่สูงสุดต่อเอกสาร (ค่าเริ่มต้น: 50)context- บรรทัดของบริบทรอบๆ แต่ละการจับคู่ (ค่าเริ่มต้น: 1, สูงสุด: 5) ควบคุมทั้งความใกล้เคียงที่คำต้องปรากฏเพื่อให้ตรงกัน และจำนวนข้อความรอบๆ ที่ส่งคืน ผลลัพธ์:totalMatches- จำนวนเอกสารทั้งหมดที่ตรงกันก่อนการแบ่งหน้าcount- ผลลัพธ์ที่ส่งกลับหลังการแบ่งหน้าresults- อาร์เรย์ของเอกสาร แต่ละรายการประกอบด้วย:id,filename,fileType,summary,charCount,createdAtmatchType-filename,content, หรือfilename+contentmatches- อาร์เรย์ของ{ line, context }สำหรับแต่ละตำแหน่งที่ตรงกัน
ใช้ค่า line ร่วมกับ stash_extract เพื่อข้ามไปยังตำแหน่งที่ตรงกันโดยตรง
stash_extract
แยกเนื้อหาจากเอกสารที่เก็บไว้เพื่ออ่าน ใช้หมายเลขบรรทัดจากผลลัพธ์ stash_search เพื่อข้ามไปยังตำแหน่งที่ตรงกันโดยตรง
พารามิเตอร์:
id(จำเป็น) - รหัสเอกสารจากstash_list/stash_searchmaxChars- จำนวนข้อความสูงสุดที่จะส่งกลับ (ค่าเริ่มต้น: 40,000 ตัวอักษร)offset- ตำแหน่งตัวอักษรที่จะเริ่มต้น (ใช้ร่วมกับlineไม่ได้)line- หมายเลขบรรทัดที่จะเริ่มต้น (ใช้ร่วมกับoffsetไม่ได้)
ผลลัพธ์:
id,filename,fileType,summary- ข้อมูลเมตาของเอกสารtotalChars- จำนวนตัวอักษรทั้งหมดในเอกสารoffset- ออฟเซ็ตตัวอักษร (รวมอยู่เมื่อใช้lineสำหรับการอ้างอิง)line- หมายเลขบรรทัด (เฉพาะเมื่อใช้พารามิเตอร์line)content- เนื้อหาข้อความที่แยกออกมาtruncated- ระบุว่ายังมีเนื้อหาเหลืออยู่หลังจากส่วนนี้หรือไม่
stash_ask
ให้ LLM ตอบคำถามเกี่ยวกับเอกสารที่เก็บไว้ เอกสารถูกประมวลผลในบริบทที่แยกต่างหาก ทำให้การสนทนาหลักของคุณกระชับ
พารามิเตอร์:
id(จำเป็น) - รหัสเอกสารจากstash_list/stash_searchask(จำเป็น) - คำถามหรือคำสั่งสำหรับ LLMaskMaxInputTokens- โทเค็นอินพุตสูงสุดต่อการเรียก LLM (ค่าเริ่มต้น: 150,000)askMaxOutputTokens- โทเค็นเอาต์พุตสูงสุดต่อการเรียก LLM (ค่าเริ่มต้น: 4,096)askTimeout- หมดเวลาในหน่วยมิลลิวินาที (ค่าเริ่มต้น: 300,000 = 5 นาที)askSplitAndSynthesize- สำหรับเอกสารขนาดใหญ่: แบ่งเป็นส่วนๆ ประมวลผลแต่ละส่วน แล้วสังเคราะห์ผลลัพธ์ (ค่าเริ่มต้น: false)
ผลลัพธ์:
id,filename,fileType,summary- ข้อมูลเมตาของเอกสารtotalChars- จำนวนตัวอักษรทั้งหมดในเอกสารask- คำสั่งที่ได้รับanswer- การตอบกลับของ LLMmodel- โมเดลที่สร้างการตอบกลับchunksProcessed- จำนวนส่วนที่ประมวลผล
ขั้นตอนการทำงานทั่วไป
- วางไฟล์ลงใน
~/.research-friend/inbox/ - รัน
stash_process_inbox - ใช้
stash_listเพื่อเรียกดูหัวข้อ - ใช้
stash_searchเพื่อค้นหาเอกสารที่เกี่ยวข้อง - ใช้
stash_extractเพื่ออ่านเอกสารที่ต้องการ หรือstash_askเพื่อถามคำถามเกี่ยวกับเอกสารนั้น
การแก้ไขปัญหา
ข้อผิดพลาด "Browser closed unexpectedly" หรือคล้ายกัน
ลองติดตั้งเบราว์เซอร์ใหม่:
npx playwright install chromium --force
บน Linux คุณอาจต้องใช้ dependencies ของระบบด้วย:
npx playwright install-deps chromium
เซิร์ฟเวอร์ไม่เริ่มทำงาน
ตรวจสอบให้แน่ใจว่าคุณใช้ Node.js เวอร์ชัน 20 หรือใหม่กว่า:
node --version
หากเวอร์ชันของคุณเก่ากว่า ไปที่ nodejs.org เพื่อดาวน์โหลดเวอร์ชันใหม่กว่า
ใบอนุญาต
MIT