search-mcp-server

kim57uak/search-mcp-server

3.2

If you are the rightful owner of search-mcp-server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to henry@mcphub.com.

MCP Naver Search Server is a Node.js server implementing the Model Context Protocol (MCP) to provide Naver web search functionality.

Tools
  1. naverSearch

    Performs a Naver web search and returns results with an option to include or exclude HTML tags.

MCP Search Server (Node.js)

๋‹ค์–‘ํ•œ ์›น ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ชจ๋ธ ์ปจํ…์ŠคํŠธ ํ”„๋กœํ† ์ฝœ(MCP)์„ ๊ตฌํ˜„ํ•œ Node.js ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค. ์ด ํ”„๋กœ์ ํŠธ๋Š” @modelcontextprotocol/sdk๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ MCP ํ˜ธํ™˜ ๋„๊ตฌ๋ฅผ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

โœจ ์ฃผ์š” ๊ธฐ๋Šฅ

  • ์œ ์—ฐํ•œ ๊ฒ€์ƒ‰ ์—”์ง„ ์ง€์›: search_engines.json ํŒŒ์ผ์„ ํ†ตํ•ด ๋‹ค์–‘ํ•œ ๊ฒ€์ƒ‰ ์—”์ง„ ์„ค์ •์„ ์ง€์›ํ•˜๋ฉฐ, ํŠน์ • ์—”์ง„ ๊ฒ€์ƒ‰ ๋ฐ ์—ฌ๋Ÿฌ ์—”์ง„ ํ†ตํ•ฉ ๊ฒ€์ƒ‰์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • ์ผ๋ฐ˜ ์›น ๊ฒ€์ƒ‰ ๋„๊ตฌ (search):
    • ์ฃผ์–ด์ง„ ๊ฒ€์ƒ‰์–ด์— ๋Œ€ํ•ด ํŠน์ • ๊ฒ€์ƒ‰ ์—”์ง„ ๋˜๋Š” ์—ฌ๋Ÿฌ ์—”์ง„์—์„œ ์›น ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    • ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์—์„œ HTML ํƒœ๊ทธ๋ฅผ ํฌํ•จํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•˜๋Š” ์˜ต์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
    • ์–ธ์–ด ์ฝ”๋“œ๋ฅผ ์ง€์ •ํ•˜์—ฌ ํ•ด๋‹น ์–ธ์–ด๋ฅผ ์ง€์›ํ•˜๋Š” ์—”์ง„์„ ๋Œ€์ƒ์œผ๋กœ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํŠน์ˆ˜ Google ๊ฒ€์ƒ‰ ๋„๊ตฌ (googleSearch):
    • CAPTCHA ์šฐํšŒ๋ฅผ ์‹œ๋„ํ•˜๋Š” HumanLikeGoogleCrawler๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Google ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • URL ์ฝ˜ํ…์ธ  ๊ฐ€์ ธ์˜ค๊ธฐ ๋ฐ ์‚ฌ์šฉ์ž ์ •์˜ URL ๊ฒ€์ƒ‰ (fetchUrl):
    • ์ง€์ •๋œ URL์˜ ์›น ํŽ˜์ด์ง€ ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
    • URL๊ณผ ํ•จ๊ป˜ ๊ฒ€์ƒ‰์–ด ๋ฐ ๊ฒ€์ƒ‰ ํŒŒ๋ผ๋ฏธํ„ฐ ์ด๋ฆ„์„ ์ œ๊ณตํ•˜์—ฌ, ํ•ด๋‹น URL ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์šฉ์ž ์ •์˜ ๊ฒ€์ƒ‰์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • MCP ํ˜ธํ™˜: @modelcontextprotocol/sdk๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ MCP ํ‘œ์ค€์„ ๋”ฐ๋ฅด๋Š” ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ์„ค์ • ๊ฐ€๋Šฅ:
    • ๊ฒ€์ƒ‰ ์—”์ง„ ๊ด€๋ จ ์„ค์ •: search_engines.json (์—”์ง„๋ณ„ URL, ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ๋“ฑ)
    • ์„œ๋น„์Šค ๋ฐ ํฌ๋กค๋Ÿฌ ์„ค์ •: src/config/serviceConfig.js (ํฌ๋กค๋Ÿฌ ํƒ€์ž…, ์ƒ์„ธ ์˜ต์…˜, Google UI ์„ ํƒ์ž ๋“ฑ)
  • ๊ตฌ์กฐํ™”๋œ ๋กœ๊น…: winston์„ ํ™œ์šฉํ•˜์—ฌ ์ƒ์„ธํ•œ ๋กœ๊ทธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • SOLID ์„ค๊ณ„ ์›์น™: ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๊ณ ๋ คํ•˜์—ฌ ๊ฐœ๋ฐœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

