WORLDBOOK

Worldbooks | WebMCP | Search | Submit WebMCP

hackernews WebMCP

Browser tool configuration for hackernews

URL Pattern: ^https?://news\.ycombinator\.com(/.*)?$
Allowed Extra Domains: example.com, hacker-news.firebaseio.com, hn.algolia.com, news.ycombinator.com

Tools (7)

hackernews_top()

获取 Hacker News 当前热门帖子

Parameters

count string - Number of posts (default: 20, max: 50)

JavaScript Handler

(params) => {
  const run = async function(args) {

      const count = Math.min(parseInt(args.count) || 20, 50);
      // HN Firebase API is public, no auth needed
      const idsResp = await fetch('https://hacker-news.firebaseio.com/v0/topstories.json');
      if (!idsResp.ok) return {error: 'HTTP ' + idsResp.status};
      const ids = await idsResp.json();
      const topIds = ids.slice(0, count);

      const items = await Promise.all(topIds.map(async id => {
        const resp = await fetch('https://hacker-news.firebaseio.com/v0/item/' + id + '.json');
        return await resp.json();
      }));

      return {
        count: items.length,
        posts: items.map((item, i) => ({
          rank: i + 1, id: item.id, title: item.title,
          url: item.url || null,
          hn_url: 'https://news.ycombinator.com/item?id=' + item.id,
          author: item.by, score: item.score,
          comments: item.descendants || 0, time: item.time
        }))
      };
  };
  return run(params || {});
}

hackernews_new()

Hacker News newest stories

Parameters

limit number - Number of stories

JavaScript Handler

(params) => {
  const args = Object.assign({"limit": 20}, params || {});
  let data = null;
  let __wbContextUrl = globalThis.location?.href || '';
  let __wbContextDocument = globalThis.document || null;
    const __wbDefault = (value, fallback) => (value === undefined || value === null || value === '' ? fallback : value);
    const __wbJoin = (value, separator) => Array.isArray(value) ? value.join(separator) : (value ?? '');
    const __wbGet = (value, path) => {
      if (!path) return value;
      return String(path).split('.').reduce((acc, part) => (acc == null ? undefined : acc[part]), value);
    };
    const __wbBaseUrl = () => (__wbContextUrl || globalThis.location?.href || 'https://example.com/');
    const __wbResolve = (target, base) => {
      if (target == null) return '';
      const text = String(target);
      try {
        return /^https?:/i.test(text) ? text : new URL(text, base || __wbBaseUrl()).toString();
      } catch (_error) {
        return text;
      }
    };
    const __wbFetch = async (target, options) => {
      const url = __wbResolve(target, __wbBaseUrl());
      const resp = await globalThis.fetch(url, Object.assign({ credentials: 'include' }, options || {}));
      if (!resp.ok) {
        throw new Error(`HTTP ${resp.status} for ${url}`);
      }
      const text = await resp.text();
      try {
        return { url, text, data: JSON.parse(text) };
      } catch (_error) {
        return { url, text, data: text };
      }
    };
  return (async () => {
    {
      const __wbResp = await __wbFetch("https://hacker-news.firebaseio.com/v0/newstories.json");
      data = __wbResp.data;
    }
    if (Array.isArray(data)) data = data.slice(0, Number(Math.min((args.limit ? args.limit : 20) + 10, 50)) || 0);
    {
      const source = Array.isArray(data) ? data : (data == null ? [] : [data]);
      data = source.map((item, index) => ({
        "id": item,
      }));
    }
    {
      const source = Array.isArray(data) ? data : (data == null ? [] : [data]);
      const next = [];
      for (let index = 0; index < source.length; index += 1) {
        const item = source[index];
        const __wbResp = await __wbFetch(`https://hacker-news.firebaseio.com/v0/item/${item.id}.json`);
        next.push(__wbResp.data);
      }
      data = next;
    }
    data = (Array.isArray(data) ? data : (data == null ? [] : [data])).filter((item, index) => Boolean(item.title && !item.deleted && !item.dead));
    {
      const source = Array.isArray(data) ? data : (data == null ? [] : [data]);
      data = source.map((item, index) => ({
        "rank": index + 1,
        "title": item.title,
        "score": item.score,
        "author": item.by,
        "comments": item.descendants,
        "url": item.url,
      }));
    }
    if (Array.isArray(data)) data = data.slice(0, Number(args.limit) || 0);
    return data;
  })();
}

hackernews_ask()

Hacker News Ask HN posts

Parameters

limit number - Number of stories

JavaScript Handler

