What We're Building โ€” and Why It Matters

Building Claude AI agents that handle scheduling and task management is one of the highest-ROI automation patterns available to enterprise teams today. The average knowledge worker spends 4.1 hours per week on scheduling, follow-ups, and task creation โ€” work that is entirely rule-based and requires zero judgement once the agent understands your preferences.

By the end of this tutorial, you'll have a working Claude agent that can: receive a natural-language meeting request ("Schedule a 45-minute product review with Sarah and Marcus next week, avoid Mondays, send a calendar invite with the agenda"), find available slots across multiple calendars, book the meeting, send personalised confirmation emails to each attendee, and create a follow-up task in Asana or Linear for the meeting preparation. No human in the loop, unless you want one.

This tutorial builds on the Claude Agent SDK and uses MCP connectors to interact with Google Calendar, Gmail, and your task management system. The same architecture extends to Outlook, Salesforce, HubSpot, or any system with an MCP server. If you want this deployed and integrated with your existing enterprise systems rather than built from scratch, our AI agent development service delivers production systems in 4-6 weeks.

What You'll Build

  • Claude agent with Google Calendar read/write access via MCP
  • Gmail integration for sending formatted invitation and confirmation emails
  • Asana/Linear task creation for meeting preparation and follow-ups
  • Human-in-the-loop approval for external meeting bookings
  • Audit log of every action the agent takes โ€” essential for enterprise deployment

Agent Architecture Overview

Before writing any code, understand the architectural pieces. A Claude AI agent that manages meetings consists of three layers: the orchestration layer (Claude itself, using the Agent SDK), the tool layer (the functions Claude can call), and the integration layer (MCP servers connecting to external systems).

Claude Sonnet is the right model for this task. It has the reasoning capability to handle complex scheduling constraints โ€” competing time zones, preferences, meeting types โ€” while being fast and cost-effective enough to run synchronously in response to user requests. You'd only reach for Claude Opus 4 if you're adding complex multi-step negotiations or working with ambiguous historical context that requires deeper reasoning.

The MCP (Model Context Protocol) layer is what makes this practical at enterprise scale. Rather than writing custom Google Calendar API wrappers, you connect a pre-built Google Calendar MCP server and Claude knows immediately what tools are available, what parameters they take, and how to chain them together. Our MCP protocol guide covers the full ecosystem of available MCP servers โ€” for this tutorial you'll need the Google Calendar MCP, Gmail MCP, and either the Asana or Linear MCP depending on your stack.

Architecture diagram โ€” meeting booking agent
User Request (natural language)
        โ†“
Claude Sonnet (Agent SDK)
        โ†“
Tool Selection & Planning
        โ†“
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  MCP Servers (connected via stdio/SSE)  โ”‚
โ”‚                                         โ”‚
โ”‚  โ€ข google-calendar-mcp  โ†’ Read/write    โ”‚
โ”‚  โ€ข gmail-mcp            โ†’ Send email    โ”‚
โ”‚  โ€ข asana-mcp            โ†’ Create tasks  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ†“
Action Execution (with audit logging)
        โ†“
Confirmation to User

Project Setup and Dependencies

Start by initialising a Python project with the necessary dependencies. Claude's Agent SDK is the primary orchestration framework โ€” it handles the agentic loop automatically, so you don't need to write your own "think, act, observe" cycle from scratch.

Install dependencies
pip install anthropic anthropic-sdk-python mcp python-dotenv google-auth google-auth-oauthlib
project structure
meeting-agent/
โ”œโ”€โ”€ .env                          # API keys (never commit this)
โ”œโ”€โ”€ agent.py                      # Main agent entrypoint
โ”œโ”€โ”€ tools/
โ”‚   โ”œโ”€โ”€ calendar_tools.py         # Calendar-specific tool definitions
โ”‚   โ”œโ”€โ”€ email_tools.py            # Email tool definitions
โ”‚   โ””โ”€โ”€ task_tools.py             # Task management tool definitions
โ”œโ”€โ”€ mcp_servers/
โ”‚   โ”œโ”€โ”€ google_calendar_server.py # Local MCP server for Google Calendar
โ”‚   โ””โ”€โ”€ gmail_server.py           # Local MCP server for Gmail
โ””โ”€โ”€ AGENT.md                      # Agent configuration & personality

Create your .env file with the necessary credentials. For production deployments, use a secrets manager (AWS Secrets Manager, HashiCorp Vault, or Google Secret Manager) instead of a dotenv file โ€” but for local development, this is fine:

.env
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_CALENDAR_CREDENTIALS_PATH=./credentials/google-credentials.json
GOOGLE_CALENDAR_TOKEN_PATH=./credentials/token.json
ASANA_ACCESS_TOKEN=...
AGENT_TIMEZONE=Europe/London
# Human-in-the-loop: set to "true" for external meeting approval
REQUIRE_APPROVAL_FOR_EXTERNAL=true

Defining the Agent's Tools

Claude's tool use system requires you to define each tool with a name, description, and input schema. The descriptions matter enormously โ€” they're how Claude decides which tool to use and in what order. Vague descriptions produce erratic tool selection. Specific descriptions produce reliable agents.

tools/calendar_tools.py
CALENDAR_TOOLS = [
    {
        "name": "get_calendar_availability",
        "description": """Check availability across one or more people's calendars
        for a given time range. Returns a list of available 30-minute slots,
        taking into account existing meetings, working hours preferences,
        and any blocked time. Use this BEFORE booking any meeting to confirm
        all participants are free. Required before calling book_meeting.""",
        "input_schema": {
            "type": "object",
            "properties": {
                "attendee_emails": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "List of email addresses to check availability for"
                },
                "date_range_start": {
                    "type": "string",
                    "description": "ISO 8601 datetime for start of search window"
                },
                "date_range_end": {
                    "type": "string",
                    "description": "ISO 8601 datetime for end of search window"
                },
                "duration_minutes": {
                    "type": "integer",
                    "description": "Length of meeting in minutes (e.g. 30, 45, 60)"
                },
                "exclude_days": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Days to exclude, e.g. ['Monday', 'Friday']"
                }
            },
            "required": ["attendee_emails", "date_range_start",
                         "date_range_end", "duration_minutes"]
        }
    },
    {
        "name": "book_meeting",
        "description": """Book a meeting in Google Calendar. Creates the event,
        sends calendar invites to all attendees, and returns the meeting ID.
        Only call this after confirming availability with get_calendar_availability
        and, for external attendees, after receiving human approval.""",
        "input_schema": {
            "type": "object",
            "properties": {
                "title": {"type": "string"},
                "start_datetime": {"type": "string"},
                "duration_minutes": {"type": "integer"},
                "attendee_emails": {"type": "array", "items": {"type": "string"}},
                "agenda": {"type": "string", "description": "Meeting agenda for the invite description"},
                "location": {"type": "string", "description": "Video link or physical location"}
            },
            "required": ["title", "start_datetime", "duration_minutes", "attendee_emails"]
        }
    }
]

Notice the description for book_meeting explicitly tells Claude the preconditions: check availability first, get human approval for external attendees. This is not safety theatre โ€” it's how you get consistent, reliable agent behaviour. Claude uses tool descriptions as implicit planning instructions. If you need help structuring tool definitions for complex agentic workflows, our Claude tool use guide covers the full specification.

Building the Agent Loop

With tools defined, the main agent file orchestrates Claude through the agentic loop: receive user input, let Claude plan and call tools, execute those tools, feed results back to Claude, and repeat until Claude returns a final response without requesting further tool calls.

agent.py โ€” main agent loop
import anthropic
import json
from tools.calendar_tools import CALENDAR_TOOLS
from tools.email_tools import EMAIL_TOOLS
from tools.task_tools import TASK_TOOLS
from tools.executor import execute_tool

client = anthropic.Anthropic()
ALL_TOOLS = CALENDAR_TOOLS + EMAIL_TOOLS + TASK_TOOLS

SYSTEM_PROMPT = """You are a scheduling assistant for a senior executive.
You book meetings, send professional confirmation emails, and create
follow-up tasks. You always:
1. Check availability before booking
2. Request approval before booking meetings with external parties
3. Send a concise confirmation email after every booking
4. Create a preparation task in Asana for meetings over 30 minutes
5. Log every action you take in structured format

You never fabricate availability data โ€” if you cannot check a calendar,
say so rather than guessing. Timezone is {timezone}."""

def run_agent(user_message: str) -> str:
    messages = [{"role": "user", "content": user_message}]

    while True:
        response = client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=4096,
            system=SYSTEM_PROMPT,
            tools=ALL_TOOLS,
            messages=messages
        )

        # Add assistant's response to message history
        messages.append({"role": "assistant", "content": response.content})

        # Check if Claude is done
        if response.stop_reason == "end_turn":
            # Extract text response
            for block in response.content:
                if hasattr(block, 'text'):
                    return block.text
            return "Task completed."

        # Process tool calls
        if response.stop_reason == "tool_use":
            tool_results = []
            for block in response.content:
                if block.type == "tool_use":
                    result = execute_tool(block.name, block.input)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": json.dumps(result)
                    })

            messages.append({"role": "user", "content": tool_results})

