Skip to main content
Glama

create_post

Generate and publish social media content across platforms using natural language instructions, with options for previews, platform selection, and conversation continuity.

Instructions

Create and post content to social media platforms based on natural language instructions

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
instructionYesNatural language instruction for the post (e.g., "Post about the latest AI developments")
platformsNoSocial media platforms to post to
postImmediatelyNoWhether to post immediately or return a preview
conversationIdNoID of an existing conversation to continue
questionIdNoID of the question being answered
answerNoAnswer to the question
ignoreHistoryNoWhether to ignore similar posts in history
actionableInsightsNoWhether to include actionable insights in the post

Implementation Reference

  • Main handler function for the 'create_post' tool. Processes arguments, parses intent using NLP, handles conversation flow (questions/answers), checks post history, researches topic, generates content for platforms, and posts if requested.
    private async handleCreatePost(args: any) {
      logger.info('Creating post', { instruction: args.instruction });
      
      try {
        // Get conversation ID if provided
        const conversationId = args.conversationId;
        
        // Parse the instruction to extract intent
        const intent = await nlpProcessor.parseIntent(args.instruction, conversationId);
        
        // Override platforms if specified in args
        if (args.platforms) {
          if (args.platforms === 'all') {
            intent.platforms = [SocialPlatform.TWITTER, SocialPlatform.MASTODON, SocialPlatform.LINKEDIN];
          } else if (Array.isArray(args.platforms)) {
            intent.platforms = [];
            if (args.platforms.includes('twitter')) {
              intent.platforms.push(SocialPlatform.TWITTER);
            }
            if (args.platforms.includes('mastodon')) {
              intent.platforms.push(SocialPlatform.MASTODON);
            }
            if (args.platforms.includes('linkedin')) {
              intent.platforms.push(SocialPlatform.LINKEDIN);
            }
          }
        }
        
        // Override scheduling requirements if postImmediately is specified
        if (args.postImmediately !== undefined) {
          if (!intent.schedulingRequirements) {
            intent.schedulingRequirements = {};
          }
          intent.schedulingRequirements.postImmediately = args.postImmediately;
        }
        
        // Set actionable insights if specified
        if (args.actionableInsights !== undefined) {
          intent.actionableInsights = args.actionableInsights ? 
            'Include practical steps that readers can take immediately.' : undefined;
        }
        
        // Check if there are similar posts in history (unless ignoreHistory is true)
        if (!args.ignoreHistory) {
          const similarPosts = historyManager.getSimilarPosts(intent.topic, 0.7);
          
          if (similarPosts.length > 0) {
            logger.info('Found similar posts in history', { 
              count: similarPosts.length,
              topic: intent.topic,
            });
            
            // Return similar posts
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify({
                    instruction: args.instruction,
                    intent,
                    similarPosts,
                    status: 'similar_posts_found',
                    message: 'Similar posts were found in history. Consider modifying your topic or focusing on a different aspect.',
                  }, null, 2),
                },
              ],
            };
          }
        } else {
          logger.info('Ignoring history check', { topic: intent.topic });
        }
        
        // Create or get conversation
        let conversation;
        if (conversationId) {
          conversation = conversationManager.getConversation(conversationId);
          if (!conversation) {
            logger.warn('Conversation not found, creating new conversation', { conversationId });
            conversation = conversationManager.createConversation(intent);
          }
        } else {
          conversation = conversationManager.createConversation(intent);
        }
        
        // Store conversation ID in intent
        intent.conversationId = conversation.id;
        
        // Check if we need to ask questions
        if (conversation.state === ConversationState.INITIAL_REQUEST) {
          // Generate questions
          const questionIds = conversationManager.generateQuestions(conversation.id);
          
          if (questionIds.length > 0) {
            // Get the first question
            const question = conversationManager.getCurrentQuestion(conversation.id);
            
            if (question) {
              logger.info('Asking question', { 
                conversationId: conversation.id,
                questionId: question.id,
                questionType: question.type,
              });
              
              // Return question
              return {
                content: [
                  {
                    type: 'text',
                    text: JSON.stringify({
                      instruction: args.instruction,
                      intent,
                      conversationId: conversation.id,
                      question: question.text,
                      questionId: question.id,
                      status: 'question',
                    }, null, 2),
                  },
                ],
              };
            }
          }
        } else if (conversation.state === ConversationState.ASKING_QUESTIONS) {
          // Check if we have a question to answer
          if (args.questionId && args.answer) {
            // Answer the question
            conversationManager.answerQuestion(conversation.id, args.questionId, args.answer);
            
            // Get the next question
            const nextQuestion = conversationManager.getCurrentQuestion(conversation.id);
            
            if (nextQuestion) {
              logger.info('Asking next question', { 
                conversationId: conversation.id,
                questionId: nextQuestion.id,
                questionType: nextQuestion.type,
              });
              
              // Return next question
              return {
                content: [
                  {
                    type: 'text',
                    text: JSON.stringify({
                      instruction: args.instruction,
                      intent,
                      conversationId: conversation.id,
                      question: nextQuestion.text,
                      questionId: nextQuestion.id,
                      status: 'question',
                    }, null, 2),
                  },
                ],
              };
            }
          } else {
            // Get the current question
            const currentQuestion = conversationManager.getCurrentQuestion(conversation.id);
            
            if (currentQuestion) {
              logger.info('Asking current question', { 
                conversationId: conversation.id,
                questionId: currentQuestion.id,
                questionType: currentQuestion.type,
              });
              
              // Return current question
              return {
                content: [
                  {
                    type: 'text',
                    text: JSON.stringify({
                      instruction: args.instruction,
                      intent,
                      conversationId: conversation.id,
                      question: currentQuestion.text,
                      questionId: currentQuestion.id,
                      status: 'question',
                    }, null, 2),
                  },
                ],
              };
            }
          }
        }
        
        // Update conversation state to generating content
        conversationManager.updateState(conversation.id, ConversationState.GENERATING_CONTENT);
        
        // Research the topic
        const researchOptions = {
          includeHashtags: intent.researchRequirements?.includeHashtags || true,
          includeFacts: intent.researchRequirements?.includeFacts || true,
          includeTrends: intent.researchRequirements?.includeTrends || true,
          includeNews: intent.researchRequirements?.includeNews || true,
        };
        
        const research = await researchAggregator.researchTopic(intent.topic, researchOptions);
        
        // Generate content for each platform
        const content = await contentGenerator.generateContentForPlatforms(
          intent,
          research,
          intent.platforms
        );
        
        // Update conversation state to preview
        conversationManager.updateState(conversation.id, ConversationState.PREVIEW);
        
        // Post content if postImmediately is true
        if (intent.schedulingRequirements?.postImmediately) {
          const postResults = await this.postContent(content);
          
          // Update conversation state to completed
          conversationManager.updateState(conversation.id, ConversationState.COMPLETED);
          
          // Add to history
          const keywords = research.hashtags || [];
          historyManager.addToHistory(
            intent.topic,
            args.instruction,
            intent.platforms,
            content,
            keywords
          );
          
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify({
                  instruction: args.instruction,
                  intent,
                  research,
                  content,
                  postResults,
                  conversationId: conversation.id,
                  status: 'posted',
                }, null, 2),
              },
            ],
          };
        }
        
        // Return preview if postImmediately is false
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify({
                instruction: args.instruction,
                intent,
                research,
                content,
                conversationId: conversation.id,
                status: 'preview',
              }, null, 2),
            },
          ],
        };
      } catch (error) {
        logger.error('Error creating post', { 
          instruction: args.instruction,
          error: error instanceof Error ? error.message : String(error) 
        });
        
        throw error;
      }
    }
  • src/index.ts:76-121 (registration)
    Tool registration in the ListToolsRequest handler, defining the name, description, and input schema for 'create_post'.
    {
      name: 'create_post',
      description: 'Create and post content to social media platforms based on natural language instructions',
      inputSchema: {
        type: 'object',
        properties: {
          instruction: {
            type: 'string',
            description: 'Natural language instruction for the post (e.g., "Post about the latest AI developments")',
          },
          platforms: {
            type: 'array',
            items: {
              type: 'string',
              enum: ['twitter', 'mastodon', 'linkedin', 'all'],
            },
            description: 'Social media platforms to post to',
          },
          postImmediately: {
            type: 'boolean',
            description: 'Whether to post immediately or return a preview',
          },
          conversationId: {
            type: 'string',
            description: 'ID of an existing conversation to continue',
          },
          questionId: {
            type: 'string',
            description: 'ID of the question being answered',
          },
          answer: {
            type: 'string',
            description: 'Answer to the question',
          },
          ignoreHistory: {
            type: 'boolean',
            description: 'Whether to ignore similar posts in history',
          },
          actionableInsights: {
            type: 'boolean',
            description: 'Whether to include actionable insights in the post',
          },
        },
        required: ['instruction'],
      },
    },
  • src/index.ts:182-183 (registration)
    Dispatch logic in CallToolRequest handler that invokes the create_post handler.
    case 'create_post':
      return await this.handleCreatePost(request.params.arguments);
  • Helper function to execute the actual posting of content to Twitter, Mastodon, and LinkedIn clients.
    private async postContent(content: Record<SocialPlatform, Content>): Promise<any> {
      const results: Record<string, any> = {};
      
      // Post to Twitter
      if (content[SocialPlatform.TWITTER]) {
        try {
          results.twitter = await twitterClient.postTweet(content[SocialPlatform.TWITTER]);
        } catch (error) {
          logger.error('Error posting to Twitter', { 
            error: error instanceof Error ? error.message : String(error) 
          });
          
          results.twitter = {
            success: false,
            error: error instanceof Error ? error.message : String(error),
          };
        }
      }
      
      // Post to Mastodon
      if (content[SocialPlatform.MASTODON]) {
        try {
          results.mastodon = await mastodonClient.postStatus(content[SocialPlatform.MASTODON]);
        } catch (error) {
          logger.error('Error posting to Mastodon', { 
            error: error instanceof Error ? error.message : String(error) 
          });
          
          results.mastodon = {
            success: false,
            error: error instanceof Error ? error.message : String(error),
          };
        }
      }
      
      // Post to LinkedIn
      if (content[SocialPlatform.LINKEDIN]) {
        try {
          results.linkedin = await linkedinClient.postShare(content[SocialPlatform.LINKEDIN]);
        } catch (error) {
          logger.error('Error posting to LinkedIn', { 
            error: error instanceof Error ? error.message : String(error) 
          });
          
          results.linkedin = {
            success: false,
            error: error instanceof Error ? error.message : String(error),
          };
        }
      }
      
      return results;
    }
  • Input schema/JSON Schema for validating arguments to the create_post tool.
    inputSchema: {
      type: 'object',
      properties: {
        instruction: {
          type: 'string',
          description: 'Natural language instruction for the post (e.g., "Post about the latest AI developments")',
        },
        platforms: {
          type: 'array',
          items: {
            type: 'string',
            enum: ['twitter', 'mastodon', 'linkedin', 'all'],
          },
          description: 'Social media platforms to post to',
        },
        postImmediately: {
          type: 'boolean',
          description: 'Whether to post immediately or return a preview',
        },
        conversationId: {
          type: 'string',
          description: 'ID of an existing conversation to continue',
        },
        questionId: {
          type: 'string',
          description: 'ID of the question being answered',
        },
        answer: {
          type: 'string',
          description: 'Answer to the question',
        },
        ignoreHistory: {
          type: 'boolean',
          description: 'Whether to ignore similar posts in history',
        },
        actionableInsights: {
          type: 'boolean',
          description: 'Whether to include actionable insights in the post',
        },
      },
      required: ['instruction'],
    },