(params) => {
  const args = Object.assign({"limit": 20}, params || {});
  let data = null;
  let __wbContextUrl = globalThis.location?.href || '';
  let __wbContextDocument = globalThis.document || null;
    const __wbDefault = (value, fallback) => (value === undefined || value === null || value === '' ? fallback : value);
    const __wbJoin = (value, separator) => Array.isArray(value) ? value.join(separator) : (value ?? '');
    const __wbGet = (value, path) => {
      if (!path) return value;
      return String(path).split('.').reduce((acc, part) => (acc == null ? undefined : acc[part]), value);
    };
    const __wbBaseUrl = () => (__wbContextUrl || globalThis.location?.href || 'https://example.com/');
    const __wbResolve = (target, base) => {
      if (target == null) return '';
      const text = String(target);
      try {
        return /^https?:/i.test(text) ? text : new URL(text, base || __wbBaseUrl()).toString();
      } catch (_error) {
        return text;
      }
    };
    const __wbFetch = async (target, options) => {
      const url = __wbResolve(target, __wbBaseUrl());
      const resp = await globalThis.fetch(url, Object.assign({ credentials: 'include' }, options || {}));
      if (!resp.ok) {
        throw new Error(`HTTP ${resp.status} for ${url}`);
      }
      const text = await resp.text();
      try {
        return { url, text, data: JSON.parse(text) };
      } catch (_error) {
        return { url, text, data: text };
      }
    };
  return (async () => {
    {
      const __wbResp = await __wbFetch("https://hacker-news.firebaseio.com/v0/askstories.json");
      data = __wbResp.data;
    }
    if (Array.isArray(data)) data = data.slice(0, Number(Math.min((args.limit ? args.limit : 20) + 10, 50)) || 0);
    {
      const source = Array.isArray(data) ? data : (data == null ? [] : [data]);
      data = source.map((item, index) => ({
        "id": item,
      }));
    }
    {
      const source = Array.isArray(data) ? data : (data == null ? [] : [data]);
      const next = [];
      for (let index = 0; index < source.length; index += 1) {
        const item = source[index];
        const __wbResp = await __wbFetch(`https://hacker-news.firebaseio.com/v0/item/${item.id}.json`);
        next.push(__wbResp.data);
      }
      data = next;
    }
    data = (Array.isArray(data) ? data : (data == null ? [] : [data])).filter((item, index) => Boolean(item.title && !item.deleted && !item.dead));
    {
      const source = Array.isArray(data) ? data : (data == null ? [] : [data]);
      data = source.map((item, index) => ({
        "rank": index + 1,
        "title": item.title,
        "score": item.score,
        "author": item.by,
        "comments": item.descendants,
        "url": item.url,
      }));
    }
    if (Array.isArray(data)) data = data.slice(0, Number(args.limit) || 0);
    return data;
  })();
}

hackernews_jobs()

Hacker News job postings

Parameters

limit number - Number of job postings

JavaScript Handler

(params) => {
  const args = Object.assign({"limit": 20}, params || {});
  let data = null;
  let __wbContextUrl = globalThis.location?.href || '';
  let __wbContextDocument = globalThis.document || null;
    const __wbDefault = (value, fallback) => (value === undefined || value === null || value === '' ? fallback : value);
    const __wbJoin = (value, separator) => Array.isArray(value) ? value.join(separator) : (value ?? '');
    const __wbGet = (value, path) => {
      if (!path) return value;
      return String(path).split('.').reduce((acc, part) => (acc == null ? undefined : acc[part]), value);
    };
    const __wbBaseUrl = () => (__wbContextUrl || globalThis.location?.href || 'https://example.com/');
    const __wbResolve = (target, base) => {
      if (target == null) return '';
      const text = String(target);
      try {
        return /^https?:/i.test(text) ? text : new URL(text, base || __wbBaseUrl()).toString();
      } catch (_error) {
        return text;
      }
    };
    const __wbFetch = async (target, options) => {
      const url = __wbResolve(target, __wbBaseUrl());
      const resp = await globalThis.fetch(url, Object.assign({ credentials: 'include' }, options || {}));
      if (!resp.ok) {
        throw new Error(`HTTP ${resp.status} for ${url}`);
      }
      const text = await resp.text();
      try {
        return { url, text, data: JSON.parse(text) };
      } catch (_error) {
        return { url, text, data: text };
      }
    };
  return (async () => {
    {
      const __wbResp = await __wbFetch("https://hacker-news.firebaseio.com/v0/jobstories.json");
      data = __wbResp.data;
    }
    if (Array.isArray(data)) data = data.slice(0, Number(Math.min((args.limit ? args.limit : 20) + 10, 50)) || 0);
    {
      const source = Array.isArray(data) ? data : (data == null ? [] : [data]);
      data = source.map((item, index) => ({
        "id": item,
      }));
    }
    {
      const source = Array.isArray(data) ? data : (data == null ? [] : [data]);
      const next = [];
      for (let index = 0; index < source.length; index += 1) {
        const item = source[index];
        const __wbResp = await __wbFetch(`https://hacker-news.firebaseio.com/v0/item/${item.id}.json`);
        next.push(__wbResp.data);
      }
      data = next;
    }
    data = (Array.isArray(data) ? data : (data == null ? [] : [data])).filter((item, index) => Boolean(item.title && !item.deleted && !item.dead));
    {
      const source = Array.isArray(data) ? data : (data == null ? [] : [data]);
      data = source.map((item, index) => ({
        "rank": index + 1,
        "title": item.title,
        "author": item.by,
        "url": item.url,
      }));
    }
    if (Array.isArray(data)) data = data.slice(0, Number(args.limit) || 0);
    return data;
  })();
}