if __name__ == "__main__":
    response = run_agent(
        "Schedule a 45-minute product review with sarah@acme.com and "
        "marcus@acme.com for next week, avoid Monday, send calendar "
        "invites with a brief agenda about Q2 roadmap review."
    )
    print(response)

Need This in Production, Not Just in a Tutorial?

Production agent deployments need error handling, retry logic, human-in-the-loop approval flows, rate limiting, and an audit trail that satisfies enterprise security requirements. Our AI agent development service delivers fully governed production agents in 4-6 weeks.

Get a Custom Implementation Plan โ†’

Email Sending and Task Creation

The email and task tools follow the same pattern. For Gmail, your MCP server wraps the Gmail API and exposes a send_email tool. Define it with enough context that Claude writes professional, concise confirmation emails rather than generic ones. Pass the meeting details โ€” title, time, attendees, agenda, video link โ€” as parameters and let Claude compose the body using its judgement on tone and format.

For task creation, the key design decision is when to create tasks. In the system prompt above, the rule is explicit: "create a preparation task for meetings over 30 minutes." You can make this more sophisticated โ€” create tasks with specific checklists based on meeting type, assign them to the appropriate team member, set due dates relative to the meeting time. The agent handles all of this once the rules are clear. See the multi-agent orchestration guide for patterns where a scheduling agent hands off to a separate preparation agent.

The audit log is non-optional for enterprise deployments. Every tool call, its inputs, its outputs, and the timestamp should be written to a structured log. When something goes wrong โ€” and in agentic systems, things occasionally go wrong โ€” you need to reconstruct exactly what the agent did and why. A structured JSON log per run, stored in your centralised logging system, satisfies most enterprise audit requirements. For regulated industries, consult our Claude security and governance service for logging architectures that meet specific compliance standards.

Human-in-the-Loop Approval Flows

Not every meeting booking should be fully autonomous. For meetings with external parties โ€” clients, prospects, vendors โ€” most enterprises want a human to approve before the calendar invite goes out. Building this approval gate into your agent requires one design decision: synchronous or asynchronous approval?

Synchronous approval means the agent pauses, sends a Slack message or email with the proposed meeting details, and waits for a thumbs-up before proceeding. This works well for agents triggered by a human request in real time. Asynchronous approval uses a workflow โ€” the agent writes the proposed meeting to a queue, a human approves it via a web UI or Slack action, and a separate process executes the booking. This is more robust for agents running on schedules or processing bulk requests.

For this tutorial, implement synchronous Slack-based approval: the agent sends a structured Slack message with Approve/Reject buttons, waits up to 30 minutes for a response, and either proceeds or informs the user that approval was not received. The Slack MCP server handles the message sending; your application code handles the webhook that receives the button click. Claude's Agent SDK guide covers interruption handling patterns in detail โ€” this is one of the more nuanced aspects of production agent design.

Deploying to Production: What Changes

The tutorial code above runs locally and works for a single user. Production deployment requires several additional components. First, you need a persistence layer โ€” a database that stores conversation history, pending approvals, and booked meetings, so the agent can resume after a server restart. Second, you need rate limiting on the Claude API calls to stay within your organisation's agreed rate limits. Third, you need error handling that distinguishes between transient failures (Google Calendar API timeout โ€” retry) and permanent failures (invalid email address โ€” inform the user immediately).

For multi-user deployments, each user session needs isolated context. Claude should not confuse Sarah's calendar constraints with Marcus's. Use session IDs to partition conversation history and tool state. If your organisation is deploying this to 500+ employees, consider a queueing architecture where agent jobs are processed asynchronously rather than synchronously per user request โ€” this prevents API rate limit contention during peak hours.

Finally, test your agent with adversarial inputs before rolling out broadly. What happens when a user asks the agent to book 47 meetings in one request? What happens when the calendar API returns ambiguous availability data? What happens when an attendee's email is misspelled? Good agent evaluation and testing catches these edge cases before users encounter them in production.

CI

ClaudeImplementation Team

Claude Certified Architects with production deployments across financial services, healthcare, and enterprise software. Learn about our team โ†’