โš™๏ธ ๋ชจ๋ธ ์ปจํ…์ŠคํŠธ ํ”„๋กœํ† ์ฝœ(MCP)์— ๋Œ€ํ•˜์—ฌ

๋ชจ๋ธ ์ปจํ…์ŠคํŠธ ํ”„๋กœํ† ์ฝœ(MCP) SDK (@modelcontextprotocol/sdk)๋Š” ์ด ์„œ๋ฒ„์˜ ํ•ต์‹ฌ ๊ตฌ์„ฑ ์š”์†Œ์ž…๋‹ˆ๋‹ค. ์ด ์„œ๋ฒ„๊ฐ€ ๋…ธ์ถœํ•˜๋Š” ๋„๊ตฌ๋ฅผ ์ •์˜ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ์™€ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

MCP SDK๋Š” package.json ํŒŒ์ผ์— ํ”„๋กœ์ ํŠธ ์ข…์†์„ฑ์œผ๋กœ ๋‚˜์—ด๋˜์–ด ์žˆ์œผ๋ฉฐ, npm install ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋ฉด ์ž๋™์œผ๋กœ ์„ค์น˜๋ฉ๋‹ˆ๋‹ค.

๐Ÿš€ ์‹œ์ž‘ํ•˜๊ธฐ

1. NPX๋ฅผ ์ด์šฉํ•œ ์ง์ ‘ ์‹คํ–‰ (๊ถŒ์žฅ, ์„ค์น˜ ๋ถˆํ•„์š”)

์ด ๋„๊ตฌ๋Š” npx๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ GitHub ์ €์žฅ์†Œ์—์„œ ์ง์ ‘ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ„๋„์˜ ์„ค์น˜ ๊ณผ์ •์ด ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค.

npx github:kim57uak/search-mcp-server

์œ„ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ตœ์‹  ๋ฒ„์ „์˜ ๋„๊ตฌ๋ฅผ GitHub์—์„œ ๋‚ด๋ ค๋ฐ›์•„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. MCP ์„œ๋ฒ„๊ฐ€ ์‹œ์ž‘๋˜๊ณ  ํ‘œ์ค€ ์ž…์ถœ๋ ฅ์„ ํ†ตํ•ด ํ†ต์‹ ํ•  ์ค€๋น„๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

2. ๋กœ์ปฌ ๊ฐœ๋ฐœ ๋ฐ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ์„ค์ •

๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ ๊ฐœ๋ฐœํ•˜๊ฑฐ๋‚˜ ํ…Œ์ŠคํŠธํ•˜๋ ค๋ฉด ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅด์„ธ์š”.

  1. ์ €์žฅ์†Œ ๋ณต์ œ:
    git clone https://github.com/kim57uak/search-mcp-server.git
    cd search-mcp-server
    
  2. ์ข…์†์„ฑ ์„ค์น˜:
    npm install
    
  3. ์„œ๋ฒ„ ์‹คํ–‰:
    npm start
    
    ๋˜๋Š” ๊ฐœ๋ฐœ ๋ชจ๋“œ(nodemon ์‚ฌ์šฉ):
    npm run dev
    

์„œ๋ฒ„๊ฐ€ ์‹œ์ž‘๋˜๋ฉด ํ‘œ์ค€ ์ž…๋ ฅ(stdin)์„ ํ†ตํ•ด MCP ์š”์ฒญ์„ ์ˆ˜์‹ ํ•˜๊ณ  ํ‘œ์ค€ ์ถœ๋ ฅ(stdout)์„ ํ†ตํ•ด ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค.

search_engines.json ํŒŒ์ผ์„ ํ†ตํ•ด ์‚ฌ์šฉํ•  ๊ฒ€์ƒ‰ ์—”์ง„์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“„ ๊ฐœ๋ฐœ์ž ๋ฌธ์„œ

