Skip to main content
Glama

workflow_contact_to_textedit

Extract contact details from macOS Contacts and generate a formatted TextEdit document with the information for easy reference or sharing.

Instructions

Get contact information and create formatted TextEdit document

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
nameYesPerson name to look up
titleNoDocument title (optional)

Implementation Reference

  • Handler implementation that fetches contact details from macOS Contacts app via AppleScript, formats them into a document, and creates a new TextEdit document with the information.
        case 'workflow_contact_to_textedit':
          try {
            const personName = (args?.name as string) || '';
            const docTitle = (args?.title as string) || `Contact: ${personName}`;
            
            if (!personName) {
              return {
                content: [
                  {
                    type: 'text',
                    text: 'Error: name is required',
                  },
                ],
              };
            }
            
            // Step 1: Get contact information
            const contactCommand = `osascript -e 'on run argv
              set personNameToFind to item 1 of argv
              tell application "Contacts" 
                set thePerson to first person whose name is personNameToFind
                set emailList to {}
                repeat with anEmail in emails of thePerson
                  set end of emailList to (value of anEmail & " (" & label of anEmail & ")")
                end repeat
                set phoneList to {}
                repeat with aPhone in phones of thePerson
                  set end of phoneList to (value of aPhone & " (" & label of aPhone & ")")
                end repeat
                return "Name: " & name of thePerson & "\\nCompany: " & organization of thePerson & "\\nJob Title: " & job title of thePerson & "\\nEmails:\\n" & (emailList as string) & "\\nPhones:\\n" & (phoneList as string)
              end tell
            end run' -- "${personName}"`;
            
            const { stdout: contactInfo, stderr: contactError } = await execAsync(contactCommand);
            
            if (contactError.trim()) {
              return {
                content: [
                  {
                    type: 'text',
                    text: `Error getting contact info: ${contactError.trim()}`,
                  },
                ],
              };
            }
            
            if (!contactInfo.trim()) {
              return {
                content: [
                  {
                    type: 'text',
                    text: `No contact found with name "${personName}"`,
                  },
                ],
              };
            }
            
            // Step 2: Format data for document
            const formattedContent = `${docTitle}
    ==================
    
    ${contactInfo.trim()}
    
    Generated: ${new Date().toLocaleString()}`;
            
            // Step 3: Create TextEdit document
            const docCommand = `osascript -e 'on run argv
              set docContent to item 1 of argv
              tell application "TextEdit"
                set newDoc to make new document
                set text of newDoc to docContent
                set docName to name of newDoc
                return "Created document: " & docName
              end tell
            end run' -- "${formattedContent}"`;
            
            const { stdout: docResult, stderr: docError } = await execAsync(docCommand);
            
            if (docError.trim()) {
              return {
                content: [
                  {
                    type: 'text',
                    text: `Error creating document: ${docError.trim()}`,
                  },
                ],
              };
            }
            
            return {
              content: [
                {
                  type: 'text',
                  text: `Workflow completed: ${docResult.trim()}`,
                },
              ],
            };
            
          } catch (error: any) {
            return {
              content: [
                {
                  type: 'text',
                  text: `Error executing workflow: ${error.message}`,
                },
              ],
            };
          }
  • Tool schema defining input parameters: required 'name' for the contact and optional 'title' for the document.
      name: 'workflow_contact_to_textedit',
      description: 'Get contact information and create formatted TextEdit document',
      inputSchema: {
        type: 'object',
        properties: {
          name: {
            type: 'string',
            description: 'Person name to look up',
          },
          title: {
            type: 'string',
            description: 'Document title (optional)',
          },
        },
        required: ['name'],
      },
    },
  • src/index.ts:27-358 (registration)
    The tool is registered in the ListToolsRequestHandler by including it in the returned tools list.
    server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [
          {
            name: 'discover_apps',
            description: 'Discover AppleScript capabilities of a macOS application',
            inputSchema: {
              type: 'object',
              properties: {
                app_name: {
                  type: 'string',
                  description: 'Name of the macOS application to discover',
                },
                method: {
                  type: 'string',
                  description: 'Discovery method: basic, dictionary, properties, system_events, comprehensive',
                },
                destination: {
                  type: 'string',
                  description: 'Directory path to save discovery results',
                },
              },
              required: ['app_name', 'method', 'destination'],
            },
          },
          {
            name: 'finder_get_selection',
            description: 'Get currently selected files/folders in Finder',
            inputSchema: {
              type: 'object',
              properties: {},
            },
          },
          {
            name: 'finder_get_current_folder',
            description: 'Get path of currently viewed folder in frontmost Finder window',
            inputSchema: {
              type: 'object',
              properties: {},
            },
          },
          {
            name: 'mail_get_accounts',
            description: 'Get list of all Mail account names',
            inputSchema: {
              type: 'object',
              properties: {},
            },
          },
          {
            name: 'mail_get_inbox_count',
            description: 'Get unread message count in inbox',
            inputSchema: {
              type: 'object',
              properties: {},
            },
          },
          {
            name: 'mail_get_total_inbox_count',
            description: 'Get total message count in inbox',
            inputSchema: {
              type: 'object',
              properties: {},
            },
          },
          {
            name: 'contacts_search_people',
            description: 'Search for people by name in Contacts',
            inputSchema: {
              type: 'object',
              properties: {
                search_term: {
                  type: 'string',
                  description: 'Name or part of name to search for',
                },
              },
              required: ['search_term'],
            },
          },
          {
            name: 'contacts_get_person_info',
            description: 'Get detailed information for a specific person',
            inputSchema: {
              type: 'object',
              properties: {
                person_name: {
                  type: 'string',
                  description: 'Full name of the person to get info for',
                },
              },
              required: ['person_name'],
            },
          },
          {
            name: 'reminders_get_lists',
            description: 'Get all reminder lists with reminder counts',
            inputSchema: {
              type: 'object',
              properties: {},
            },
          },
          {
            name: 'reminders_get_incomplete_reminders',
            description: 'Get incomplete reminders across all lists',
            inputSchema: {
              type: 'object',
              properties: {
                limit: {
                  type: 'number',
                  description: 'Maximum number of reminders to return (default: 10)',
                },
              },
            },
          },
          {
            name: 'notes_get_folders',
            description: 'Get all note folders with note counts',
            inputSchema: {
              type: 'object',
              properties: {},
            },
          },
          {
            name: 'notes_get_recent_notes',
            description: 'Get recently modified notes',
            inputSchema: {
              type: 'object',
              properties: {
                limit: {
                  type: 'number',
                  description: 'Maximum number of notes to return (default: 10)',
                },
              },
            },
          },
          {
            name: 'notes_search_notes',
            description: 'Search notes by title or content',
            inputSchema: {
              type: 'object',
              properties: {
                query: {
                  type: 'string',
                  description: 'Search query for note title or content',
                },
              },
              required: ['query'],
            },
          },
          {
            name: 'notes_create_note',
            description: 'Create new note with title and content',
            inputSchema: {
              type: 'object',
              properties: {
                title: {
                  type: 'string',
                  description: 'Note title',
                },
                content: {
                  type: 'string',
                  description: 'Note content/body',
                },
                folder: {
                  type: 'string',
                  description: 'Target folder name (optional, defaults to "Notes")',
                },
              },
              required: ['title', 'content'],
            },
          },
          {
            name: 'textedit_get_documents',
            description: 'Get list of open TextEdit documents',
            inputSchema: {
              type: 'object',
              properties: {},
            },
          },
          {
            name: 'textedit_create_document',
            description: 'Create new TextEdit document with optional content',
            inputSchema: {
              type: 'object',
              properties: {
                content: {
                  type: 'string',
                  description: 'Optional initial content for the document',
                },
              },
            },
          },
          {
            name: 'calendar_get_calendars',
            description: 'Get all calendars with event counts',
            inputSchema: {
              type: 'object',
              properties: {},
            },
          },
          {
            name: 'calendar_create_event',
            description: 'Create new calendar event',
            inputSchema: {
              type: 'object',
              properties: {
                title: {
                  type: 'string',
                  description: 'Event title/summary',
                },
                start_datetime: {
                  type: 'string',
                  description: 'Start date and time (YYYY-MM-DD HH:MM format)',
                },
                end_datetime: {
                  type: 'string',
                  description: 'End date and time (YYYY-MM-DD HH:MM format)',
                },
                calendar: {
                  type: 'string',
                  description: 'Target calendar name (optional, defaults to "Calendar")',
                },
                notes: {
                  type: 'string',
                  description: 'Event notes/description (optional)',
                },
              },
              required: ['title', 'start_datetime', 'end_datetime'],
            },
          },
          {
            name: 'calendar_get_today_events',
            description: 'Get today\'s events across all calendars',
            inputSchema: {
              type: 'object',
              properties: {},
            },
          },
          {
            name: 'calendar_get_upcoming_events',
            description: 'Get upcoming events in date range',
            inputSchema: {
              type: 'object',
              properties: {
                days: {
                  type: 'number',
                  description: 'Number of days ahead to look (default: 7)',
                },
              },
            },
          },
          {
            name: 'workflow_contact_to_textedit',
            description: 'Get contact information and create formatted TextEdit document',
            inputSchema: {
              type: 'object',
              properties: {
                name: {
                  type: 'string',
                  description: 'Person name to look up',
                },
                title: {
                  type: 'string',
                  description: 'Document title (optional)',
                },
              },
              required: ['name'],
            },
          },
          {
            name: 'mail_create_message',
            description: 'Create new email message with recipients, subject, and body',
            inputSchema: {
              type: 'object',
              properties: {
                to: {
                  type: 'string',
                  description: 'Recipient email address',
                },
                subject: {
                  type: 'string',
                  description: 'Email subject',
                },
                body: {
                  type: 'string',
                  description: 'Email body content',
                },
                cc: {
                  type: 'string',
                  description: 'CC recipient email address (optional)',
                },
              },
              required: ['to', 'subject', 'body'],
            },
          },
          {
            name: 'mail_send_message',
            description: 'Send the most recently created message',
            inputSchema: {
              type: 'object',
              properties: {},
            },
          },
          {
            name: 'reminders_create_reminder',
            description: 'Create new reminder with title, optional due date and list',
            inputSchema: {
              type: 'object',
              properties: {
                title: {
                  type: 'string',
                  description: 'Reminder title',
                },
                due_date: {
                  type: 'string',
                  description: 'Due date in format YYYY-MM-DD (optional)',
                },
                list: {
                  type: 'string',
                  description: 'Target reminder list name (optional, defaults to "Reminders")',
                },
                notes: {
                  type: 'string',
                  description: 'Reminder notes/body (optional)',
                },
              },
              required: ['title'],
            },
          },
        ],
      };
    });
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 of behavioral disclosure. It mentions 'get contact information' and 'create formatted TextEdit document,' implying a read operation followed by a write operation, but doesn't detail permissions, formatting specifics, error handling, or output behavior. This is inadequate for a tool that performs multiple actions without annotation support.

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: 'Get contact information and create formatted TextEdit document.' It is front-loaded with the core purpose, has no redundant words, and every part contributes directly to understanding the tool's function, making it highly concise and well-structured.

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 the tool's complexity (combining contact lookup and document creation) and lack of annotations or output schema, the description is insufficient. It doesn't explain the workflow, how data is formatted, what the output looks like, or potential errors. This leaves significant gaps for an agent to understand and use the tool 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?

The input schema has 100% description coverage, with parameters 'name' and 'title' clearly documented. The description doesn't add any semantic details beyond the schema, such as formatting rules or lookup constraints. With high schema coverage, the baseline score of 3 is appropriate, as the schema handles parameter documentation effectively.

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 tool's purpose: 'Get contact information and create formatted TextEdit document.' It specifies the verb ('get' and 'create') and resources ('contact information' and 'TextEdit document'), making the action clear. However, it doesn't differentiate from sibling tools like 'contacts_get_person_info' or 'textedit_create_document', which handle similar operations separately.

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?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention sibling tools such as 'contacts_get_person_info' for getting contact info or 'textedit_create_document' for creating documents, nor does it specify prerequisites or exclusions. This leaves the agent without context for tool selection.

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/samicokar/mcp-mac'

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