Behavior2/5

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

With no annotations provided, the description carries full burden but only states the basic function. It doesn't disclose critical behavioral traits like authentication requirements, rate limits, whether posts are permanent or draftable, error handling, or what happens when posting to multiple platforms. The mention of 'return a preview' via the postImmediately parameter is helpful but insufficient.

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 front-loads the core purpose without unnecessary words. Every element ('create and post', 'social media platforms', 'natural language instructions') contributes directly to understanding the tool's function.

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?

For a complex tool with 8 parameters, no annotations, and no output schema, the description is inadequate. It doesn't explain what the tool returns (e.g., post IDs, success status), error conditions, or how it handles the various parameters like conversationId or actionableInsights. The sibling tools suggest a broader context that isn't addressed.

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 parameters are well-documented in the schema itself. The description adds minimal value beyond the schema by implying natural language processing for the 'instruction' parameter, but doesn't explain parameter interactions or provide additional context for the 8 parameters.

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

Purpose4/5

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

The description clearly states the action ('Create and post content') and target ('social media platforms'), with the specific method 'based on natural language instructions' distinguishing it from typical API-based posting tools. However, it doesn't differentiate from sibling tools like 'get_trending_topics' or 'research_topic', which serve completely different purposes.

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 about when to use this tool versus alternatives or prerequisites. The description mentions posting to social media but doesn't specify use cases, constraints, or how it relates to the sibling tools for research and trend analysis.

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/tayler-id/social-media-mcp'

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