๊ฐœ๋ฐœ, ๊ธฐ์—ฌ ๊ฐ€์ด๋“œ๋ผ์ธ ๋ฐ ํ”„๋กœ์ ํŠธ ์•„ํ‚คํ…์ฒ˜์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์ •๋ณด๋Š” ****๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๐Ÿ› ๏ธ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ MCP ๋„๊ตฌ

1. search (์ผ๋ฐ˜ ๊ฒ€์ƒ‰)

  • ์„ค๋ช…: ์›น ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. engineName์œผ๋กœ ํŠน์ • ๊ฒ€์ƒ‰ ์—”์ง„์„ ์ง€์ •ํ•˜๊ฑฐ๋‚˜, languageCode๋ฅผ ์ œ๊ณตํ•˜์—ฌ ํ•ด๋‹น ์–ธ์–ด๋ฅผ ์ง€์›ํ•˜๋Š” ์—ฌ๋Ÿฌ ์—”์ง„์—์„œ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. engineName์ด ์ง€์ •๋˜์ง€ ์•Š์œผ๋ฉด languageCode๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ (๋˜๋Š” ๋ชจ๋“ ) ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์—ฌ๋Ÿฌ ์—”์ง„์—์„œ ํ†ตํ•ฉ ๊ฒ€์ƒ‰์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. (search_engines.json์— ์ •์˜๋œ ์—”์ง„ ์‚ฌ์šฉ)
  • ์ž…๋ ฅ (inputs):
    • query (string, ํ•„์ˆ˜): ๊ฒ€์ƒ‰ํ•  ๋‹จ์–ด๋‚˜ ๋ฌธ์žฅ์ž…๋‹ˆ๋‹ค.
    • engineName (string, ์„ ํƒ): ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•  ํŠน์ • ์—”์ง„์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค (์˜ˆ: "Naver", "Bing". search_engines.json ํŒŒ์ผ ์ฐธ๊ณ ).
    • languageCode (string, ์„ ํƒ): ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์˜ ์–ธ์–ด๋ฅผ ์ง€์ •ํ•˜๊ฑฐ๋‚˜, ํ†ตํ•ฉ ๊ฒ€์ƒ‰ ์‹œ ํ•ด๋‹น ์–ธ์–ด๋ฅผ ์ง€์›ํ•˜๋Š” ์—”์ง„์„ ํ•„ํ„ฐ๋งํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค (์˜ˆ: "ko", "en").
    • includeHtml (boolean, ์„ ํƒ, ๊ธฐ๋ณธ๊ฐ’: false): true๋กœ ์„ค์ •ํ•˜๋ฉด ๊ฒฐ๊ณผ์— HTML ํƒœ๊ทธ๋ฅผ ํฌํ•จํ•˜๊ณ , false์ด๋ฉด ์ œ๊ฑฐ๋œ ํ…์ŠคํŠธ๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • ์˜ˆ์ƒ ์ถœ๋ ฅ (MCP ์‘๋‹ต์˜ result.content[0].text ๋‚ด๋ถ€ JSON ๋ฌธ์ž์—ด):
    • ํŠน์ • ์—”์ง„ ๊ฒ€์ƒ‰ ์‹œ (engineName ์ œ๊ณต):
      {
        "query": "๊ฒ€์ƒ‰์–ด",
        "engine": "์—”์ง„์ด๋ฆ„",
        "language": "์–ธ์–ด์ฝ”๋“œ", // ์ œ๊ณต๋œ ๊ฒฝ์šฐ
        "resultText": "๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๋‚ด์šฉ (HTML ํฌํ•จ ๋˜๋Š” ์ œ๊ฑฐ๋จ)",
        "retrievedAt": "YYYY-MM-DDTHH:mm:ss.sssZ",
        "searchUrl": "์‹ค์ œ์š”์ฒญ๋œ๊ฒ€์ƒ‰URL"
      }
      
    • ํ†ตํ•ฉ ๊ฒ€์ƒ‰ ์‹œ (engineName ๋ฏธ์ œ๊ณต):
      [ // ์—ฌ๋Ÿฌ ์—”์ง„ ๊ฒฐ๊ณผ์˜ ๋ฐฐ์—ด
        {
          "query": "๊ฒ€์ƒ‰์–ด",
          "searchEngine": "์—”์ง„์ด๋ฆ„1",
          "language": "์–ธ์–ด์ฝ”๋“œ", // ์ œ๊ณต๋œ ๊ฒฝ์šฐ
          "resultText": "์—”์ง„1 ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ...",
          "retrievedAt": "YYYY-MM-DDTHH:mm:ss.sssZ",
          "searchUrl": "์‹ค์ œ์š”์ฒญ๋œ๊ฒ€์ƒ‰URL1"
        },
        {
          "query": "๊ฒ€์ƒ‰์–ด",
          "searchEngine": "์—”์ง„์ด๋ฆ„2",
          // ... (์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ error ํ•„๋“œ ํฌํ•จ)
          "error": "์—”์ง„2 ๊ฒ€์ƒ‰ ์‹คํŒจ ๋ฉ”์‹œ์ง€",
          "retrievedAt": "YYYY-MM-DDTHH:mm:ss.sssZ"
        }
      ]
      
  • ํ˜ธ์ถœ ์˜ˆ์‹œ (Stdio):
    • Naver์—์„œ "Node.js" ๊ฒ€์ƒ‰:
      {
        "tool": "search",
        "inputs": {
          "query": "Node.js",
          "engineName": "Naver",
          "languageCode": "ko",
          "includeHtml": false
        },
        "id": "readme-generic-naver"
      }
      
    • ํ•œ๊ตญ์–ด๋ฅผ ์ง€์›ํ•˜๋Š” ์—”์ง„๋“ค์—์„œ "๋‚ ์”จ" ํ†ตํ•ฉ ๊ฒ€์ƒ‰:
      {
        "tool": "search",
        "inputs": {
          "query": "๋‚ ์”จ",
          "languageCode": "ko",
          "includeHtml": false
        },
        "id": "readme-integrated-ko"
      }
      

