{"url_pattern":"^https?://(www\\.)?ctrip\\.com(/.*)?$","site_name":"ctrip","allowed_domains":["ctrip.com","hotels.ctrip.com","m.ctrip.com","you.ctrip.com"],"tools":[{"name":"ctrip_search","description":"携程旅行搜索 - 搜索目的地景点信息","inputSchema":{"type":"object","properties":{"query":{"type":"string","description":"搜索关键词，如城市名或景点名"}},"required":["query"]},"handler":"(params) => {\n  const run = async function(args) {\n\n      const query = args.query;\n      if (!query) return {error: 'query is required'};\n\n      // Strategy 1: Try the hotel/destination suggestion API (works in-browser with session)\n      try {\n        const suggestUrl = 'https://m.ctrip.com/restapi/h5api/searchapp/search?action=onekeyali&keyword=' + encodeURIComponent(query);\n        const suggestResp = await fetch(suggestUrl, {credentials: 'include'});\n        if (suggestResp.ok) {\n          const suggestData = await suggestResp.json();\n          if (suggestData && (suggestData.data || suggestData.result)) {\n            const raw = suggestData.data || suggestData.result || suggestData;\n            return {query: query, source: 'suggest_api', data: raw};\n          }\n        }\n      } catch(e) { /* fall through */ }\n\n      // Strategy 2: Try the global search suggestion API\n      try {\n        const globalUrl = 'https://www.ctrip.com/m/i/webapp/search-result/?query=' + encodeURIComponent(query);\n        const globalResp = await fetch(globalUrl, {credentials: 'include'});\n        if (globalResp.ok) {\n          const text = await globalResp.text();\n          if (text.startsWith('{') || text.startsWith('[')) {\n            return {query: query, source: 'global_api', data: JSON.parse(text)};\n          }\n        }\n      } catch(e) { /* fall through */ }\n\n      // Strategy 3: Parse the hotel search results page\n      try {\n        const hotelUrl = 'https://hotels.ctrip.com/hotels/list?keyword=' + encodeURIComponent(query);\n        const hotelResp = await fetch(hotelUrl, {credentials: 'include'});\n        if (hotelResp.ok) {\n          const html = await hotelResp.text();\n          const parser = new DOMParser();\n          const doc = parser.parseFromString(html, 'text/html');\n\n          // Try to extract embedded JSON data (Ctrip often puts initialState in scripts)\n          const scripts = doc.querySelectorAll('script');\n          for (const script of scripts) {\n            const text = script.textContent || '';\n            // Look for hotel list data in window.__INITIAL_STATE__ or similar\n            const stateMatch = text.match(/window\\.__INITIAL_STATE__\\s*=\\s*(\\{[\\s\\S]*?\\});?\\s*(?:window\\.|<\\/script>|$)/);\n            if (stateMatch) {\n              try {\n                const state = JSON.parse(stateMatch[1]);\n                const hotelList = state.hotelList || state.list || state.hotels;\n                if (hotelList && Array.isArray(hotelList)) {\n                  return {\n                    query: query,\n                    source: 'hotel_initial_state',\n                    count: hotelList.length,\n                    hotels: hotelList.slice(0, 15).map(h => ({\n                      name: h.hotelName || h.name,\n                      star: h.star,\n                      score: h.score,\n                      commentCount: h.commentCount,\n                      price: h.price || h.minPrice,\n                      address: h.address,\n                      url: h.url || h.detailUrl\n                    }))\n                  };\n                }\n              } catch(e) { /* JSON parse failed, continue */ }\n            }\n          }\n\n          // Fallback: parse hotel cards from DOM\n          const hotelCards = doc.querySelectorAll('[class*=\"hotel-card\"], [class*=\"hotelList\"], li[class*=\"list-item\"], div[class*=\"hotel_new_list\"]');\n          if (hotelCards.length > 0) {\n            const hotels = [];\n            hotelCards.forEach(card => {\n              const nameEl = card.querySelector('a[class*=\"name\"], h2, [class*=\"hotel_name\"]');\n              const scoreEl = card.querySelector('[class*=\"score\"], [class*=\"rating\"]');\n              const priceEl = card.querySelector('[class*=\"price\"]');\n              const addrEl = card.querySelector('[class*=\"address\"], [class*=\"location\"]');\n              if (nameEl) {\n                hotels.push({\n                  name: (nameEl.textContent || '').trim(),\n                  score: scoreEl ? (scoreEl.textContent || '').trim() : '',\n                  price: priceEl ? (priceEl.textContent || '').trim() : '',\n                  address: addrEl ? (addrEl.textContent || '').trim() : ''\n                });\n              }\n            });\n            if (hotels.length > 0) {\n              return {query: query, source: 'hotel_html', count: hotels.length, hotels: hotels.slice(0, 15)};\n            }\n          }\n\n          // Last resort: extract any text content from the results area\n          const resultArea = doc.querySelector('#hotel_list, [class*=\"list-body\"], [class*=\"result\"], .searchresult');\n          if (resultArea) {\n            return {\n              query: query,\n              source: 'hotel_text',\n              text: (resultArea.textContent || '').replace(/\\s+/g, ' ').trim().substring(0, 2000)\n            };\n          }\n        }\n      } catch(e) { /* fall through */ }\n\n      // Strategy 4: Try the travel guide / destination page via you.ctrip.com\n      try {\n        const guideUrl = 'https://you.ctrip.com/SearchSite/Default/Destination?keyword=' + encodeURIComponent(query);\n        const guideResp = await fetch(guideUrl, {credentials: 'include'});\n        if (guideResp.ok) {\n          const html = await guideResp.text();\n          const parser = new DOMParser();\n          const doc = parser.parseFromString(html, 'text/html');\n\n          const items = doc.querySelectorAll('[class*=\"result\"], [class*=\"dest-item\"], li, .list_mod_item');\n          const results = [];\n          items.forEach(el => {\n            const linkEl = el.querySelector('a[href]');\n            const nameEl = el.querySelector('h2, h3, [class*=\"name\"], [class*=\"title\"]');\n            if (linkEl && nameEl) {\n              results.push({\n                name: (nameEl.textContent || '').trim(),\n                url: linkEl.getAttribute('href') || ''\n              });\n            }\n          });\n          if (results.length > 0) {\n            return {query: query, source: 'destination_search', count: results.length, results: results.slice(0, 15)};\n          }\n        }\n      } catch(e) { /* fall through */ }\n\n      // Strategy 5: Use site-wide search on ctrip.com\n      try {\n        const searchUrl = 'https://www.ctrip.com/global-search/result?keyword=' + encodeURIComponent(query);\n        const searchResp = await fetch(searchUrl, {credentials: 'include'});\n        if (searchResp.ok) {\n          const html = await searchResp.text();\n          const parser = new DOMParser();\n          const doc = parser.parseFromString(html, 'text/html');\n\n          const allLinks = doc.querySelectorAll('a[href]');\n          const results = [];\n          allLinks.forEach(a => {\n            const text = (a.textContent || '').trim();\n            const href = a.getAttribute('href') || '';\n            if (text.length > 4 && text.length < 200 && href.includes('ctrip.com') && !href.includes('javascript:')) {\n              results.push({title: text, url: href});\n            }\n          });\n          if (results.length > 0) {\n            // deduplicate by title\n            const seen = new Set();\n            const unique = results.filter(r => {\n              if (seen.has(r.title)) return false;\n              seen.add(r.title);\n              return true;\n            });\n            return {query: query, source: 'global_search', count: unique.length, results: unique.slice(0, 20)};\n          }\n        }\n      } catch(e) { /* fall through */ }\n\n      return {\n        query: query,\n        error: 'No results found. Ctrip may require an active browser session on www.ctrip.com.',\n        hint: 'Open www.ctrip.com in bb-browser first, then retry.'\n      };\n  };\n  return run(params || {});\n}"}]}