qidian WebMCP
Browser tool configuration for qidian
Tools (4)
qidian_get_current_book()
Extract book metadata from the current Qidian detail page
Parameters
No parameters
JavaScript Handler
() => {
const title = document.querySelector('.book-info h1 em, .book-information h1 em, h1 em')?.textContent?.trim() || document.title;
const author = document.querySelector('.book-info h1 a.writer, .book-information .writer, a.writer')?.textContent?.trim() || '';
const intro = document.querySelector('.book-intro p, .book-intro-detail, .intro')?.textContent?.replace(/\s+/g, ' ').trim() || '';
const category = Array.from(document.querySelectorAll('.tag span, .book-info .tag a, .book-information .tag a')).map((node) => node.textContent?.trim()).filter(Boolean);
return {
success: true,
book: {
title,
author,
intro,
category,
url: window.location.href
}
};
}
qidian_open_book()
Open a Qidian book detail page by book ID
Parameters
JavaScript Handler
(params) => {
window.location.href = 'https://book.qidian.com/info/' + params.bookId + '/';
return { success: true, message: 'Opening book detail...', bookId: params.bookId };
}
qidian_search()
起点中文网小说搜索
Parameters
JavaScript Handler
(params) => {
const run = async function(args) {
if (!args.query) return {error: 'Missing argument: query'};
const page = parseInt(args.page) || 1;
const kw = encodeURIComponent(args.query);
const url = 'https://www.qidian.com/so/' + kw + '.html' + (page > 1 ? '?page=' + page : '');
const resp = await fetch(url, {credentials: 'include'});
if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Make sure a qidian.com tab is open'};
const html = await resp.text();
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
// Try embedded JSON data first
let jsonData = null;
for (const s of doc.querySelectorAll('script')) {
const text = s.textContent || '';
for (const pat of [
/g_search_data\s*=\s*(\{[\s\S]*?\});/,
/window\.__INITIAL_STATE__\s*=\s*(\{[\s\S]*?\});/,
/"bookList"\s*:\s*(\[[\s\S]*?\])\s*[,}]/
]) {
const m = text.match(pat);
if (m) { try { jsonData = JSON.parse(m[1]); } catch(e) {} }
if (jsonData) break;
}
if (jsonData) break;
}
if (jsonData) {
const bookList = jsonData.bookList || jsonData.books || jsonData.data?.bookList || [];
if (Array.isArray(bookList) && bookList.length > 0) {
const books = bookList.map((b, i) => ({
rank: i + 1,
id: b.bookId || b.bid || b.id,
title: b.bookName || b.bName || b.title || b.name,
author: b.authorName || b.author || b.aName,
category: b.catName || b.category || b.cat,
description: (b.desc || b.description || b.intro || '').substring(0, 300),
wordCount: b.totalWordCount || b.wordCount || b.cnt || null,
status: b.isFinish === 1 ? '完结' : b.isFinish === 0 ? '连载' : b.state || null,
cover: b.bookCoverUrl || b.cover || null,
url: 'https://book.qidian.com/info/' + (b.bookId || b.bid || b.id) + '/'
}));
return {query: args.query, page, count: books.length, books};
}
}
// DOM parsing: Qidian uses <li class="res-book-item"> for each result
const resultItems = doc.querySelectorAll(
'.res-book-item, .book-result-item, li[data-bid], ' +
'.search-result-page .book-img-text li'
);
if (resultItems.length > 0) {
const seen = new Set();
const books = [];
resultItems.forEach((el) => {
// Title: first meaningful link
const titleEl = el.querySelector('h2 a, h3 a, h4 a, [class*="title"] a');
const title = titleEl ? titleEl.textContent.trim() : '';
if (!title) return;
// Book ID from link
const href = titleEl ? (titleEl.getAttribute('href') || '') : '';
const idMatch = href.match(/\/(?:info|book)\/(\d+)/);
const bookId = idMatch ? idMatch[1] : (el.getAttribute('data-bid') || null);
if (bookId && seen.has(bookId)) return;
if (bookId) seen.add(bookId);
// Parse the full text content to extract structured fields
// Typical format: "Title Author |Category|Status Description... 最新更新 Chapter·Time WordCount"
const fullText = (el.textContent || '').replace(/\s+/g, ' ').trim();
// Extract "author |category|status" metadata
let author = '';
let category = '';
let status = null;
const metaMatch = fullText.match(/([^\s|]{2,})\s*\|([^|]+)\|(\S+)/);
if (metaMatch) {
author = metaMatch[1].trim();
category = metaMatch[2].trim();
const st = metaMatch[3].trim();
if (st === '连载' || st === '完结') status = st;
}
// Extract description: text between "status" and "最新更新"
let description = '';
const descMatch = fullText.match(/\|(连载|完结)\s+(.+?)(?:\s+最新更新|$)/);
if (descMatch && descMatch[2]) {
description = descMatch[2].trim().substring(0, 300);
}
// Word count: "XX.XX万总字数" or "XX万字"
let wordCount = null;
const wcMatch = fullText.match(/([\d,.]+)\s*万(?:总字数|字)/);
if (wcMatch) wordCount = wcMatch[1] + '万字';
// Last update
let lastUpdate = null;
const updateMatch = fullText.match(/最新更新\s*(.+?)(?:\s+[\d,.]+万|$)/);
if (updateMatch) lastUpdate = updateMatch[1].trim();
// Cover image
const imgEl = el.querySelector('img[src*="bookcover"], img[src*="qdbimg"], img[data-src*="bookcover"]');
let cover = imgEl ? (imgEl.getAttribute('src') || imgEl.getAttribute('data-src')) : null;
if (cover && cover.startsWith('//')) cover = 'https:' + cover;
const bookUrl = bookId
? 'https://book.qidian.com/info/' + bookId + '/'
: (href.startsWith('http') ? href : 'https://www.qidian.com' + href);
books.push({
rank: books.length + 1, id: bookId, title, author, category,
description, wordCount, status, lastUpdate, cover, url: bookUrl
});
});
if (books.length > 0) {
const pageInfo = doc.querySelector('[class*="pagination"], [class*="page"]');
const hasMore = pageInfo ? pageInfo.textContent.includes('下一页') : false;
return {query: args.query, page, count: books.length, hasMore, books};
}
}
// Last resort: collect unique book links
const allBookLinks = doc.querySelectorAll('a[href*="book.qidian.com/info/"], a[href*="/info/"], a[href*="/book/"]');
if (allBookLinks.length > 0) {
const seen = new Set();
const books = [];
allBookLinks.forEach((a) => {
const href = a.getAttribute('href') || '';
const idMatch = href.match(/\/(?:info|book)\/(\d+)/);
const bookId = idMatch ? idMatch[1] : null;
if (!bookId || seen.has(bookId)) return;
seen.add(bookId);
const title = a.textContent.trim();
if (!title || title.length > 100) return;
books.push({
rank: books.length + 1, id: bookId, title,
url: 'https://book.qidian.com/info/' + bookId + '/'
});
});
if (books.length > 0) {
return {query: args.query, page, count: books.length, books, note: 'Partial results'};
}
}
return {
error: 'No results found',
hint: 'Navigate to www.qidian.com first. The page may require login or the HTML structure has changed.',
debug: { htmlLength: html.length, title: doc.title || '' }
};
};
return run(params || {});
}
qidian_search_books()
Search novels on Qidian and return structured results
Parameters
JavaScript Handler
(params) => {
if (!params.query) {
return { success: false, message: 'query is required' };
}
const page = Number(params.page || 1) || 1;
const kw = encodeURIComponent(params.query);
const url = 'https://www.qidian.com/so/' + kw + '.html' + (page > 1 ? '?page=' + page : '');
return fetch(url, { credentials: 'include' })
.then((resp) => resp.ok ? resp.text() : Promise.reject(new Error('HTTP ' + resp.status)))
.then((html) => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const books = [];
const seen = new Set();
doc.querySelectorAll('.res-book-item, .book-result-item, li[data-bid], .search-result-page .book-img-text li').forEach((el) => {
const titleEl = el.querySelector('h2 a, h3 a, h4 a, [class*="title"] a');
const title = titleEl ? titleEl.textContent.trim() : '';
if (!title) return;
const href = titleEl ? (titleEl.getAttribute('href') || '') : '';
const idMatch = href.match(/\/(?:info|book)\/(\d+)/);
const bookId = idMatch ? idMatch[1] : (el.getAttribute('data-bid') || null);
if (bookId && seen.has(bookId)) return;
if (bookId) seen.add(bookId);
const text = (el.textContent || '').replace(/\s+/g, ' ').trim();
const metaMatch = text.match(/([^\s|]{2,})\s*\|([^|]+)\|(\S+)/);
const wordCountMatch = text.match(/([\d,.]+)\s*万(?:总字数|字)/);
books.push({
rank: books.length + 1,
bookId,
title,
author: metaMatch ? metaMatch[1].trim() : '',
category: metaMatch ? metaMatch[2].trim() : '',
status: metaMatch ? metaMatch[3].trim() : null,
wordCount: wordCountMatch ? wordCountMatch[1] + '万字' : null,
url: bookId ? 'https://book.qidian.com/info/' + bookId + '/' : (href.startsWith('http') ? href : 'https://www.qidian.com' + href)
});
});
return { success: true, query: params.query, page, count: books.length, books: books.slice(0, Number(params.limit || 10) || 10) };
})
.catch((error) => ({ success: false, message: error.message }));
}
🔌 Chrome MCP Server Extension
Use these tools with Claude, ChatGPT, and other AI assistants.
How to Use WebMCP
WebMCP tools are designed for browser extensions or automation frameworks. The browser extension matches the current URL against the pattern and executes the JavaScript handler when the tool is invoked.
API Endpoint:
GET /api/webmcp/match?url=https://www.qidian.com/...