2. googleSearch (Google ํŠนํ™” ๊ฒ€์ƒ‰)

  • ์„ค๋ช…: Google ๊ฒ€์ƒ‰์„ "์ธ๊ฐ„์ฒ˜๋Ÿผ" ์ˆ˜ํ–‰ํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. CAPTCHA ์šฐํšŒ๋ฅผ ์‹œ๋„ํ•˜๋Š” ํŠน์ˆ˜ ํฌ๋กค๋Ÿฌ(HumanLikeGoogleCrawler)๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋Š” ์ฃผ๋กœ Google ์„ค์ • ๋ฐ ๊ฒ€์ƒ‰์–ด์˜ ์–ธ์–ด์— ๋”ฐ๋ผ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.
  • ์ž…๋ ฅ (inputs):
    • query (string, ํ•„์ˆ˜): ๊ฒ€์ƒ‰ํ•  ๋‹จ์–ด๋‚˜ ๋ฌธ์žฅ์ž…๋‹ˆ๋‹ค.
    • includeHtml (boolean, ์„ ํƒ, ๊ธฐ๋ณธ๊ฐ’: false): HTML ํƒœ๊ทธ ํฌํ•จ ์—ฌ๋ถ€.
  • ์˜ˆ์ƒ ์ถœ๋ ฅ (MCP ์‘๋‹ต์˜ result.content[0].text ๋‚ด๋ถ€ JSON ๋ฌธ์ž์—ด):
    {
      "query": "๊ฒ€์ƒ‰์–ด",
      "resultText": "Google ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๋‚ด์šฉ (HTML ํฌํ•จ ๋˜๋Š” ์ œ๊ฑฐ๋จ)",
      "retrievedAt": "YYYY-MM-DDTHH:mm:ss.sssZ",
      "searchEngine": "google"
    }
    
  • ํ˜ธ์ถœ ์˜ˆ์‹œ (Stdio):
    {
      "tool": "googleSearch",
      "inputs": {
        "query": "์˜ค๋Š˜์˜ ๊ตฌ๊ธ€ ๊ฒ€์ƒ‰",
        "includeHtml": false
      },
      "id": "readme-example-google"
    }
    

