
Farcaster
- 13 installs
- 610 repo stars
- Updated June 26, 2026
- alsk1992/cloddsbot
Farcaster is a Claude Code skill that reads feeds and posts casts on the Farcaster social protocol via the Neynar API.
About
Farcaster is a skill that connects an agent to the Farcaster decentralized social protocol through the Neynar API. A developer uses /fc commands to look up users, read feeds and trending casts, search casts and channels, and (with a signer) post, reply, like, recast, and follow. It requires a NEYNAR_API_KEY and an optional signer UUID for write operations.
- Interacts with the Farcaster social protocol via the Neynar API
- Reads feeds, trending casts, channels, and user profiles; searches casts and users
- Write operations (post, reply, like, recast, follow) via an optional Neynar signer
Farcaster by the numbers
- 13 all-time installs (skills.sh)
- Ranked #1,436 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
farcaster capabilities & compatibility
Requires a NEYNAR_API_KEY (free tier 300 req/min); posting also needs a signer UUID.
- Capabilities
- read feed · post cast · user lookup · channel discovery
- Pricing
- Bring your own API key
What farcaster says it does
Interact with the Farcaster decentralized social protocol via Neynar API.
Free tier: 300 requests/minute
npx skills add https://github.com/alsk1992/cloddsbot --skill farcasterAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 13 |
|---|---|
| repo stars | ★ 610 |
| Last updated | June 26, 2026 |
| Repository | alsk1992/cloddsbot ↗ |
What it does
Read Farcaster feeds and profiles and post casts, replies, likes, and follows via the Neynar API.
Who is it for?
Reading and writing to Farcaster from an agent through the Neynar API.
Skip if: Non-Farcaster social networks or use without a Neynar API key.
When should I use this skill?
You want an agent to read Farcaster feeds or post casts and follow users via Neynar.
What you get
Agent access to Farcaster feeds, profiles, and channels, plus posting, replying, liking, and following.
By the numbers
- Free tier: 300 requests/minute
Files
Farcaster
Interact with the Farcaster decentralized social protocol via Neynar API.
Setup
Get your API key from dev.neynar.com:
export NEYNAR_API_KEY=xxx
# Optional: For posting (requires signer)
export NEYNAR_SIGNER_UUID=xxxCommands
Users
/fc user <username> Look up user profile
/fc search-users <query> Search for usersFeeds
/fc feed [--channel X] Get feed (optionally from channel)
/fc trending Trending casts
/fc channel <id> Get channel info
/fc channels <query> Search channelsSearch
/fc search <query> Search castsWrite Operations (require signer)
/fc post <text> Post a cast
/fc reply <hash> <text> Reply to a cast
/fc like <hash> Like a cast
/fc recast <hash> Recast
/fc follow <username> Follow user
/fc unfollow <username> Unfollow userExamples
/fc user vitalik.eth
/fc trending
/fc channel base
/fc search "ethereum"
/fc post "Hello Farcaster!"
/fc follow dwr.ethRate Limits
- Free tier: 300 requests/minute
- Standard: 1,000 requests/minute
- Premium: Higher limits
Features
- User profiles with verified addresses
- Cast search and feeds
- Channel discovery
- Trending content
- Full write operations (with signer)
/**
* Farcaster Skill - Social protocol via Neynar API
*
* Commands:
* /fc user <username> Look up user
* /fc search-users <query> Search users
* /fc feed [--channel X] Get feed
* /fc trending Trending casts
* /fc search <query> Search casts
* /fc channel <id> Channel info
* /fc channels <query> Search channels
* /fc post <text> Post cast
* /fc reply <hash> <text> Reply to cast
* /fc like <hash> Like cast
* /fc recast <hash> Recast
* /fc follow <username> Follow user
* /fc unfollow <username> Unfollow user
*/
import { getNeynarClient, type Cast, type FarcasterUser, type Channel } from '../../../farcaster';
// =============================================================================
// Execute
// =============================================================================
export async function execute(args: string): Promise<string> {
const trimmed = args.trim();
if (!trimmed || trimmed === 'help' || trimmed === '--help') {
return getHelp();
}
const [cmd, ...rest] = trimmed.split(/\s+/);
const restStr = rest.join(' ');
try {
switch (cmd) {
case 'user':
return handleUser(restStr);
case 'search-users':
return handleSearchUsers(restStr);
case 'feed':
return handleFeed(restStr);
case 'trending':
return handleTrending();
case 'search':
return handleSearch(restStr);
case 'channel':
return handleChannel(restStr);
case 'channels':
return handleSearchChannels(restStr);
case 'post':
return handlePost(restStr);
case 'reply':
return handleReply(restStr);
case 'like':
return handleLike(restStr);
case 'recast':
return handleRecast(restStr);
case 'follow':
return handleFollow(restStr);
case 'unfollow':
return handleUnfollow(restStr);
default:
return `Unknown command: ${cmd}\n\n${getHelp()}`;
}
} catch (error) {
const msg = error instanceof Error ? error.message : String(error);
return `❌ Error: ${msg}`;
}
}
// =============================================================================
// Handlers
// =============================================================================
async function handleUser(username: string): Promise<string> {
if (!username) return '❌ Usage: /fc user <username>';
const client = getNeynarClient();
const user = await client.getUserByUsername(username.replace('@', ''));
return formatUser(user);
}
async function handleSearchUsers(query: string): Promise<string> {
if (!query) return '❌ Usage: /fc search-users <query>';
const client = getNeynarClient();
const users = await client.searchUsers(query, 10);
if (users.length === 0) return 'No users found.';
return users.map((u) => `@${u.username} (${u.displayName}) - ${u.followerCount} followers`).join('\n');
}
async function handleFeed(args: string): Promise<string> {
const client = getNeynarClient();
// Parse --channel flag
const channelMatch = args.match(/--channel\s+(\S+)/);
const channelId = channelMatch?.[1];
let casts: Cast[];
if (channelId) {
casts = await client.getChannelFeed(channelId, { limit: 10 });
} else {
casts = await client.getTrendingCasts({ limit: 10 });
}
if (casts.length === 0) return 'No casts found.';
return casts.map(formatCast).join('\n\n---\n\n');
}
async function handleTrending(): Promise<string> {
const client = getNeynarClient();
const casts = await client.getTrendingCasts({ limit: 10 });
if (casts.length === 0) return 'No trending casts.';
return '**Trending on Farcaster**\n\n' + casts.map(formatCast).join('\n\n---\n\n');
}
async function handleSearch(query: string): Promise<string> {
if (!query) return '❌ Usage: /fc search <query>';
const client = getNeynarClient();
const casts = await client.searchCasts(query, { limit: 10 });
if (casts.length === 0) return 'No casts found.';
return casts.map(formatCast).join('\n\n---\n\n');
}
async function handleChannel(channelId: string): Promise<string> {
if (!channelId) return '❌ Usage: /fc channel <id>';
const client = getNeynarClient();
const channel = await client.getChannel(channelId);
return formatChannel(channel);
}
async function handleSearchChannels(query: string): Promise<string> {
if (!query) return '❌ Usage: /fc channels <query>';
const client = getNeynarClient();
const channels = await client.searchChannels(query, 10);
if (channels.length === 0) return 'No channels found.';
return channels.map((ch) => `/${ch.id} - ${ch.name} (${ch.followerCount} followers)`).join('\n');
}
async function handlePost(text: string): Promise<string> {
if (!text) return '❌ Usage: /fc post <text>';
if (!process.env.NEYNAR_SIGNER_UUID) return '❌ Set NEYNAR_SIGNER_UUID to post casts.';
const client = getNeynarClient();
const cast = await client.postCast(text);
return `✅ Posted!\n\n${formatCast(cast)}`;
}
async function handleReply(args: string): Promise<string> {
const [hash, ...textParts] = args.split(/\s+/);
const text = textParts.join(' ');
if (!hash || !text) return '❌ Usage: /fc reply <hash> <text>';
if (!process.env.NEYNAR_SIGNER_UUID) return '❌ Set NEYNAR_SIGNER_UUID to post casts.';
const client = getNeynarClient();
const cast = await client.postCast(text, { parentHash: hash });
return `✅ Replied!\n\n${formatCast(cast)}`;
}
async function handleLike(hash: string): Promise<string> {
if (!hash) return '❌ Usage: /fc like <hash>';
if (!process.env.NEYNAR_SIGNER_UUID) return '❌ Set NEYNAR_SIGNER_UUID to interact.';
const client = getNeynarClient();
await client.likeCast(hash);
return `✅ Liked cast ${hash.slice(0, 10)}...`;
}
async function handleRecast(hash: string): Promise<string> {
if (!hash) return '❌ Usage: /fc recast <hash>';
if (!process.env.NEYNAR_SIGNER_UUID) return '❌ Set NEYNAR_SIGNER_UUID to interact.';
const client = getNeynarClient();
await client.recastCast(hash);
return `✅ Recasted ${hash.slice(0, 10)}...`;
}
async function handleFollow(username: string): Promise<string> {
if (!username) return '❌ Usage: /fc follow <username>';
if (!process.env.NEYNAR_SIGNER_UUID) return '❌ Set NEYNAR_SIGNER_UUID to follow users.';
const client = getNeynarClient();
const user = await client.getUserByUsername(username.replace('@', ''));
await client.followUser(user.fid);
return `✅ Now following @${user.username}`;
}
async function handleUnfollow(username: string): Promise<string> {
if (!username) return '❌ Usage: /fc unfollow <username>';
if (!process.env.NEYNAR_SIGNER_UUID) return '❌ Set NEYNAR_SIGNER_UUID to unfollow users.';
const client = getNeynarClient();
const user = await client.getUserByUsername(username.replace('@', ''));
await client.unfollowUser(user.fid);
return `✅ Unfollowed @${user.username}`;
}
// =============================================================================
// Formatting
// =============================================================================
function formatUser(u: FarcasterUser): string {
const lines = [
`**@${u.username}** (${u.displayName})`,
u.bio ? `> ${u.bio}` : '',
'',
`📊 ${u.followerCount.toLocaleString()} followers · ${u.followingCount.toLocaleString()} following`,
`🆔 FID: ${u.fid}`,
];
if (u.verifiedAddresses?.ethAddresses?.length) {
lines.push(`🔗 ETH: ${u.verifiedAddresses.ethAddresses[0]}`);
}
if (u.verifiedAddresses?.solAddresses?.length) {
lines.push(`🔗 SOL: ${u.verifiedAddresses.solAddresses[0]}`);
}
return lines.filter(Boolean).join('\n');
}
function formatCast(c: Cast): string {
const channelStr = c.channel ? ` in /${c.channel.id}` : '';
const lines = [
`**@${c.author.username}**${channelStr}`,
c.text,
'',
`❤️ ${c.reactions.likes} · 🔁 ${c.reactions.recasts} · 💬 ${c.replies.count}`,
`\`${c.hash.slice(0, 10)}...\``,
];
return lines.join('\n');
}
function formatChannel(ch: Channel): string {
return [
`**/${ch.id}** - ${ch.name}`,
ch.description ? `> ${ch.description}` : '',
'',
`👥 ${ch.followerCount.toLocaleString()} followers`,
].filter(Boolean).join('\n');
}
function getHelp(): string {
return `**Farcaster** - Social Protocol
**Read:**
\`/fc user <username>\` - Look up user
\`/fc search-users <query>\` - Search users
\`/fc feed [--channel X]\` - Get feed
\`/fc trending\` - Trending casts
\`/fc search <query>\` - Search casts
\`/fc channel <id>\` - Channel info
\`/fc channels <query>\` - Search channels
**Write (requires NEYNAR_SIGNER_UUID):**
\`/fc post <text>\` - Post cast
\`/fc reply <hash> <text>\` - Reply
\`/fc like <hash>\` - Like
\`/fc recast <hash>\` - Recast
\`/fc follow <username>\` - Follow
\`/fc unfollow <username>\` - Unfollow`;
}
export default {
name: 'farcaster',
description: 'Farcaster social protocol - browse feeds, search casts, post, and interact via Neynar API',
commands: ['/farcaster', '/fc'],
handle: execute,
};
// =============================================================================
// Agent Tools
// =============================================================================
export const tools = [
{
name: 'farcaster_user',
description: 'Get Farcaster user profile by username',
parameters: {
type: 'object',
properties: {
username: { type: 'string', description: 'Farcaster username' },
},
required: ['username'],
},
execute: async ({ username }: { username: string }) => handleUser(username),
},
{
name: 'farcaster_search',
description: 'Search Farcaster casts',
parameters: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' },
},
required: ['query'],
},
execute: async ({ query }: { query: string }) => handleSearch(query),
},
{
name: 'farcaster_trending',
description: 'Get trending Farcaster casts',
parameters: { type: 'object', properties: {} },
execute: async () => handleTrending(),
},
{
name: 'farcaster_post',
description: 'Post a cast to Farcaster (requires signer)',
parameters: {
type: 'object',
properties: {
text: { type: 'string', description: 'Cast text' },
channelId: { type: 'string', description: 'Optional channel ID' },
},
required: ['text'],
},
execute: async ({ text, channelId }: { text: string; channelId?: string }) => {
if (!text) return '❌ Text required';
const client = getNeynarClient();
const cast = await client.postCast(text, { channelId });
return `✅ Posted!\n\n${formatCast(cast)}`;
},
},
{
name: 'farcaster_channel_feed',
description: 'Get casts from a Farcaster channel',
parameters: {
type: 'object',
properties: {
channelId: { type: 'string', description: 'Channel ID (e.g., "base", "ethereum")' },
},
required: ['channelId'],
},
execute: async ({ channelId }: { channelId: string }) => handleFeed(`--channel ${channelId}`),
},
];