Skip to main content
Glama

mastodon_create_toot

Post text content to Mastodon with options for visibility, media attachments, scheduling, and content warnings.

Instructions

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

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
contentYesThe text content of the toot
visibilityNoThe visibility level of the tootpublic
sensitiveNoMark the toot as sensitive content
spoiler_textNoText to be shown as a warning before the actual content
media_fileNoPath to a media file to attach (image, video, or audio)
media_descriptionNoAlt text / description for the attached media
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

Implementation Reference

  • The main handler function for the 'mastodon_create_toot' tool. It handles media upload if provided, calls the Mastodon API to create or schedule a toot, and formats a success response with link or schedule details.
    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 server.tool(), including name, description, input schema, and handler function.
    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,
            },
          ],
        };
      }
    );
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It mentions creating a toot (implying a write/mutation operation) and optional media attachments, but doesn't disclose behavioral traits like authentication requirements, rate limits, error conditions, or what happens on success/failure. For a mutation tool with zero annotation coverage, this is a significant gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that is front-loaded with the core purpose ('Create a new toot on Mastodon') and includes a useful qualifier ('optionally with media attachments'). There is zero waste or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given this is a mutation tool (creating content) with no annotations and no output schema, the description is incomplete. It doesn't address authentication needs, rate limits, error handling, or what the tool returns. For a 7-parameter tool that performs a write operation, more contextual information is necessary to guide an AI agent effectively.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all 7 parameters thoroughly. The description adds minimal value by mentioning 'optionally with media attachments,' which loosely relates to the media_file and media_description parameters, but doesn't provide additional meaning beyond what the schema specifies. Baseline 3 is appropriate when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Create a new toot') and resource ('on Mastodon'), and distinguishes it from sibling tools by specifying it's for posting content rather than retrieving timelines, tags, or searching. It also mentions the optional media attachments feature.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus alternatives. The description doesn't mention prerequisites (e.g., authentication needs), when not to use it, or how it differs from other posting methods if they exist. It only states what it does without context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other 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