3. fetchUrl (URL ์ฝ˜ํ…์ธ  ๊ฐ€์ ธ์˜ค๊ธฐ ๋ฐ ์‚ฌ์šฉ์ž ์ •์˜ URL ๊ฒ€์ƒ‰)

  • ์„ค๋ช…: ์ œ๊ณต๋œ URL์˜ ์›น ํŽ˜์ด์ง€ ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ์„ ํƒ์ ์œผ๋กœ ๊ฒ€์ƒ‰์–ด(query)์™€ ๊ฒ€์ƒ‰์–ด ํŒŒ๋ผ๋ฏธํ„ฐ๋ช…(queryParamName)์„ ์ œ๊ณตํ•˜์—ฌ, ํ•ด๋‹น URL์— ๊ฒ€์ƒ‰์–ด๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์š”์ฒญํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (์˜ˆ: ํŠน์ • ๊ฒ€์ƒ‰ ์—”์ง„์˜ URL ํ…œํ”Œ๋ฆฟ์— ๊ฒ€์ƒ‰์–ด ์‚ฝ์ž…).
  • ์ž…๋ ฅ (inputs):
    • url (string, ํ•„์ˆ˜): ๋‚ด์šฉ์„ ๊ฐ€์ ธ์˜ฌ ์›น ํŽ˜์ด์ง€์˜ ์ „์ฒด URL ๋˜๋Š” ๊ฒ€์ƒ‰์„ ์œ„ํ•œ ๊ธฐ๋ณธ URL์ž…๋‹ˆ๋‹ค.
    • query (string, ์„ ํƒ): url์— ์ถ”๊ฐ€ํ•  ๊ฒ€์ƒ‰์–ด์ž…๋‹ˆ๋‹ค. ์ด ๊ฐ’์ด ์ œ๊ณต๋˜๋ฉด ์‚ฌ์šฉ์ž ์ •์˜ URL ๊ฒ€์ƒ‰์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.
    • queryParamName (string, ์„ ํƒ, ๊ธฐ๋ณธ๊ฐ’: q): query๋ฅผ ์ „๋‹ฌํ•  URL ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.
    • includeHtml (boolean, ์„ ํƒ, ๊ธฐ๋ณธ๊ฐ’: false): ๊ฒฐ๊ณผ์— HTML ํƒœ๊ทธ ํฌํ•จ ์—ฌ๋ถ€.
  • ์˜ˆ์ƒ ์ถœ๋ ฅ (MCP ์‘๋‹ต์˜ result.content[0].text ๋‚ด๋ถ€ JSON ๋ฌธ์ž์—ด):
    {
      "url": "์š”์ฒญ๋œ ์ตœ์ข… URL", // query๊ฐ€ ์‚ฌ์šฉ๋œ ๊ฒฝ์šฐ ๋ณ€๊ฒฝ๋œ URL
      "textContent": "์ถ”์ถœ๋œ ํ…์ŠคํŠธ ๋‚ด์šฉ...",
      "retrievedAt": "YYYY-MM-DDTHH:mm:ss.sssZ",
      "originalUrl": "์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์ดˆ๊ธฐ URL", // query ์‚ฌ์šฉ ์‹œ์—๋งŒ ํฌํ•จ๋  ์ˆ˜ ์žˆ์Œ
      "queryUsed": "์‚ฌ์šฉ๋œ ๊ฒ€์ƒ‰์–ด", // query ์‚ฌ์šฉ ์‹œ์—๋งŒ ํฌํ•จ๋  ์ˆ˜ ์žˆ์Œ
      "targetUrl": "์‹ค์ œ ์š”์ฒญ๋œ ์ตœ์ข… URL" // query ์‚ฌ์šฉ ์‹œ ๋ณ€๊ฒฝ๋œ URL๊ณผ ๋™์ผ
    }
    
  • ํ˜ธ์ถœ ์˜ˆ์‹œ (Stdio):
    • ๋‹จ์ˆœ URL ๋‚ด์šฉ ๊ฐ€์ ธ์˜ค๊ธฐ:
      {
        "tool": "fetchUrl",
        "inputs": {
          "url": "https://developer.mozilla.org/ko/docs/Web/JavaScript",
          "includeHtml": false
        },
        "id": "readme-fetch-simple"
      }
      
    • ์‚ฌ์šฉ์ž ์ •์˜ URL๋กœ ๊ฒ€์ƒ‰ (DuckDuckGo URL ํ…œํ”Œ๋ฆฟ ์‚ฌ์šฉ):
      {
        "tool": "fetchUrl",
        "inputs": {
          "url": "https://duckduckgo.com/",
          "query": "Node.js MCP",
          "queryParamName": "q",
          "includeHtml": true
        },
        "id": "readme-fetch-custom-search"
      }
      

