Skip to main content
Glama

timeline_add_scheduled_event

Schedule social media posts across multiple platforms by adding events to organized tracks for content automation workflows.

Instructions

Add a scheduled event to a track. IMPORTANT: 1) Use the terminal MCP tool to get the current date/time (execute_command("date")) before scheduling events to ensure correct dates. 2) ALWAYS use timeline_list_tracks first to check existing tracks - if a track with a similar name or purpose already exists, use that instead of creating a new one. If unsure whether an existing track matches your needs, ask the user for clarification before proceeding.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
trackNameYes
eventNameYes
promptYes
scheduledTimeYes
platformNox
agentNoclaude-sonnet-4-5-20250929
approvalViaNomanual
mcpToolsNo
metadataNoPlatform-specific metadata (e.g., { targetSubreddit: "subredditname" } for Reddit posts)

Implementation Reference

  • The execute function implementing the core logic for adding a scheduled event: validates input, finds or creates track, creates media directory and info.json, prepares and inserts event data into database, returns event details.
    execute: async (params) => {
      console.error('[Timeline MCP] Add scheduled event called with params:', JSON.stringify(params, null, 2));
      
      const db = await getDb();
      
      try {
        // Validate params
        const validatedParams = addScheduledEventParams.parse(params);
        console.error('[Timeline MCP] Validated params:', JSON.stringify(validatedParams, null, 2));
        
        // Find or create track
        let track = await db.select().from(tracks)
          .where(and(eq(tracks.name, validatedParams.trackName), eq(tracks.type, 'planned')))
          .limit(1);
        
        if (track.length === 0) {
          const maxOrder = await db.select({ maxOrder: tracks.order })
            .from(tracks)
            .orderBy(desc(tracks.order))
            .limit(1);
          
          const newOrder = (maxOrder[0]?.maxOrder || 0) + 1;
          
          const trackId = uuidv4();
          const postyAccountId = await getDefaultPostyAccountId();
          await db.insert(tracks).values({
            id: trackId,
            postyAccountId,
            name: validatedParams.trackName,
            type: 'planned',
            order: newOrder
          });
          
          const [newTrack] = await db.select().from(tracks).where(eq(tracks.id, trackId));
          
          track = [newTrack];
        }
        
        // Generate event ID first
        const eventId = uuidv4();
        
        // Create media folder path
        const mediaPath = await createMediaPath(validatedParams.trackName, validatedParams.eventName);
        
        // Create the actual folder on disk
        const workspacePath = getWorkspacePath();
        const fullMediaPath = path.join(workspacePath, mediaPath);
        console.error('[Timeline MCP] Creating media folder:', fullMediaPath);
        
        try {
          await fs.mkdir(fullMediaPath, { recursive: true });
          
          // Create an info.json file with event metadata
          const infoFile = path.join(fullMediaPath, 'info.json');
          const info = {
            eventId: eventId,
            eventName: validatedParams.eventName,
            trackId: track[0].id,
            createdAt: new Date().toISOString()
          };
          await fs.writeFile(infoFile, JSON.stringify(info, null, 2));
          
          console.error('[Timeline MCP] Media folder created successfully with info.json');
        } catch (error) {
          console.error('[Timeline MCP] Error creating media folder:', error);
          // Continue even if folder creation fails
        }
        
        // Create event
        const scheduledTime = new Date(validatedParams.scheduledTime);
        const generationTime = calculateGenerationTime(scheduledTime);
        const postyAccountId = await getDefaultPostyAccountId();
    
        const eventData = prepareEventForDb({
          id: eventId,
          postyAccountId,
          trackId: track[0].id,
          name: validatedParams.eventName,
          platform: validatedParams.platform,
          scheduledTime: scheduledTime,
          generationTime: generationTime,
          prompt: validatedParams.prompt, // Store prompt string directly
          agent: validatedParams.agent,
          eventType: 'scheduled',
          mediaPath: mediaPath,
          mcpTools: JSON.stringify(validatedParams.mcpTools),
          approvalVia: validatedParams.approvalVia,
          metadata: JSON.stringify(validatedParams.metadata || {}), // Platform-specific metadata
          contentGenerated: false,
          approved: false,
          posted: false
        });
        
        await db.insert(events).values(eventData);
        
        const [newEvent] = await db.select().from(events).where(eq(events.id, eventId));
        
        const response = {
          success: true,
          event: {
            id: newEvent.id,
            trackId: newEvent.trackId,
            name: newEvent.name,
            scheduledTime: newEvent.scheduledTime,
            generationTime: newEvent.generationTime,
            mediaPath: newEvent.mediaPath,
            platform: newEvent.platform,
            metadata: newEvent.metadata ? JSON.parse(newEvent.metadata) : undefined
          }
        };
        
        // Log event creation
        console.log('[MCP Timeline] Event created:', newEvent.id, newEvent.name);
        
        return JSON.stringify(response, null, 2);
      } catch (error) {
        console.error('[Timeline MCP] Error in add_scheduled_event:', error);
        
        if (error instanceof z.ZodError) {
          return JSON.stringify({
            success: false,
            error: 'Validation error',
            details: error.errors
          }, null, 2);
        }
        
        return JSON.stringify({
          success: false,
          error: error instanceof Error ? error.message : 'Unknown error occurred',
          stack: error instanceof Error ? error.stack : undefined
        }, null, 2);
      }
    }
  • Zod schema defining the input parameters for the timeline_add_scheduled_event tool, including validation rules for trackName, eventName, prompt, scheduledTime, platform, agent, etc.
    const addScheduledEventParams = z.object({
      trackName: z.string().min(1, 'Track name cannot be empty').max(100, 'Track name too long'),
      eventName: z.string().min(1, 'Event name cannot be empty').max(200, 'Event name too long'),
      prompt: z.string().min(1, 'Prompt cannot be empty').max(5000, 'Prompt too long'),
      scheduledTime: isoDateTimeSchema,
      platform: platformSchema.default('x'),
      agent: agentSchema.optional().default('claude-sonnet-4-5-20250929'),
      approvalVia: z.string().optional().default('manual'),
      mcpTools: z.array(z.string()).optional().default(['timeline', 'fal', 'sqlite', 'playwright']),
      metadata: z.record(z.any()).optional().describe('Platform-specific metadata (e.g., { targetSubreddit: "subredditname" } for Reddit posts)')
    });
  • Registration of the timeline_add_scheduled_event tool with FastMCP using mcp.addTool(), including name, description, parameters schema, and execute handler.
    mcp.addTool({
      name: 'timeline_add_scheduled_event',
      description: 'Add a scheduled event to a track. IMPORTANT: 1) Use the terminal MCP tool to get the current date/time (execute_command("date")) before scheduling events to ensure correct dates. 2) ALWAYS use timeline_list_tracks first to check existing tracks - if a track with a similar name or purpose already exists, use that instead of creating a new one. If unsure whether an existing track matches your needs, ask the user for clarification before proceeding.',
      parameters: addScheduledEventParams,
      execute: async (params) => {
        console.error('[Timeline MCP] Add scheduled event called with params:', JSON.stringify(params, null, 2));
        
        const db = await getDb();
        
        try {
          // Validate params
          const validatedParams = addScheduledEventParams.parse(params);
          console.error('[Timeline MCP] Validated params:', JSON.stringify(validatedParams, null, 2));
          
          // Find or create track
          let track = await db.select().from(tracks)
            .where(and(eq(tracks.name, validatedParams.trackName), eq(tracks.type, 'planned')))
            .limit(1);
          
          if (track.length === 0) {
            const maxOrder = await db.select({ maxOrder: tracks.order })
              .from(tracks)
              .orderBy(desc(tracks.order))
              .limit(1);
            
            const newOrder = (maxOrder[0]?.maxOrder || 0) + 1;
            
            const trackId = uuidv4();
            const postyAccountId = await getDefaultPostyAccountId();
            await db.insert(tracks).values({
              id: trackId,
              postyAccountId,
              name: validatedParams.trackName,
              type: 'planned',
              order: newOrder
            });
            
            const [newTrack] = await db.select().from(tracks).where(eq(tracks.id, trackId));
            
            track = [newTrack];
          }
          
          // Generate event ID first
          const eventId = uuidv4();
          
          // Create media folder path
          const mediaPath = await createMediaPath(validatedParams.trackName, validatedParams.eventName);
          
          // Create the actual folder on disk
          const workspacePath = getWorkspacePath();
          const fullMediaPath = path.join(workspacePath, mediaPath);
          console.error('[Timeline MCP] Creating media folder:', fullMediaPath);
          
          try {
            await fs.mkdir(fullMediaPath, { recursive: true });
            
            // Create an info.json file with event metadata
            const infoFile = path.join(fullMediaPath, 'info.json');
            const info = {
              eventId: eventId,
              eventName: validatedParams.eventName,
              trackId: track[0].id,
              createdAt: new Date().toISOString()
            };
            await fs.writeFile(infoFile, JSON.stringify(info, null, 2));
            
            console.error('[Timeline MCP] Media folder created successfully with info.json');
          } catch (error) {
            console.error('[Timeline MCP] Error creating media folder:', error);
            // Continue even if folder creation fails
          }
          
          // Create event
          const scheduledTime = new Date(validatedParams.scheduledTime);
          const generationTime = calculateGenerationTime(scheduledTime);
          const postyAccountId = await getDefaultPostyAccountId();
    
          const eventData = prepareEventForDb({
            id: eventId,
            postyAccountId,
            trackId: track[0].id,
            name: validatedParams.eventName,
            platform: validatedParams.platform,
            scheduledTime: scheduledTime,
            generationTime: generationTime,
            prompt: validatedParams.prompt, // Store prompt string directly
            agent: validatedParams.agent,
            eventType: 'scheduled',
            mediaPath: mediaPath,
            mcpTools: JSON.stringify(validatedParams.mcpTools),
            approvalVia: validatedParams.approvalVia,
            metadata: JSON.stringify(validatedParams.metadata || {}), // Platform-specific metadata
            contentGenerated: false,
            approved: false,
            posted: false
          });
          
          await db.insert(events).values(eventData);
          
          const [newEvent] = await db.select().from(events).where(eq(events.id, eventId));
          
          const response = {
            success: true,
            event: {
              id: newEvent.id,
              trackId: newEvent.trackId,
              name: newEvent.name,
              scheduledTime: newEvent.scheduledTime,
              generationTime: newEvent.generationTime,
              mediaPath: newEvent.mediaPath,
              platform: newEvent.platform,
              metadata: newEvent.metadata ? JSON.parse(newEvent.metadata) : undefined
            }
          };
          
          // Log event creation
          console.log('[MCP Timeline] Event created:', newEvent.id, newEvent.name);
          
          return JSON.stringify(response, null, 2);
        } catch (error) {
          console.error('[Timeline MCP] Error in add_scheduled_event:', error);
          
          if (error instanceof z.ZodError) {
            return JSON.stringify({
              success: false,
              error: 'Validation error',
              details: error.errors
            }, null, 2);
          }
          
          return JSON.stringify({
            success: false,
            error: error instanceof Error ? error.message : 'Unknown error occurred',
            stack: error instanceof Error ? error.stack : undefined
          }, null, 2);
        }
      }
    });
