Skip to main content
Glama

mastodon_create_toot

Create and publish Mastodon toots with customizable visibility, media attachments, alt text, and scheduling options. Manage content sensitivity and add spoiler warnings as needed.

Instructions

Create a new toot (status) on Mastodon, optionally with media attachments

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
contentYesThe text content of the toot
media_descriptionNoAlt text / description for the attached media
media_fileNoPath to a media file to attach (image, video, or audio)
scheduled_atNoOptional ISO 8601 datetime string to schedule the toot for a future time. Examples: 2024-01-01T10:00:00Z, 2024-01-01T10:00:00+01:00
sensitiveNoMark the toot as sensitive content
spoiler_textNoText to be shown as a warning before the actual content
visibilityNoThe visibility level of the tootpublic

Implementation Reference

  • Handler function that implements the core logic of the mastodon_create_toot tool: optional media upload, status creation via MastodonClient, and formatted response handling for both immediate and scheduled toots.
    async (params: TootParams) => { let media_ids: string[] | undefined; if (params.media_file) { try { const fileData = await readFile(params.media_file); const media = await client.uploadMedia( fileData, params.media_file.split("/").pop() || "image", params.media_description ); media_ids = [media.id]; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; throw new Error(`Failed to upload media: ${errorMessage}`); } } const result = await client.createStatus({ status: params.content, visibility: params.visibility, sensitive: params.sensitive, spoiler_text: params.spoiler_text, media_ids, scheduled_at: params.scheduled_at, }); let successMessage: string; // Check if 'url' property exists to differentiate MastodonStatus from ScheduledMastodonStatus if ('url' in result) { // It's a MastodonStatus (posted immediately) const mediaInfo = result.media_attachments.length > 0 ? `\nMedia: ${result.media_attachments.map((m) => m.url).join(", ")}` : ""; successMessage = `Successfully created toot! View it at: ${result.url}${mediaInfo}`; } else { // It's a ScheduledMastodonStatus const scheduledTime = new Date(result.scheduled_at).toLocaleString(); let mediaInfo = ""; if (result.media_attachments && result.media_attachments.length > 0) { mediaInfo = `\nMedia will be attached: ${result.media_attachments.length} item(s).`; } successMessage = `Successfully scheduled toot! ID: ${result.id}. It will be posted at: ${scheduledTime}.${mediaInfo}`; } return { content: [ { type: "text", text: successMessage, }, ], }; }
  • Zod schema defining the input parameters and validation for the mastodon_create_toot tool.
    const TootSchema = z.object({ content: z.string().describe("The text content of the toot"), visibility: z .enum(["public", "unlisted", "private", "direct"]) .describe("The visibility level of the toot") .default("public"), sensitive: z .boolean() .describe("Mark the toot as sensitive content") .default(false), spoiler_text: z .string() .describe("Text to be shown as a warning before the actual content") .default(""), media_file: z .string() .describe("Path to a media file to attach (image, video, or audio)") .optional(), media_description: z .string() .describe("Alt text / description for the attached media") .optional(), scheduled_at: z .string() .datetime({ offset: true, message: "Invalid datetime string, expected ISO 8601 format (e.g., YYYY-MM-DDTHH:mm:ss.sssZ, YYYY-MM-DDTHH:mm:ss.sss+HH:MM)" }) .describe("Optional ISO 8601 datetime string to schedule the toot for a future time. Examples: 2024-01-01T10:00:00Z, 2024-01-01T10:00:00+01:00") .optional(), });
  • Registration of the mastodon_create_toot tool using McpServer.tool() method, including name, description, input schema, and handler reference.
    server.tool( "mastodon_create_toot", "Create a new toot (status) on Mastodon, optionally with media attachments", TootSchema.shape, async (params: TootParams) => { let media_ids: string[] | undefined; if (params.media_file) { try { const fileData = await readFile(params.media_file); const media = await client.uploadMedia( fileData, params.media_file.split("/").pop() || "image", params.media_description ); media_ids = [media.id]; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; throw new Error(`Failed to upload media: ${errorMessage}`); } } const result = await client.createStatus({ status: params.content, visibility: params.visibility, sensitive: params.sensitive, spoiler_text: params.spoiler_text, media_ids, scheduled_at: params.scheduled_at, }); let successMessage: string; // Check if 'url' property exists to differentiate MastodonStatus from ScheduledMastodonStatus if ('url' in result) { // It's a MastodonStatus (posted immediately) const mediaInfo = result.media_attachments.length > 0 ? `\nMedia: ${result.media_attachments.map((m) => m.url).join(", ")}` : ""; successMessage = `Successfully created toot! View it at: ${result.url}${mediaInfo}`; } else { // It's a ScheduledMastodonStatus const scheduledTime = new Date(result.scheduled_at).toLocaleString(); let mediaInfo = ""; if (result.media_attachments && result.media_attachments.length > 0) { mediaInfo = `\nMedia will be attached: ${result.media_attachments.length} item(s).`; } successMessage = `Successfully scheduled toot! ID: ${result.id}. It will be posted at: ${scheduledTime}.${mediaInfo}`; } return { content: [ { type: "text", text: successMessage, }, ], }; } );
  • MastodonClient.createStatus method: core API call to create or schedule a toot/status on Mastodon server.
    async createStatus(params: CreateStatusParams): Promise<StatusOrScheduledStatus> { const payload: CreateStatusParams = { status: params.status, visibility: params.visibility, sensitive: params.sensitive, spoiler_text: params.spoiler_text, language: params.language, media_ids: params.media_ids, poll: params.poll, in_reply_to_id: params.in_reply_to_id, }; if (params.scheduled_at) { payload.scheduled_at = params.scheduled_at; } return this.request<StatusOrScheduledStatus>("/api/v1/statuses", "POST", payload); }
  • MastodonClient.uploadMedia method: uploads media file to Mastodon and returns attachment ID, used by the tool handler.
    async uploadMedia( file: Buffer | Uint8Array, filename: string, description?: string ): Promise<MastodonMediaAttachment> { const formData = new FormData(); const blob = new Blob([file], { type: this.getMimeType(filename) }); formData.append("file", blob, filename); if (description) { formData.append("description", description); } return this.request<MastodonMediaAttachment>( "/api/v1/media", "POST", formData, true ); }

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/The-Focus-AI/mastodon-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server