๐Ÿ“ฆ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

mcp-search-server/ โ”œโ”€โ”€ logs/ # ๋กœ๊ทธ ํŒŒ์ผ (gitignored) โ”œโ”€โ”€ src/ # ์†Œ์Šค ์ฝ”๋“œ โ”‚ โ”œโ”€โ”€ config/ # ์„ค์ • ํŒŒ์ผ (serviceConfig.js) โ”‚ โ”œโ”€โ”€ crawlers/ # ํฌ๋กค๋Ÿฌ ๊ตฌํ˜„ (humanLikeGoogleCrawler.js ๋“ฑ) โ”‚ โ”œโ”€โ”€ services/ # ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง (searchService.js) โ”‚ โ”œโ”€โ”€ tools/ # MCP ๋„๊ตฌ ์ •์˜ (genericSearchTool.js, googleSearchTool.js, urlFetcherTool.js, index.js) โ”‚ โ”œโ”€โ”€ transports/ # ์ „์†ก ๊ณ„์ธต ์„ค์ • (stdioTransport.js) โ”‚ โ””โ”€โ”€ utils/ # ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜ (logger.cjs, htmlParser.js, searchEngineManager.js ๋“ฑ) โ”œโ”€โ”€ tests/ # ํ…Œ์ŠคํŠธ ์ฝ”๋“œ โ”œโ”€โ”€ .gitignore โ”œโ”€โ”€ .prettierrc.json # Prettier ์„ค์ • โ”œโ”€โ”€ DEVELOPER_MANUAL.ko.md โ”œโ”€โ”€ INSTALL.md โ”œโ”€โ”€ README.md # ํ˜„์žฌ ํŒŒ์ผ โ”œโ”€โ”€ eslint.config.mjs # ESLint ์„ค์ • (flat config) โ”œโ”€โ”€ jest.config.js # Jest ์„ค์ • โ”œโ”€โ”€ nodemon.json # Nodemon ์„ค์ • โ”œโ”€โ”€ package.json โ”œโ”€โ”€ package-lock.json โ””โ”€โ”€ search_engines.json # ๊ฒ€์ƒ‰ ์—”์ง„ URL ๋ฐ ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •

๐ŸŒ ํฌ๋กค๋Ÿฌ ์ƒ์„ธ: Google ๊ฒ€์ƒ‰ ๋ฐ CAPTCHA ์šฐํšŒ

์ด ํ”„๋กœ์ ํŠธ์˜ googleSearch ๋„๊ตฌ๋Š” src/crawlers/humanLikeGoogleCrawler.js์— ๊ตฌํ˜„๋œ ํŠน์ˆ˜ ํฌ๋กค๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ํฌ๋กค๋Ÿฌ๋Š” Google์˜ CAPTCHA ๋ฐ ๋ด‡ ํƒ์ง€ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์šฐํšŒํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์–‘ํ•œ ์ „๋žต์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Google ๊ฒ€์ƒ‰ ํฌ๋กค๋Ÿฌ (HumanLikeGoogleCrawler)

  • Puppeteer ๊ธฐ๋ฐ˜: ์‹ค์ œ Chrome ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ œ์–ดํ•˜๋Š” Puppeteer ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›น ํŽ˜์ด์ง€์™€ ์ƒํ˜ธ์ž‘์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด JavaScript ์‹คํ–‰, ์‚ฌ์šฉ์ž ์ž…๋ ฅ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋“ฑ ์‹ค์ œ ์‚ฌ์šฉ์ž์™€ ์œ ์‚ฌํ•œ ํ™˜๊ฒฝ์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • Stealth ๊ธฐ๋Šฅ: puppeteer-extra-plugin-stealth ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜์—ฌ Puppeteer๊ฐ€ ์ž๋™ํ™”๋œ ๋ธŒ๋ผ์šฐ์ €์ž„์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋‹ค์–‘ํ•œ ์ง€ํ‘œ(์˜ˆ: navigator.webdriver ํ”Œ๋ž˜๊ทธ)๋ฅผ ์ˆจ๊น๋‹ˆ๋‹ค. ๋˜ํ•œ User-Agent, ๋ธŒ๋ผ์šฐ์ € ํ”Œ๋Ÿฌ๊ทธ์ธ ์ •๋ณด, ์–ธ์–ด ์„ค์ • ๋“ฑ์„ ์‹ค์ œ ์‚ฌ์šฉ์ž์™€ ์œ ์‚ฌํ•˜๊ฒŒ ์œ„์žฅํ•ฉ๋‹ˆ๋‹ค.