Behavior3/5

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

With no annotations provided, the description carries full burden. It reveals important behavioral constraints (date/time verification requirement, track existence checking prerequisite) and mentions user clarification fallback. However, it doesn't disclose what happens on success/failure, whether events can be modified/removed later, or any rate limits/authentication needs. For a 9-parameter mutation tool with no annotations, this leaves significant behavioral gaps.

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

Conciseness4/5

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

The description is appropriately sized and well-structured with numbered IMPORTANT points. It's front-loaded with the core purpose, then provides critical usage guidelines. Every sentence serves a clear purpose, though the second sentence could be slightly more concise by combining the two verification steps.

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

Completeness3/5

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

Given high complexity (9 parameters, nested objects, no output schema, no annotations), the description provides strong usage guidance but lacks parameter explanations and behavioral details. The guidance for track verification and date/time checking is valuable, but without parameter semantics or output information, an agent would struggle to construct proper calls despite knowing when to use the tool.

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

Parameters2/5

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

Schema description coverage is only 11% (just the metadata parameter has a description), so the description must compensate. However, the description provides NO information about any of the 9 parameters - it doesn't explain what trackName, eventName, prompt, scheduledTime, platform, agent, approvalVia, mcpTools, or metadata represent or how they should be used. This leaves most parameters semantically undefined.

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 ('Add a scheduled event to a track') with specific resources (event, track). It distinguishes from siblings by focusing on adding events rather than tracks or listing/removing operations. However, it doesn't explicitly differentiate from 'timeline_update_scheduled_event' which could be confusing for similar update scenarios.

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

Usage Guidelines5/5

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

The description provides explicit usage guidance with two numbered IMPORTANT instructions: 1) use terminal tool for date/time verification, and 2) ALWAYS use timeline_list_tracks first to check existing tracks. It specifies when to use existing tracks vs. create new ones, and includes fallback guidance ('ask the user for clarification'). This is comprehensive guidance for proper tool invocation.

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/derekalia/timeline-mcp'

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