Hive MCP Server
by gluneau
Verified
- src
- tools
// Content creation tools implementation
import { PrivateKey } from '@hiveio/dhive';
import client from '../config/client';
import config from '../config';
import { Response } from '../utils/response';
import { handleError } from '../utils/error';
import { successJson, errorResponse } from '../utils/response';
// Create a new blog post
export async function createPost(
params: {
title: string;
body: string;
tags: string[];
beneficiaries?: { account: string; weight: number }[] | null;
permalink?: string;
max_accepted_payout?: string;
percent_hbd?: number;
allow_votes: boolean;
allow_curation_rewards: boolean;
}
): Promise<Response> {
try {
// Get credentials from environment variables
const username = config.hive.username;
const postingKey = config.hive.postingKey;
if (!username || !postingKey) {
return errorResponse('Error: HIVE_USERNAME or HIVE_POSTING_KEY environment variables are not set.');
}
// Generate permalink if not provided
const finalPermalink =
params.permalink ||
params.title
.toLowerCase()
.replace(/[^\w\s-]/g, '') // Remove non-word chars except spaces and hyphens
.replace(/\s+/g, '-') // Replace spaces with hyphens
.replace(/-+/g, '-') // Replace multiple hyphens with single hyphen
.slice(0, 255); // Restrict to 255 chars
// Ensure first tag is used as the main category
const finalTags = [...new Set(params.tags)]; // Remove duplicates
// Prepare the post operation
const postOperation = {
parent_author: '', // Empty for main posts (non-comments)
parent_permlink: finalTags[0], // First tag is the main category
author: username,
permlink: finalPermalink,
title: params.title,
body: params.body,
json_metadata: JSON.stringify({
tags: finalTags,
app: 'hive-mcp-server/1.0',
}),
};
// Prepare post options if needed
let hasOptions = false;
const options: {
author: string;
permlink: string;
max_accepted_payout: string;
percent_hbd: number;
allow_votes: boolean;
allow_curation_rewards: boolean;
extensions: [0, { beneficiaries: { account: string; weight: number }[] }][];
} = {
author: username,
permlink: finalPermalink,
max_accepted_payout: params.max_accepted_payout || '1000000.000 HBD',
percent_hbd: params.percent_hbd ?? 10000,
allow_votes: params.allow_votes,
allow_curation_rewards: params.allow_curation_rewards,
extensions: params.beneficiaries?.length
? [
[
0,
{
beneficiaries: params.beneficiaries.map((b) => ({
account: b.account,
weight: b.weight,
})),
},
],
]
: [],
};
// Add optional parameters if provided
if (params.max_accepted_payout) {
options.max_accepted_payout = params.max_accepted_payout;
hasOptions = true;
}
if (params.percent_hbd !== undefined) {
options.percent_hbd = params.percent_hbd;
hasOptions = true;
}
// Add beneficiaries if provided
if (params.beneficiaries && params.beneficiaries.length > 0) {
options.extensions = [
[
0,
{
beneficiaries: params.beneficiaries.map((b) => ({
account: b.account,
weight: b.weight,
})),
},
],
];
hasOptions = true;
}
let result;
if (hasOptions) {
// Use commentWithOptions when we have options
result = await client.broadcast.commentWithOptions(
postOperation,
options,
PrivateKey.fromString(postingKey)
);
} else {
// Use standard comment for basic posts
result = await client.broadcast.comment(
postOperation,
PrivateKey.fromString(postingKey)
);
}
return successJson({
success: true,
transaction_id: result.id,
transaction_url: `https://www.hiveblockexplorer.com/tx/${result.id}`,
block_num: result.block_num,
author: username,
permlink: finalPermalink,
title: params.title,
tags: finalTags,
url: `https://hive.blog/@${username}/${finalPermalink}`,
});
} catch (error) {
return errorResponse(handleError(error, 'create_post'));
}
}
// Create a comment on an existing post or reply to a comment
export async function createComment(
params: {
parent_author: string;
parent_permlink: string;
body: string;
permalink?: string;
beneficiaries?: { account: string; weight: number }[] | null;
max_accepted_payout?: string;
percent_hbd?: number;
allow_votes: boolean;
allow_curation_rewards: boolean;
}
): Promise<Response> {
try {
// Get credentials from environment variables
const username = config.hive.username;
const postingKey = config.hive.postingKey;
if (!username || !postingKey) {
return errorResponse('Error: HIVE_USERNAME or HIVE_POSTING_KEY environment variables are not set.');
}
// Generate a random permalink if not provided
const finalPermalink =
params.permalink ||
`re-${params.parent_permlink.slice(0, 20)}-${Date.now().toString(36)}`;
// Prepare the comment operation
const commentOperation = {
parent_author: params.parent_author,
parent_permlink: params.parent_permlink,
author: username,
permlink: finalPermalink,
title: '', // Comments don't have titles
body: params.body,
json_metadata: JSON.stringify({
app: 'hive-mcp-server/1.0',
}),
};
// Prepare comment options if needed
let hasOptions = false;
const options: {
author: string;
permlink: string;
max_accepted_payout: string;
percent_hbd: number;
allow_votes: boolean;
allow_curation_rewards: boolean;
extensions: [0, { beneficiaries: { account: string; weight: number }[] }][];
} = {
author: username,
permlink: finalPermalink,
max_accepted_payout: params.max_accepted_payout || '1000000.000 HBD',
percent_hbd: params.percent_hbd ?? 10000,
allow_votes: params.allow_votes,
allow_curation_rewards: params.allow_curation_rewards,
extensions: params.beneficiaries?.length
? [
[
0,
{
beneficiaries: params.beneficiaries.map((b) => ({
account: b.account,
weight: b.weight,
})),
},
],
]
: [],
};
// Add optional parameters if provided
if (params.max_accepted_payout) {
options.max_accepted_payout = params.max_accepted_payout;
hasOptions = true;
}
if (params.percent_hbd !== undefined) {
options.percent_hbd = params.percent_hbd;
hasOptions = true;
}
// Add beneficiaries if provided
if (params.beneficiaries && params.beneficiaries.length > 0) {
options.extensions = [
[
0,
{
beneficiaries: params.beneficiaries.map((b) => ({
account: b.account,
weight: b.weight,
})),
},
],
];
hasOptions = true;
}
let result;
if (hasOptions) {
// Use commentWithOptions when we have options
result = await client.broadcast.commentWithOptions(
commentOperation,
options,
PrivateKey.fromString(postingKey)
);
} else {
// Use standard comment for basic comments
result = await client.broadcast.comment(
commentOperation,
PrivateKey.fromString(postingKey)
);
}
return successJson({
success: true,
transaction_id: result.id,
transaction_url: `https://www.hiveblockexplorer.com/tx/${result.id}`,
block_num: result.block_num,
parent_author: params.parent_author,
parent_permlink: params.parent_permlink,
author: username,
permlink: finalPermalink,
url: `https://hive.blog/@${params.parent_author}/${params.parent_permlink}#@${username}/${finalPermalink}`,
});
} catch (error) {
return errorResponse(handleError(error, 'create_comment'));
}
}