hermoso 0.1.408 → 0.1.409
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/mcp/tools.mjs +12 -8
- package/package.json +1 -1
package/mcp/tools.mjs
CHANGED
|
@@ -7123,11 +7123,13 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
7123
7123
|
}));
|
|
7124
7124
|
server.registerTool('list_x_chats', {
|
|
7125
7125
|
title: 'List X Chat conversations',
|
|
7126
|
-
description: "The connected X account's X CHAT conversations: id (group ids start with g), type, muted, disappearing-message timer, screenshot controls, admins, members, PENDING join requests, which actions are admin-only, and the active invite link. X Chat is END-TO-END ENCRYPTED, so message text and group names cannot be read here (list_x_dms reads the plain direct-message history). view 'admin_requests' (id = a group the brand admins) lists who asked to become an admin; view 'invite' (inviteLink, or id + inviteToken) previews an invite link: group name, member count, whether the brand is in it. withMembers:true resolves member @handles, and each user X returns is billed. Read-only. Billed per conversation returned, so keep maxResults small." + XC_SCOPE_NOTE,
|
|
7126
|
+
description: "The connected X account's X CHAT conversations: id (group ids start with g), type, muted, disappearing-message timer, screenshot controls, admins, members, PENDING join requests, which actions are admin-only, and the active invite link. X Chat is END-TO-END ENCRYPTED, so message text and group names cannot be read here (list_x_dms reads the plain direct-message history). view 'admin_requests' (id = a group the brand admins) lists who asked to become an admin; view 'invite' (inviteLink, or id + inviteToken) previews an invite link: group name, member count, whether the brand is in it. view 'message_requests' lists the MESSAGE REQUESTS waiting outside the inbox (inbox 'priority', the default, or 'low_quality' for X's filtered tab): who sent each one (@handle, name, followers, verified), which accounts the brand follows also follow them, and whether they changed their name or picture recently (X's impersonation hint); the message itself is encrypted. Show that to the user before manage_x_chat accept_request. withMembers:true resolves member @handles, and each user X returns is billed. Read-only. Billed per conversation or request returned, plus one user lookup per request sender, so keep maxResults small." + XC_SCOPE_NOTE,
|
|
7127
7127
|
inputSchema: {
|
|
7128
|
-
view: z.enum(['conversations', 'admin_requests', 'invite']).optional().describe("default 'conversations'"),
|
|
7128
|
+
view: z.enum(['conversations', 'admin_requests', 'invite', 'message_requests']).optional().describe("default 'conversations'"),
|
|
7129
|
+
inbox: z.enum(['priority', 'low_quality']).optional().describe("message_requests: X's Priority tab (default) or the filtered low-quality one"),
|
|
7130
|
+
withSenders: z.boolean().optional().describe('message_requests: resolve who sent each request (default true, each sender billed)'),
|
|
7129
7131
|
id: z.string().optional().describe('one conversation; for admin_requests, the group (g + digits)'),
|
|
7130
|
-
maxResults: z.number().optional().describe('1-100, default 10; every conversation returned is billed'),
|
|
7132
|
+
maxResults: z.number().optional().describe('1-100 (message_requests 1-50), default 10; every conversation or request returned is billed'),
|
|
7131
7133
|
paginationToken: z.string().optional().describe('nextToken from a previous call'),
|
|
7132
7134
|
withMembers: z.boolean().optional().describe('also resolve member/admin @handles (each one billed)'),
|
|
7133
7135
|
inviteLink: z.string().optional().describe('an x.com/i/chat/group_join/... link, for view invite'),
|
|
@@ -7139,15 +7141,16 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
7139
7141
|
const cost = `Cost ${d.costCredits ?? '?'} credits.`;
|
|
7140
7142
|
if (d.view === 'invite') { const i = d.invite || {}; return ok(`Invite ${i.url || '?'}: "${i.groupName || '(no name)'}", ${i.memberCount ?? '?'} members, the brand is ${i.joinState || '?'}${i.expiresAt ? `, expires ${i.expiresAt}` : ''}. ${cost}`, d); }
|
|
7141
7143
|
if (d.view === 'admin_requests') return ok(`${d.count} admin request(s) in ${d.id}:\n` + (d.requests || []).map((u) => `- ${u.username || u.id} (${u.id})`).join('\n') + `\n${cost}`, d);
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
+
if (d.view === 'message_requests') return ok(`${d.count} message request(s) in the ${d.inbox === 'low_quality' ? 'low-quality' : 'Priority'} tab${d.nextToken ? ' (more available, pass paginationToken)' : ''}:\n` + (d.requests || []).map((m) => `- ${(m.sender && m.sender.username) || m.senderId}${m.sender && m.sender.name ? ` (${m.sender.name})` : ''}, id ${m.senderId}${m.sender && m.sender.followers != null ? `, ${m.sender.followers} followers` : ''}${m.sender && m.sender.verified ? ', verified' : ''}${m.followedByIds && m.followedByIds.length ? `, followed by ${m.followedByIds.length} account(s) you follow` : ''}${m.recentlyChangedProfile ? ', WARNING: changed name or picture recently' : ''}`).join('\n') + `\n${d.note || ''} ${cost}`.trim(), d);
|
|
7145
|
+
const rows = (d.conversations || []).slice(0, 25).map((c) => `- ${c.id} ${c.isGroup ? 'group' : '1:1'}${c.unread ? ', unread' : ''}${c.archived ? ', archived' : ''}${c.muted ? ', muted' : ''}${c.disappearAfterMs ? `, messages disappear after ${Math.round(c.disappearAfterMs / 3600e3)}h` : ''}${c.memberIds ? `, ${c.memberIds.length} members` : ''}${c.pendingJoinIds && c.pendingJoinIds.length ? `, ${c.pendingJoinIds.length} waiting to join` : ''}${c.invite && c.invite.url ? `, invite ${c.invite.url}` : ''}`);
|
|
7146
|
+
return ok(`${d.count} X Chat conversation(s)${d.nextToken ? ' (more available, pass paginationToken)' : ''}${d.hasMessageRequests ? '; message requests are waiting (view message_requests)' : ''}:\n${rows.join('\n') || '(none)'}\n${d.note || ''} ${cost}`.trim(), d);
|
|
7144
7147
|
}));
|
|
7145
7148
|
server.registerTool('manage_x_chat', {
|
|
7146
7149
|
title: 'Manage an X Chat conversation',
|
|
7147
|
-
description: "Manage one of the connected X account's X Chat conversations (id from list_x_chats). For the brand only: 'mute' / 'unmute'; 'delete' removes it from the brand's inbox for good while the others keep theirs (confirm:true). For everyone in it: 'set_disappearing' (disappearAfter like '24h' or '7d'), 'clear_disappearing', 'screen_capture' (blockScreenshots OR notifyOnScreenshot, one per call). GROUPS (id starts with g; the brand must be an admin): 'admin_settings' (adminOnly {addMember, editGroupInfo, sendMessage, startCall, editMessageTtl, blockScreenCapture}; sendMessage:true makes it announcement-only), 'add_admins' / 'remove_admins' (userIds), 'remove_members' (userIds, confirm:true), 'leave' (confirm:true), 'approve_admin_request' / 'reject_admin_request' (userId), 'reject_join_requests' (userIds; APPROVING a join re-keys the encrypted group, so it is done in the X app), 'enable_invite' (optional title, avatarUrl, welcomeMessage, expiresAt; X stores that preview UNENCRYPTED and shows it to anyone with the link), 'disable_invite' (the shared link dies for good: confirm:true). Confirm the exact conversation with the user before passing confirm:true; a refusal is free. The answer is X's own yes or no, never an echo." + XC_SCOPE_NOTE,
|
|
7150
|
+
description: "Manage one of the connected X account's X Chat conversations (id from list_x_chats). For the brand only: 'mute' / 'unmute'; 'delete' removes it from the brand's inbox for good while the others keep theirs (confirm:true). For everyone in it: 'set_disappearing' (disappearAfter like '24h' or '7d'), 'clear_disappearing', 'screen_capture' (blockScreenshots OR notifyOnScreenshot, one per call). GROUPS (id starts with g; the brand must be an admin): 'admin_settings' (adminOnly {addMember, editGroupInfo, sendMessage, startCall, editMessageTtl, blockScreenCapture}; sendMessage:true makes it announcement-only), 'add_admins' / 'remove_admins' (userIds), 'remove_members' (userIds, confirm:true), 'leave' (confirm:true), 'approve_admin_request' / 'reject_admin_request' (userId), 'reject_join_requests' (userIds; APPROVING a join re-keys the encrypted group, so it is done in the X app), 'enable_invite' (optional title, avatarUrl, welcomeMessage, expiresAt; X stores that preview UNENCRYPTED and shows it to anyone with the link), 'disable_invite' (the shared link dies for good: confirm:true). INBOX, for the brand only: 'archive' / 'unarchive', 'mark_read' (may send the other person a read receipt). MESSAGE REQUESTS: 'accept_request' (id = the sender's numeric id, or the request's conversationId from list_x_chats view message_requests) moves the conversation into the inbox; show the user who sent it first, since the message itself cannot be read here. Confirm the exact conversation with the user before passing confirm:true; a refusal is free. The answer is X's own yes or no, never an echo." + XC_SCOPE_NOTE,
|
|
7148
7151
|
inputSchema: {
|
|
7149
|
-
action: z.enum(['mute', 'unmute', 'delete', 'set_disappearing', 'clear_disappearing', 'screen_capture', 'admin_settings', 'add_admins', 'remove_admins', 'remove_members', 'leave', 'approve_admin_request', 'reject_admin_request', 'reject_join_requests', 'enable_invite', 'disable_invite']),
|
|
7150
|
-
id: z.string().describe('conversation id from list_x_chats: g + digits for a group, or the other person\'s numeric id for a 1:1'),
|
|
7152
|
+
action: z.enum(['mute', 'unmute', 'delete', 'set_disappearing', 'clear_disappearing', 'screen_capture', 'admin_settings', 'add_admins', 'remove_admins', 'remove_members', 'leave', 'approve_admin_request', 'reject_admin_request', 'reject_join_requests', 'enable_invite', 'disable_invite', 'accept_request', 'archive', 'unarchive', 'mark_read']),
|
|
7153
|
+
id: z.string().describe('conversation id from list_x_chats: g + digits for a group, or the other person\'s numeric id for a 1:1 (accept_request: the sender\'s numeric id)'),
|
|
7151
7154
|
userIds: z.array(z.string()).optional().describe('NUMERIC X account ids (add_admins, remove_admins, remove_members, reject_join_requests)'),
|
|
7152
7155
|
userId: z.string().optional().describe('NUMERIC X account id (approve_admin_request, reject_admin_request)'),
|
|
7153
7156
|
disappearAfter: z.string().optional().describe("set_disappearing: '24h', '7d', '90m' or a number of seconds"),
|
|
@@ -7164,6 +7167,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
7164
7167
|
const d = await apiPost('/api/x/chats/manage', a);
|
|
7165
7168
|
const cost = `Cost ${d.costCredits ?? '?'} credits.`;
|
|
7166
7169
|
if (d.action === 'enable_invite') return ok(d.confirmed ? `Invite link on: ${d.invite && d.invite.url}${d.invite && d.invite.expiresAt ? ` (expires ${d.invite.expiresAt})` : ''}. ${d.note || ''} ${cost}` : `X did not return an invite link. ${cost}`, d);
|
|
7170
|
+
if (d.action === 'accept_request') return ok(d.confirmed ? `Accepted: the conversation with ${d.id} is in the brand's X inbox now. ${cost}` : `X did not confirm accepting the request from ${d.id}. ${cost}`, d);
|
|
7167
7171
|
return ok(`${d.confirmed ? 'Done' : 'X did not confirm it'}: ${String(d.action).replace(/_/g, ' ')} on ${d.id}${d.userIds ? ` (${d.userIds.join(', ')})` : ''}. ${cost}`, d);
|
|
7168
7172
|
}));
|
|
7169
7173
|
server.registerTool('x_user', {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hermoso",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.409",
|
|
4
4
|
"mcpName": "io.github.hermoso-ai/hermoso",
|
|
5
5
|
"description": "AI marketing agent and AI ad generator, run from your own AI agent. 906 tools. Publishing, scheduling, ad campaign management, comments, DMs and analytics cost no credits on every plan; credits are only for generating creative and for Ad Spy research. AD PLATFORMS: Meta, Google Ads, TikTok Ads, LinkedIn Ads, Reddit Ads, X Ads, Pinterest Ads, Snapchat Ads, Microsoft Advertising, Apple Search Ads, ChatGPT Ads and AppLovin Ads, plus product feeds in Google Merchant Center. PUBLISHING AND SCHEDULING: Facebook, Instagram, Threads, TikTok, YouTube, X, LinkedIn, Pinterest, Bluesky, Telegram and Google Business Profile. INSTAGRAM DM AUTOMATIONS: a keyword comment, a DM, a story reply or an ig.me link gets an automatic private reply from the brand. AD RESEARCH: the Meta, Google and LinkedIn ad libraries plus organic TikTok, Instagram, YouTube, Threads and Reddit. ANALYTICS: Google Analytics 4, Google Search Console and every connected platform's own post and campaign insights. Also brand onboarding, 50+ image and video generation models, ad scoring, competitor teardowns, Google Drive and OneDrive, a CLI and installable Claude skills.",
|
|
6
6
|
"type": "module",
|