CAPTCHA ์šฐํšŒ ๋ฐ ํƒ์ง€ ํšŒํ”ผ ์ „๋žต

Google์˜ ์ •๊ตํ•œ ๋ด‡ ํƒ์ง€ ์‹œ์Šคํ…œ์— ๋Œ€์‘ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋ฒ•๋“ค์ด ์ ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค:

  • ๋‹ค์–‘ํ•œ User-Agent ๋žœ๋ค ์‚ฌ์šฉ: ๋ฏธ๋ฆฌ ์ •์˜๋œ ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ € User-Agent ๋ชฉ๋ก์—์„œ ๋ฌด์ž‘์œ„๋กœ ์„ ํƒํ•˜์—ฌ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • Viewport ์„ค์ •: ์ผ๋ฐ˜์ ์ธ ๋ฐ์Šคํฌํ†ฑ ํ•ด์ƒ๋„(์˜ˆ: 1920x1080)๋กœ ๋ธŒ๋ผ์šฐ์ € Viewport๋ฅผ ์„ค์ •ํ•˜์—ฌ ์ผ๊ด€์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.
  • ์ธ๊ฐ„๊ณผ ์œ ์‚ฌํ•œ ํ–‰๋™ ์‹œ๋ฎฌ๋ ˆ์ด์…˜:
    • ๋งˆ์šฐ์Šค ์ปค์„œ์˜ ์ž์—ฐ์Šค๋Ÿฌ์šด ์ด๋™ ๋ฐ ํด๋ฆญ ์œ„์น˜์˜ ๋ฏธ์„ธํ•œ ๋žœ๋ค ๋ณ€ํ™”.
    • ๊ฒ€์ƒ‰์–ด ์ž…๋ ฅ ์‹œ ์‹ค์ œ ํƒ€์ดํ•‘๊ณผ ์œ ์‚ฌํ•œ ๋žœ๋ค ๋”œ๋ ˆ์ด ์ ์šฉ.
    • ํŽ˜์ด์ง€ ๋กœ๋“œ ํ›„ ๋žœ๋ค ์Šคํฌ๋กค ๋ฐ ๋งˆ์šฐ์Šค ์ด๋™.
  • ์š”์ฒญ ๊ฐ„ ๋žœ๋ค ๋”œ๋ ˆ์ด: ํŽ˜์ด์ง€ ์ด๋™, ํด๋ฆญ, ์ž…๋ ฅ ๋“ฑ ์ฃผ์š” ์ž‘์—… ์ „ํ›„์— ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•œ ๋žœ๋ค ๋”œ๋ ˆ์ด๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์ž๋™ํ™”๋œ ํŒจํ„ด์„ ๊นจ๋œจ๋ฆฝ๋‹ˆ๋‹ค.
  • CAPTCHA ๊ฐ์ง€ ์‹œ ์žฌ์‹œ๋„ ๋กœ์ง:
    • "๋กœ๋ด‡์ด ์•„๋‹™๋‹ˆ๋‹ค", "๋น„์ •์ƒ ํŠธ๋ž˜ํ”ฝ" ๋“ฑ์˜ ํ‚ค์›Œ๋“œ๋กœ CAPTCHA ํŽ˜์ด์ง€๋ฅผ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค.
    • ๊ฐ์ง€ ์‹œ, ์ตœ๋Œ€ 2ํšŒ๊นŒ์ง€ ์žฌ์‹œ๋„๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. (์ด 3ํšŒ ์‹œ๋„)
    • ์žฌ์‹œ๋„ ์‚ฌ์ด์—๋Š” 5~10์ดˆ์˜ ๋น„๊ต์  ๊ธด ๋žœ๋ค ๋Œ€๊ธฐ ์‹œ๊ฐ„์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

