{"url_pattern":"^https?://(www\\.|m\\.)?weibo\\.(com|cn)/.*$","site_name":"weibo","allowed_domains":["s.weibo.com","weibo.com"],"tools":[{"name":"post_status","description":"Insert content into Weibo status composer","inputSchema":{"type":"object","properties":{"content":{"type":"string","description":"Status content (plain text, max 140 chars for standard post)"}},"required":["content"]},"handler":"(params) => {\n  const editor = document.querySelector('textarea[placeholder*=\"分享\"]') || document.querySelector('.Form_input_2gtXx') || document.querySelector('[contenteditable=\"true\"]') || document.querySelector('textarea');\n  if (!editor) {\n    return { success: false, message: 'Weibo composer not found' };\n  }\n  if (editor.tagName === 'TEXTAREA') {\n    editor.value = params.content;\n  } else {\n    editor.textContent = params.content;\n  }\n  editor.dispatchEvent(new Event('input', { bubbles: true }));\n  editor.focus();\n  const charCount = params.content.length;\n  return { success: true, message: 'Content inserted (' + charCount + ' chars)' };\n}"},{"name":"weibo_comments","description":"Get comments on a Weibo post","inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"Post ID (numeric idstr)"},"count":{"type":"string","description":"Number of comments (default: 20, max: 50)"},"max_id":{"type":"string","description":"Pagination cursor (from previous response)"}},"required":["id"]},"handler":"(params) => {\n  const run = async function(args) {\n\n      if (!args.id) return {error: 'Missing argument: id'};\n      const count = Math.min(parseInt(args.count) || 20, 50);\n\n      let url = '/ajax/statuses/buildComments?flow=0&is_reload=1&id=' + args.id + '&is_show_bulletin=2&is_mix=0&count=' + count;\n      if (args.max_id) url += '&max_id=' + args.max_id;\n\n      const resp = await fetch(url, {credentials: 'include'});\n      if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: resp.status === 404 ? 'Post not found' : 'Not logged in?'};\n      const data = await resp.json();\n      if (!data.ok) return {error: 'API error: ' + (data.msg || 'unknown'), hint: 'Not logged in?'};\n\n      const strip = (html) => (html || '').replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&').trim();\n\n      const comments = (data.data || []).map(c => ({\n        id: c.idstr || String(c.id),\n        text: strip(c.text || ''),\n        created_at: c.created_at,\n        likes_count: c.like_count || 0,\n        reply_count: c.total_number || 0,\n        user: {\n          id: c.user?.id,\n          screen_name: c.user?.screen_name,\n          verified: c.user?.verified || false\n        },\n        reply_to: c.reply_comment ? {\n          id: c.reply_comment.idstr || String(c.reply_comment.id),\n          user: c.reply_comment.user?.screen_name || '',\n          text: strip(c.reply_comment.text || '')\n        } : null\n      }));\n\n      return {\n        post_id: args.id,\n        count: comments.length,\n        max_id: data.max_id || null,\n        has_more: !!data.max_id,\n        comments\n      };\n  };\n  return run(params || {});\n}"},{"name":"weibo_feed","description":"Get Weibo home timeline (posts from followed users)","inputSchema":{"type":"object","properties":{"count":{"type":"string","description":"Number of posts (default: 15, max: 50)"}},"required":null},"handler":"(params) => {\n  const run = async function(args) {\n\n      const count = Math.min(parseInt(args.count) || 15, 50);\n\n      // Get user uid and list_id from Vuex store\n      const app = document.querySelector('#app')?.__vue_app__;\n      const store = app?.config?.globalProperties?.$store;\n      const cfg = store?.state?.config?.config;\n      const uid = cfg?.uid;\n      if (!uid) return {error: 'Not logged in', hint: 'Please log in to weibo.com first'};\n\n      const listId = '10001' + uid;\n      const resp = await fetch('/ajax/feed/unreadfriendstimeline?list_id=' + listId + '&refresh=4&since_id=0&count=' + count, {credentials: 'include'});\n      if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};\n      const data = await resp.json();\n      if (!data.ok) return {error: 'API error: ' + (data.msg || 'unknown'), hint: 'Not logged in?'};\n\n      const strip = (html) => (html || '').replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&').trim();\n\n      const statuses = (data.statuses || []).slice(0, count).map(s => {\n        const item = {\n          id: s.idstr || String(s.id),\n          mblogid: s.mblogid,\n          text: s.text_raw || strip(s.text || ''),\n          created_at: s.created_at,\n          source: strip(s.source || ''),\n          reposts_count: s.reposts_count || 0,\n          comments_count: s.comments_count || 0,\n          likes_count: s.attitudes_count || 0,\n          is_long_text: !!s.isLongText,\n          pic_count: s.pic_num || 0,\n          user: {\n            id: s.user?.id,\n            screen_name: s.user?.screen_name,\n            verified: s.user?.verified || false\n          },\n          url: 'https://weibo.com/' + (s.user?.id || '') + '/' + (s.mblogid || '')\n        };\n\n        // Include retweet info if present\n        if (s.retweeted_status) {\n          const rt = s.retweeted_status;\n          item.retweeted = {\n            id: rt.idstr || String(rt.id),\n            text: rt.text_raw || strip(rt.text || ''),\n            user: rt.user?.screen_name || '[deleted]',\n            reposts_count: rt.reposts_count || 0,\n            comments_count: rt.comments_count || 0,\n            likes_count: rt.attitudes_count || 0\n          };\n        }\n\n        return item;\n      });\n\n      return {count: statuses.length, statuses};\n  };\n  return run(params || {});\n}"},{"name":"weibo_hot","description":"Get Weibo hot search / trending topics","inputSchema":{"type":"object","properties":{"count":{"type":"string","description":"Number of items to return (default: 30, max: 50)"}},"required":null},"handler":"(params) => {\n  const run = async function(args) {\n\n      const count = Math.min(parseInt(args.count) || 30, 50);\n\n      const resp = await fetch('/ajax/statuses/hot_band', {credentials: 'include'});\n      if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};\n      const data = await resp.json();\n      if (!data.ok) return {error: 'API error', hint: 'Not logged in?'};\n\n      const bandList = data.data?.band_list || [];\n      const items = bandList.slice(0, count).map((item, i) => ({\n        rank: item.realpos || (i + 1),\n        word: item.word,\n        hot_value: item.num || 0,\n        raw_hot: item.raw_hot || 0,\n        category: item.category || '',\n        label: item.label_name || '',\n        is_new: !!item.is_new,\n        url: 'https://s.weibo.com/weibo?q=' + encodeURIComponent('#' + item.word + '#')\n      }));\n\n      const hotgov = data.data?.hotgov;\n      const top = hotgov ? {\n        word: hotgov.word || hotgov.name || '',\n        url: hotgov.url || ''\n      } : null;\n\n      return {count: items.length, top, items};\n  };\n  return run(params || {});\n}"},{"name":"weibo_me","description":"Get current logged-in Weibo user info","inputSchema":{"type":"object","properties":{},"required":null},"handler":"(params) => {\n  const run = async function(args) {\n\n      // Try Vuex store first (fastest, no network)\n      const app = document.querySelector('#app')?.__vue_app__;\n      const store = app?.config?.globalProperties?.$store;\n      const cfg = store?.state?.config?.config;\n\n      if (cfg?.user && cfg.uid) {\n        const u = cfg.user;\n        const detail = await fetch('/ajax/profile/detail?uid=' + cfg.uid, {credentials: 'include'})\n          .then(r => r.ok ? r.json() : null).catch(() => null);\n        const d = detail?.data || {};\n        return {\n          id: u.id,\n          screen_name: u.screen_name,\n          description: u.description || d.description || '',\n          location: u.location || '',\n          gender: u.gender === 'm' ? 'male' : u.gender === 'f' ? 'female' : 'unknown',\n          followers_count: u.followers_count,\n          following_count: u.friends_count,\n          statuses_count: u.statuses_count,\n          verified: u.verified || false,\n          domain: u.domain || '',\n          url: u.url || '',\n          avatar: u.avatar_hd || u.avatar_large || '',\n          profile_url: 'https://weibo.com' + (u.profile_url || '/u/' + u.id),\n          birthday: d.birthday || '',\n          created_at: d.created_at || '',\n          ip_location: d.ip_location || '',\n          company: d.company || '',\n          credit: d.sunshine_credit?.level || ''\n        };\n      }\n\n      // Fallback: fetch config API\n      const resp = await fetch('/ajax/config/get_config', {credentials: 'include'});\n      if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};\n      const data = await resp.json();\n      if (!data.ok || !data.data?.uid) return {error: 'Not logged in', hint: 'Please log in to weibo.com first'};\n      return {error: 'User data not available from config', hint: 'Navigate to weibo.com and reload'};\n  };\n  return run(params || {});\n}"},{"name":"weibo_post","description":"Get a single Weibo post by ID (numeric or mblogid)","inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"Post ID (numeric idstr) or mblogid (short alphanumeric ID from URL)"}},"required":["id"]},"handler":"(params) => {\n  const run = async function(args) {\n\n      if (!args.id) return {error: 'Missing argument: id'};\n\n      const resp = await fetch('/ajax/statuses/show?id=' + encodeURIComponent(args.id), {credentials: 'include'});\n      if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: resp.status === 404 ? 'Post not found or deleted' : 'Not logged in?'};\n      const s = await resp.json();\n      if (!s.ok && !s.idstr) return {error: 'Post not found', hint: 'Check the post ID'};\n\n      const strip = (html) => (html || '').replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&').trim();\n\n      // Fetch long text if needed\n      let fullText = s.text_raw || strip(s.text || '');\n      if (s.isLongText) {\n        const ltResp = await fetch('/ajax/statuses/longtext?id=' + s.idstr, {credentials: 'include'});\n        if (ltResp.ok) {\n          const lt = await ltResp.json();\n          if (lt.data?.longTextContent) {\n            fullText = strip(lt.data.longTextContent);\n          }\n        }\n      }\n\n      const result = {\n        id: s.idstr || String(s.id),\n        mblogid: s.mblogid,\n        text: fullText,\n        created_at: s.created_at,\n        source: strip(s.source || ''),\n        reposts_count: s.reposts_count || 0,\n        comments_count: s.comments_count || 0,\n        likes_count: s.attitudes_count || 0,\n        is_long_text: !!s.isLongText,\n        pic_count: s.pic_num || 0,\n        pics: (s.pic_ids || []).map(pid => {\n          const info = s.pic_infos?.[pid];\n          return info?.large?.url || info?.original?.url || null;\n        }).filter(Boolean),\n        user: {\n          id: s.user?.id,\n          screen_name: s.user?.screen_name,\n          verified: s.user?.verified || false,\n          verified_reason: s.user?.verified_reason || '',\n          followers_count: s.user?.followers_count\n        },\n        url: 'https://weibo.com/' + (s.user?.id || '') + '/' + (s.mblogid || '')\n      };\n\n      // Include retweet info\n      if (s.retweeted_status) {\n        const rt = s.retweeted_status;\n        result.retweeted = {\n          id: rt.idstr || String(rt.id),\n          mblogid: rt.mblogid,\n          text: rt.text_raw || strip(rt.text || ''),\n          user: {\n            id: rt.user?.id,\n            screen_name: rt.user?.screen_name || '[deleted]'\n          },\n          reposts_count: rt.reposts_count || 0,\n          comments_count: rt.comments_count || 0,\n          likes_count: rt.attitudes_count || 0,\n          url: 'https://weibo.com/' + (rt.user?.id || '') + '/' + (rt.mblogid || '')\n        };\n      }\n\n      return result;\n  };\n  return run(params || {});\n}"},{"name":"weibo_user","description":"Get Weibo user profile by uid (numeric) or screen_name","inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"User ID (numeric uid) or screen name"}},"required":["id"]},"handler":"(params) => {\n  const run = async function(args) {\n\n      if (!args.id) return {error: 'Missing argument: id', hint: 'Provide a numeric uid or screen_name'};\n\n      // Auto-detect: numeric string = uid, otherwise screen_name\n      const isUid = /^\\d+$/.test(args.id);\n      const query = isUid ? 'uid=' + args.id : 'screen_name=' + encodeURIComponent(args.id);\n\n      // Fetch basic profile info\n      const resp = await fetch('/ajax/profile/info?' + query, {credentials: 'include'});\n      if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};\n      const data = await resp.json();\n      if (!data.ok) return {error: 'User not found', hint: 'Check uid or screen_name'};\n\n      const u = data.data?.user;\n      if (!u) return {error: 'User not found', hint: 'Check uid or screen_name'};\n\n      // Fetch detailed profile info\n      const detailResp = await fetch('/ajax/profile/detail?uid=' + u.id, {credentials: 'include'});\n      const detail = detailResp.ok ? await detailResp.json() : null;\n      const d = detail?.data || {};\n\n      return {\n        id: u.id,\n        screen_name: u.screen_name,\n        description: u.description || d.description || '',\n        location: u.location || '',\n        gender: u.gender === 'm' ? 'male' : u.gender === 'f' ? 'female' : 'unknown',\n        followers_count: u.followers_count,\n        following_count: u.friends_count,\n        statuses_count: u.statuses_count,\n        verified: u.verified || false,\n        verified_type: u.verified_type,\n        verified_reason: u.verified_reason || '',\n        domain: u.domain || '',\n        url: u.url || '',\n        avatar: u.avatar_hd || u.avatar_large || '',\n        profile_url: 'https://weibo.com' + (u.profile_url || '/u/' + u.id),\n        birthday: d.birthday || '',\n        created_at: d.created_at || '',\n        ip_location: d.ip_location || '',\n        company: d.company || '',\n        credit: d.sunshine_credit?.level || '',\n        following: u.following || false,\n        follow_me: u.follow_me || false,\n        mbrank: u.mbrank || 0,\n        svip: u.svip || 0\n      };\n  };\n  return run(params || {});\n}"},{"name":"weibo_user_posts","description":"Get a Weibo user's posts (timeline)","inputSchema":{"type":"object","properties":{"uid":{"type":"string","description":"User ID (numeric)"},"page":{"type":"string","description":"Page number (default: 1)"},"feature":{"type":"string","description":"Filter: 0=all, 1=original, 2=picture, 3=video, 4=music (default: 0)"}},"required":["uid"]},"handler":"(params) => {\n  const run = async function(args) {\n\n      if (!args.uid) return {error: 'Missing argument: uid'};\n      const page = parseInt(args.page) || 1;\n      const feature = parseInt(args.feature) || 0;\n\n      const resp = await fetch('/ajax/statuses/mymblog?uid=' + args.uid + '&page=' + page + '&feature=' + feature, {credentials: 'include'});\n      if (!resp.ok) return {error: 'HTTP ' + resp.status, hint: 'Not logged in?'};\n      const data = await resp.json();\n      if (!data.ok) return {error: 'API error: ' + (data.msg || 'unknown'), hint: 'Not logged in? Or user does not exist.'};\n\n      const strip = (html) => (html || '').replace(/<[^>]+>/g, '').replace(/&nbsp;/g, ' ').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&').trim();\n\n      const list = (data.data?.list || []).map(s => {\n        const item = {\n          id: s.idstr || String(s.id),\n          mblogid: s.mblogid,\n          text: s.text_raw || strip(s.text || ''),\n          created_at: s.created_at,\n          source: strip(s.source || ''),\n          reposts_count: s.reposts_count || 0,\n          comments_count: s.comments_count || 0,\n          likes_count: s.attitudes_count || 0,\n          is_long_text: !!s.isLongText,\n          pic_count: s.pic_num || 0,\n          url: 'https://weibo.com/' + args.uid + '/' + (s.mblogid || '')\n        };\n\n        if (s.retweeted_status) {\n          const rt = s.retweeted_status;\n          item.retweeted = {\n            id: rt.idstr || String(rt.id),\n            text: rt.text_raw || strip(rt.text || ''),\n            user: rt.user?.screen_name || '[deleted]'\n          };\n        }\n\n        return item;\n      });\n\n      return {\n        uid: args.uid,\n        page,\n        total: data.data?.total || 0,\n        count: list.length,\n        posts: list\n      };\n  };\n  return run(params || {});\n}"}]}