MCP Lesson 12: Implementing a Trend‑Aggregating MCP Server for Weibo, Douyin, Toutiao, and Juejin
This article walks through the open‑source mcp‑trends‑hub project, explains its one‑stop hot‑trend aggregation features, shows how to configure the MCP Server in a client, and provides step‑by‑step Java implementations for Weibo, Douyin, Toutiao, NetEase News and Juejin trend tools, including code, architecture diagrams and registration flow.
In earlier chapters the author built a generic TypeScript MCP Server framework. This article introduces the open‑source project mcp‑trends‑hub (GitHub: https://github.com/baranwang/mcp-trends-hub/tree/main), which aggregates hot‑trend data from more than 20 Chinese platforms such as Weibo, Douyin, Toutiao, NetEase News and Juejin.
The server offers four key capabilities:
One‑stop aggregation of all hot‑trend sources.
Real‑time synchronization with source sites.
Full compatibility with the Model Context Protocol (MCP), making it easy to plug into AI applications.
Simple extension via configuration or custom RSS sources, and flexible field customization through environment variables.
To use the server, add the following configuration to the MCP client:
{
"mcpServers": {
"trends-hub": {
"command": "npx",
"args": ["-y", "[email protected]"]
}
}
}If the local environment requires an absolute path for npx, adjust the command accordingly.
The article then details Java implementations for each trend tool.
Weibo Trending Tool
Implementation steps:
Define a service class annotated with @Service and @Slf4j.
Use Jsoup to request https://weibo.com/ajax/side/hotSearch.
Check HTTP status, parse the JSON response, verify ok == 1 and the presence of data.realtime.
Iterate the realtime array, skip items where is_ad == 1, and build a map containing title, description, popularity and a constructed search URL.
Return the list as a JSON string via JSON.toJSONString. If an exception occurs, log the error and return a failure message.
@Service
@Slf4j
public class WeiboTrendingDataRequestService {
@Tool(name = "get-weibo-trending",
description = "获取微博热搜榜,包含时事热点、社会现象、娱乐新闻、明星动态及网络热议话题的实时热门中文资讯")
public String getWeiboTrending() {
String url = "https://weibo.com/ajax/side/hotSearch";
try {
Connection.Response response = Jsoup.connect(url)
.ignoreContentType(true)
.timeout(10000)
.header("referer", "https://weibo.com")
.method(Connection.Method.GET)
.execute();
if (response.statusCode() != 200) {
log.warn("请求微博热搜失败,HTTP状态码: {}", response.statusCode());
return "微博热搜获取失败,请稍后再试";
}
String body = response.body();
JSON.Object json = JSON.parseObject(body);
if (json.getInteger("ok") != 1 || !json.containsKey("data")) {
return "微博热搜获取失败,返回数据异常";
}
JSON.Array realtimeList = json.getJSONObject("data").getJSONArray("realtime");
if (realtimeList == null || realtimeList.isEmpty()) {
return "微博热搜为空";
}
List<Map<String, Object>> resultList = new ArrayList<>();
for (int i = 0; i < realtimeList.size(); i++) {
JSON.Object item = realtimeList.getJSONObject(i);
if (item.getIntValue("is_ad") == 1) continue;
String word = item.getString("word");
String wordScheme = item.getString("word_scheme");
String key = (wordScheme != null && !wordScheme.isEmpty()) ? wordScheme : "#" + word;
String searchUrl = "https://s.weibo.com/weibo?q=" + URLEncoder.encode(key, StandardCharsets.UTF_8) + "&band_rank=1&Refer=top";
Map<String, Object> map = new HashMap<>();
map.put("title", word);
map.put("description", item.getString("note") != null ? item.getString("note") : key);
map.put("popularity", item.getString("num"));
map.put("link", searchUrl);
resultList.add(map);
}
return JSON.toJSONString(resultList);
} catch (Exception e) {
log.error("工具调用异常: {}", e.getMessage(), e);
return "工具调用失败,请稍后再试";
}
}
}After registering the service in the Spring boot startup class, the server can be started in HTTP SSE mode and queried from an MCP client, returning JSON‑formatted results.
Douyin Trending Tool
The Douyin implementation follows a similar pattern but requires a CSRF token. The steps are:
Fetch the CSRF token from
https://www.douyin.com/passport/general/login_guiding_strategy/by parsing the Set‑Cookie header.
Call the hot‑search API https://www.douyin.com/aweme/v1/web/hot/search/list/ with the token in the Cookie header.
Parse the JSON response, iterate the word_list array, and build a map containing title, eventTime, cover, popularity and a link to the Douyin hot page.
Convert the list to XML using ConvertXmlUtil.convertToXml and return it.
@Service
@Slf4j
public class DouyinTrendingDataRequestService {
@Tool(name = "get-douyin-trending",
description = "获取抖音热搜榜单,展示当下最热门的社会话题、娱乐事件、网络热点和流行趋势")
public String htmlContentDeploy() {
try {
String csrfToken = fetchCsrfToken();
if (csrfToken == null || csrfToken.isEmpty()) {
return "抖音 CSRF Token 获取失败,请稍后再试";
}
String url = "https://www.douyin.com/aweme/v1/web/hot/search/list/";
Map<String, String> headers = Map.of("Cookie", "passport_csrf_token=" + csrfToken);
String fullUrl = url + "?device_platform=webapp&aid=6383&channel=channel_pc_web&detail_list=1";
Connection.Response response = Jsoup.connect(fullUrl)
.headers(headers)
.ignoreContentType(true)
.method(Connection.Method.GET)
.timeout(10000)
.execute();
if (response.statusCode() != 200) {
log.warn("请求抖音热榜失败,状态码: {}", response.statusCode());
return "获取抖音热搜失败,请稍后再试";
}
JSON.Object json = JSON.parseObject(response.body());
if (json.getIntValue("status_code") != 0 || !json.containsKey("data")) {
return "抖音热搜接口返回异常";
}
JSON.Array wordList = json.getJSONObject("data").getJSONArray("word_list");
if (wordList == null || wordList.isEmpty()) {
return "抖音热搜榜暂无数据";
}
List<Map<String, Object>> resultList = new ArrayList<>();
for (int i = 0; i < wordList.size(); i++) {
JSON.Object item = wordList.getJSONObject(i);
String word = item.getString("word");
long eventTime = item.getLongValue("event_time");
String cover = null;
if (item.containsKey("word_cover")) {
JSON.Array coverList = item.getJSONObject("word_cover").getJSONArray("url_list");
if (coverList != null && !coverList.isEmpty()) {
cover = coverList.getString(0);
}
}
Map<String, Object> map = new HashMap<>();
map.put("title", word);
map.put("eventTime", Instant.ofEpochSecond(eventTime).toString());
map.put("cover", cover);
map.put("popularity", item.getString("hot_value"));
map.put("link", "https://www.douyin.com/hot/" + item.getString("sentence_id"));
resultList.add(map);
}
return ConvertXmlUtil.convertToXml(resultList, Set.of());
} catch (Exception e) {
log.error("抖音热榜工具调用失败: {}", e.getMessage(), e);
return "获取抖音热搜失败,请稍后再试";
}
}
private String fetchCsrfToken() {
try {
String url = "https://www.douyin.com/passport/general/login_guiding_strategy/?aid=6383";
Connection.Response resp = Jsoup.connect(url)
.ignoreContentType(true)
.method(Connection.Method.GET)
.timeout(10000)
.execute();
for (String cookie : resp.headers("Set-Cookie")) {
Matcher m = Pattern.compile("passport_csrf_token=([^;]*);\\s*Path").matcher(cookie);
if (m.find()) return m.group(1);
}
} catch (Exception e) {
log.warn("获取抖音 CSRF Token 失败: {}", e.getMessage());
}
return null;
}
}Toutiao, NetEase News and Juejin Tools
Similar service classes are provided for Toutiao, NetEase News and Juejin. Each class follows the same pattern: request the platform’s API endpoint, verify HTTP status and JSON fields, transform each item into a map with a uniform set of keys (title, link, popularity, etc.), and finally return the collection as JSON or XML.
Key points demonstrated across all implementations:
Use of Jsoup for HTTP requests and content‑type‑agnostic responses.
Robust error handling: logging warnings for non‑200 status codes and returning clear error messages.
Filtering of advertisement entries (e.g., is_ad == 1 for Weibo).
Construction of user‑friendly URLs for each trend item.
Consistent return format (JSON or XML) to be consumed by MCP clients.
The article also shows the overall architecture of the MCP Server: a STDIO‑based List/Call interface at the protocol layer, a dynamic tool loader at the application layer, and ZOD‑based parameter definitions at the tool implementation layer. Diagrams (included as images) illustrate the server’s modular, protocol‑driven design and the tool registration/discovery flow.
Finally, the author encourages readers to explore the remaining open‑source tools in the repository, adapt them to other platforms, and experiment with extending the server’s capabilities.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Ubiquitous Tech
A ubiquitous public account for pirate enthusiasts, regularly sharing curated experiences, tech learning, and growth insights. Currently publishing articles on AI RAG customer service, AI MCP technology, and open-source design. Personal free Knowledge Planet: Awakening New World Programmer.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