hackernews_search()

Search Hacker News stories

Parameters

query string required - Search query
limit number - Number of results
sort string - Sort by relevance or date

JavaScript Handler

hackernews_thread()

获取 Hacker News 帖子的评论树

Parameters

id string required - HN item ID or URL

JavaScript Handler

(params) => {
  const run = async function(args) {

      if (!args.id) return {error: 'Missing argument: id'};
      let itemId = args.id;
      const urlMatch = itemId.match(/id=(\d+)/);
      if (urlMatch) itemId = urlMatch[1];

      const resp = await fetch('https://hacker-news.firebaseio.com/v0/item/' + itemId + '.json');
      if (!resp.ok) return {error: 'HTTP ' + resp.status};
      const item = await resp.json();
      if (!item) return {error: 'Item not found', hint: 'Check the ID: ' + itemId};

      // Fetch comment tree (max 2 levels deep for performance)
      async function fetchComments(ids, depth) {
        if (!ids || ids.length === 0 || depth > 2) return [];
        const comments = await Promise.all(ids.slice(0, 30).map(async id => {
          const r = await fetch('https://hacker-news.firebaseio.com/v0/item/' + id + '.json');
          const c = await r.json();
          if (!c || c.deleted || c.dead) return null;
          return {
            id: c.id, author: c.by, text: c.text, time: c.time, depth,
            replies: await fetchComments(c.kids, depth + 1)
          };
        }));
        return comments.filter(Boolean);
      }

      const comments = await fetchComments(item.kids, 0);
      return {
        post: {id: item.id, title: item.title, url: item.url || null, hn_url: 'https://news.ycombinator.com/item?id=' + item.id, author: item.by, score: item.score, comments_count: item.descendants || 0, time: item.time, text: item.text},
        comments
      };
  };
  return run(params || {});
}

hackernews_user()

Hacker News user profile

Parameters

username string required - HN username

JavaScript Handler

(params) => {
  const args = Object.assign({}, params || {});
  let data = null;
  let __wbContextUrl = globalThis.location?.href || '';
  let __wbContextDocument = globalThis.document || null;
    const __wbDefault = (value, fallback) => (value === undefined || value === null || value === '' ? fallback : value);
    const __wbJoin = (value, separator) => Array.isArray(value) ? value.join(separator) : (value ?? '');
    const __wbGet = (value, path) => {
      if (!path) return value;
      return String(path).split('.').reduce((acc, part) => (acc == null ? undefined : acc[part]), value);
    };
    const __wbBaseUrl = () => (__wbContextUrl || globalThis.location?.href || 'https://example.com/');
    const __wbResolve = (target, base) => {
      if (target == null) return '';
      const text = String(target);
      try {
        return /^https?:/i.test(text) ? text : new URL(text, base || __wbBaseUrl()).toString();
      } catch (_error) {
        return text;
      }
    };
    const __wbFetch = async (target, options) => {
      const url = __wbResolve(target, __wbBaseUrl());
      const resp = await globalThis.fetch(url, Object.assign({ credentials: 'include' }, options || {}));
      if (!resp.ok) {
        throw new Error(`HTTP ${resp.status} for ${url}`);
      }
      const text = await resp.text();
      try {
        return { url, text, data: JSON.parse(text) };
      } catch (_error) {
        return { url, text, data: text };
      }
    };
  return (async () => {
    {
      const __wbResp = await __wbFetch(`https://hacker-news.firebaseio.com/v0/user/${args.username}.json`);
      data = __wbResp.data;
    }
    {
      const source = Array.isArray(data) ? data : (data == null ? [] : [data]);
      data = source.map((item, index) => ({
        "username": item.id,
        "karma": item.karma,
        "created": item.created ? new Date(item.created * 1000).toISOString().slice(0, 10) : '',
        "about": item.about,
      }));
    }
    return data;
  })();
}

🔌 Chrome MCP Server Extension

Use these tools with Claude, ChatGPT, and other AI assistants.

Get Extension →

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.hackernews.com/...