์ตœ๊ทผ ๊ฐœ์„  ์‚ฌํ•ญ

  • Viewport ์„ค์ • ์ถ”๊ฐ€: ๋ชจ๋“  Google ๊ฒ€์ƒ‰ ์‹œ ๋™์ผํ•œ Viewport(1920x1080)๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋”œ๋ ˆ์ด ๊ฐ•ํ™”: ํฌ๋กค๋ง ๊ณผ์ • ์ „๋ฐ˜์˜ ๋žœ๋ค ๋”œ๋ ˆ์ด ๊ฐ’์„ ์กฐ์ •ํ•˜๊ณ  ๋‹ค์–‘์„ฑ์„ ๋†’์˜€์Šต๋‹ˆ๋‹ค.
  • ์žฌ์‹œ๋„ ๋กœ์ง ๊ฐœ์„ : CAPTCHA ๋ฐœ์ƒ ์‹œ ์žฌ์‹œ๋„ ํšŸ์ˆ˜๋ฅผ ๋Š˜๋ฆฌ๊ณ , ์žฌ์‹œ๋„ ๊ฐ„ ๋Œ€๊ธฐ ์‹œ๊ฐ„์„ ์ฆ๊ฐ€์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.

ํ•œ๊ณ„์  ๋ฐ ์ฃผ์˜์‚ฌํ•ญ

  • ์™„๋ฒฝํ•œ ์šฐํšŒ๋Š” ์–ด๋ ค์›€: Google์˜ ๋ด‡ ํƒ์ง€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ์ง€์†์ ์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜๋ฏ€๋กœ, ํ˜„์žฌ ์ ์šฉ๋œ ๋ฐฉ๋ฒ•์œผ๋กœ๋„ CAPTCHA๊ฐ€ ๊ฐ„ํ—์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ํ–ฅํ›„ ๋‹ค์‹œ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • IP ์ฃผ์†Œ ํ‰ํŒ: ์‚ฌ์šฉ๋˜๋Š” ์„œ๋ฒ„์˜ IP ์ฃผ์†Œ๊ฐ€ ๊ณผ๊ฑฐ์— ์ŠคํŒธ์ด๋‚˜ ์–ด๋ทฐ์ง•์— ์‚ฌ์šฉ๋œ ์ ์ด ์žˆ๋‹ค๋ฉด CAPTCHA ๋ฐœ์ƒ ๋นˆ๋„๊ฐ€ ๋†’์•„์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊นจ๋—ํ•œ IP๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ณผ๋„ํ•œ ์š”์ฒญ: ์งง์€ ์‹œ๊ฐ„์— ๋„ˆ๋ฌด ๋งŽ์€ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด IP๊ฐ€ ์ฐจ๋‹จ๋˜๊ฑฐ๋‚˜ CAPTCHA๊ฐ€ ๋” ์ž์ฃผ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ ์ ˆํ•œ ์š”์ฒญ ๊ฐ„๊ฒฉ์„ ์œ ์ง€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ถ”๊ฐ€ ๊ณ ๋ ค ์‚ฌํ•ญ (ํ–ฅํ›„ ๊ฐœ์„  ๋ฐฉํ–ฅ)

  • ํ”„๋ก์‹œ ์„œ๋ฒ„ ๋˜๋Š” IP ๋กœํ…Œ์ด์…˜: ๋‹ค์–‘ํ•œ IP ์ฃผ์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์š”์ฒญ์„ ๋ถ„์‚ฐ์‹œํ‚ค๋ฉด ํƒ์ง€ ๊ฐ€๋Šฅ์„ฑ์„ ๋‚ฎ์ถœ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์š”์ฒญ ๋นˆ๋„ ์ œ์–ด: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ์—์„œ ์ „์—ญ์ ์ธ ์š”์ฒญ ๋นˆ๋„๋ฅผ ์ œ์–ดํ•˜์—ฌ ํŠน์ • ์‹œ๊ฐ„ ๋™์•ˆ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋Š” ์š”์ฒญ ์ˆ˜๋ฅผ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.
  • ํ—ค๋” ๋ฐ ๋ธŒ๋ผ์šฐ์ € ํ•‘๊ฑฐํ”„๋ฆฐํŠธ ์ •๊ตํ™”: User-Agent ์™ธ์˜ HTTP ํ—ค๋”(Accept-Language, Accept-Encoding ๋“ฑ)์™€ ๋ธŒ๋ผ์šฐ์ € ํ•‘๊ฑฐํ”„๋ฆฐํŠธ ์š”์†Œ๋“ค์„ ๋”์šฑ ์‹ค์ œ ์‚ฌ์šฉ์ž์™€ ์œ ์‚ฌํ•˜๊ฒŒ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.