# How to integrate Ashby MCP with Vercel AI SDK v6

```json
{
  "title": "How to integrate Ashby MCP with Vercel AI SDK v6",
  "toolkit": "Ashby",
  "toolkit_slug": "ashby",
  "framework": "Vercel AI SDK",
  "framework_slug": "ai-sdk",
  "url": "https://composio.dev/toolkits/ashby/framework/ai-sdk",
  "markdown_url": "https://composio.dev/toolkits/ashby/framework/ai-sdk.md",
  "updated_at": "2026-05-12T10:02:01.759Z"
}
```

## Introduction

This guide walks you through connecting Ashby to Vercel AI SDK v6 using the Composio tool router. By the end, you'll have a working Ashby agent that can list all candidates for open roles, post a new job opening for engineering, summarize candidates in interview stage through natural language commands.
This guide will help you understand how to give your Vercel AI SDK agent real control over a Ashby account through Composio's Ashby MCP server.
Before we dive in, let's take a quick look at the key ideas and tools involved.

## Also integrate Ashby with

- [OpenAI Agents SDK](https://composio.dev/toolkits/ashby/framework/open-ai-agents-sdk)
- [Claude Agent SDK](https://composio.dev/toolkits/ashby/framework/claude-agents-sdk)
- [Claude Code](https://composio.dev/toolkits/ashby/framework/claude-code)
- [Claude Cowork](https://composio.dev/toolkits/ashby/framework/claude-cowork)
- [Codex](https://composio.dev/toolkits/ashby/framework/codex)
- [OpenClaw](https://composio.dev/toolkits/ashby/framework/openclaw)
- [Hermes](https://composio.dev/toolkits/ashby/framework/hermes-agent)
- [CLI](https://composio.dev/toolkits/ashby/framework/cli)
- [Google ADK](https://composio.dev/toolkits/ashby/framework/google-adk)
- [LangChain](https://composio.dev/toolkits/ashby/framework/langchain)
- [Mastra AI](https://composio.dev/toolkits/ashby/framework/mastra-ai)
- [LlamaIndex](https://composio.dev/toolkits/ashby/framework/llama-index)
- [CrewAI](https://composio.dev/toolkits/ashby/framework/crew-ai)

## TL;DR

Here's what you'll learn:
- How to set up and configure a Vercel AI SDK agent with Ashby integration
- Using Composio's Tool Router to dynamically load and access Ashby tools
- Creating an MCP client connection using HTTP transport
- Building an interactive CLI chat interface with conversation history management
- Handling tool calls and results within the Vercel AI SDK framework

## What is Vercel AI SDK?

The Vercel AI SDK is a TypeScript library for building AI-powered applications. It provides tools for creating agents that can use external services and maintain conversation state.
Key features include:
- streamText: Core function for streaming responses with real-time tool support
- MCP Client: Built-in support for Model Context Protocol via @ai-sdk/mcp
- Step Counting: Control multi-step tool execution with stopWhen: stepCountIs()
- OpenAI Provider: Native integration with OpenAI models

## What is the Ashby MCP server, and what's possible with it?

The Ashby MCP server is an implementation of the Model Context Protocol that connects your AI agent and assistants like Claude, Cursor, etc directly to your Ashby account. It provides structured and secure access to your recruiting data, so your agent can perform actions like managing job postings, tracking candidate progress, scheduling interviews, and generating hiring reports on your behalf.
- Automated job posting management: Easily create, update, or close job listings across your organization with direct agent assistance.
- Candidate pipeline tracking: Have your agent fetch, organize, and update candidate progress through every stage of the hiring process.
- Interview scheduling and coordination: Let your agent schedule interviews, send calendar invites, and manage interviewer assignments to streamline the process.
- Data-driven hiring analytics: Generate reports and insights about your hiring funnel, candidate sources, and time-to-hire with a simple agent request.
- Centralized communication with applicants: Enable your agent to send status updates, feedback, or reminders to candidates, keeping everyone in the loop automatically.

## Supported Tools

| Tool slug | Name | Description |
|---|---|---|
| `ASHBY_ADD_CANDIDATE_PROJECT` | Add Candidate to Project | Add a candidate to a project in Ashby. Projects are sourcing initiatives or recruiting campaigns used to organize and track candidate pipelines. Use this to associate candidates with specific sourcing efforts. Common use cases: - Adding sourced candidates to recruiting campaigns - Organizing candidates by sourcing channel or initiative - Tracking candidates in specific hiring projects Requires candidatesWrite permission. Returns the updated candidate object on success, or error details if the candidate or project is not found. |
| `ASHBY_ADD_CANDIDATE_TAG` | Add Candidate Tag | Add a tag to a candidate in Ashby. Use this to categorize and organize candidates with existing tags from the system. This action allows you to apply labels/tags to candidates for filtering, searching, and organizational purposes. Tags must already exist in the system - use LIST_CANDIDATE_TAGS to view available tags or CREATE_CANDIDATE_TAG to create new ones. The response includes the full updated candidate object when successful, showing the newly added tag in the candidate's tags array. If a candidate already has the tag, the operation will still succeed idempotently. |
| `ASHBY_ADD_HIRING_TEAM_MEMBER` | Add Hiring Team Member | Add an Ashby user to a hiring team at the application, job, or opening level. Use this when you need to assign a team member to participate in the hiring process with a specific role. Requires the organizationWrite permission. |
| `ASHBY_ADD_INTERVIEWER_POOL_USER` | Add User to Interviewer Pool | Add a user to an interviewer pool. Use this to assign interviewers to interview pools for scheduling and coordination. Requires the hiringProcessMetadataWrite permission. |
| `ASHBY_ADD_OPENING_JOB` | Add Opening Job | Adds a job to an opening (job requisition) in Ashby ATS. An opening represents a position to be filled, while a job defines the role details. This action associates an existing job with an existing opening, allowing multiple jobs to be linked to a single opening. Use this when you need to add job definitions to an opening or when creating multi-role requisitions. Requires the jobsWrite permission. On success, returns the updated opening with the job added to its jobIds array. On failure, returns error details with specific error codes. |
| `ASHBY_ADD_OPENING_LOCATION` | Add Opening Location | Tool to add a location to an opening (job requisition). Use when you need to associate a location with an existing opening. Requires the jobsWrite permission. |
| `ASHBY_ANONYMIZE_CANDIDATE` | Anonymize Candidate | Anonymize a candidate by removing personally identifiable information. Use this when you need to permanently anonymize a candidate's data. This action cannot be reversed and requires all of the candidate's applications to be in the archived or hired state. |
| `ASHBY_APPROVE_OFFER` | Approve Offer | Approve an offer or a specific approval step within an offer's approval process. This action supports two modes: - Complete offer approval: Provide only offerId to approve the entire offer - Step-specific approval: Provide offerId, approvalStepId, and userId to approve a specific step When approving a specific step, both approvalStepId and userId are required together. Requires the 'offersWrite' permission. |
| `ASHBY_ARCHIVE_DEPARTMENT` | Archive Department | Archive a department by its unique identifier. Archiving a department marks it as inactive while preserving all its data and historical records. Archived departments are excluded from active department lists by default but can be restored later using the restore department action. This action is idempotent - archiving an already archived department will succeed and return the same result. Required permission: organizationWrite |
| `ASHBY_ARCHIVE_INTERVIEWER_POOL` | Archive Interviewer Pool | Archive an interviewer pool in Ashby. This marks the pool as archived, making it unavailable for new interview assignments while preserving historical data. Use this when an interviewer pool is no longer needed (e.g., for deprecated roles or outdated interview formats). The operation is idempotent - archiving an already archived pool will succeed without errors. Requires the hiringProcessMetadataWrite permission. To restore an archived pool, use the ASHBY_RESTORE_INTERVIEWER_POOL action. |
| `ASHBY_ARCHIVE_LOCATION` | Archive Location | Archives a location or location hierarchy in Ashby. When archived, the location is marked as inactive (isArchived=true) and typically becomes unavailable for new job postings or assignments. The operation is idempotent - archiving an already archived location succeeds without error. Requires the organizationWrite permission. Common use cases: - Decommissioning office locations that are no longer in use - Removing obsolete location entries from active use - Managing location lifecycle when restructuring office locations Note: Use restore_location to unarchive a location if needed. |
| `ASHBY_CHANGE_APPLICATION_SOURCE` | Change Application Source | Change the source attribution of an application. Use this to update which source and user should be credited for bringing in the candidate. |
| `ASHBY_CHANGE_APPLICATION_STAGE` | Change Application Stage | Move an application to a different interview stage in the hiring pipeline. Use this to: - Advance candidates to the next interview stage (e.g., from "Application Review" to "First Round") - Move candidates to any stage within their job's interview plan (forward or backward) - Archive applications by moving to an Archived stage (requires archiveReasonId) - Move to Offer or Hired stages when progressing candidates Important: The interview stage must belong to the same interview plan as the application's job. Use LIST_INTERVIEW_STAGES with the application's job interview plan ID to get valid stage options. |
| `ASHBY_CREATE_APPLICATION` | Create Application | Create a new job application by associating a candidate with a job opening in Ashby ATS. Use this action to: - Add a candidate to a job's hiring pipeline and interview process - Track the source of the application (referral, job board, etc.) - Credit a user (recruiter/referrer) for bringing in the candidate - Place the candidate directly into a specific interview stage (optional) The created application will automatically be assigned to the job's hiring team and start tracking the candidate's progress through the interview stages. Required: candidateId and jobId must reference existing entities in Ashby. |
| `ASHBY_CREATE_CANDIDATE` | Create Candidate | Create a new candidate in the system. Use this to add candidates with their contact information and social profiles before applying them to jobs. |
| `ASHBY_CREATE_CANDIDATE_NOTE` | Create Candidate Note | Create a note on a candidate profile. Use this to add comments, observations, or feedback about a candidate during the recruitment process. |
| `ASHBY_CREATE_CANDIDATE_TAG` | Create Candidate Tag | Create a new candidate tag in Ashby for categorizing and organizing candidates. Tags are labels that help you filter, search, and group candidates for better organization and workflow management. Use this action to create custom tags based on skills, experience levels, departments, or any other criteria relevant to your hiring process. Returns the newly created tag with its unique ID, title, and archived status. Tags are created in an active (non-archived) state by default. |
| `ASHBY_CREATE_CUSTOM_FIELD` | Create Custom Field | Create a new custom field in Ashby. Custom fields allow you to track additional structured data on Ashby objects like candidates, applications, and jobs. This action creates the field definition that can then be populated with values using the set custom field value actions. Supported field types: - String: Free text input - Number: Numeric values - Boolean: True/false values - Date: Date values (optionally date-only without time) - ValueSelect: Single selection from predefined options - MultiValueSelect: Multiple selections from predefined options - Employee: Reference to an Ashby employee (limited object type support) Note: ValueSelect and MultiValueSelect field types require the selectableValues parameter. Requires the hiringProcessMetadataWrite permission. |
| `ASHBY_CREATE_DEPARTMENT` | Create Department | Create a new department. Use this to add organizational departments or teams for structuring jobs and hierarchy. |
| `ASHBY_CREATE_INTERVIEWER_POOL` | Create Interviewer Pool | Create a new interviewer pool. Use this to organize interviewers into groups by expertise or interview type. Requires the hiringProcessMetadataWrite permission. |
| `ASHBY_CREATE_JOB` | Create Job | Create a new job opening in Ashby ATS. Use this action to: - Add a new role to your organization with a title, department, location, and brand - Set up the job structure before publishing or opening it to candidates - Optionally assign a default interview plan to define the hiring process Important notes: - Newly created jobs start in 'Draft' status and cannot accept applications yet - To open the job for applications, you must set a defaultInterviewPlanId either during creation or update it later - Use LIST_DEPARTMENTS, LIST_LOCATIONS, LIST_BRAND, and LIST_INTERVIEW_PLANS actions to get valid IDs - The job creator is automatically set as the author |
| `ASHBY_CREATE_LOCATION` | Create Location | Create a new location or location hierarchy. Use this to add office locations, remote work settings, or nested location structures for organizational management. |
| `ASHBY_CREATE_OFFER` | Create Offer | Create a new offer for a candidate in Ashby ATS. Use this to generate formal job offers with all required field values after starting an offer process and obtaining the form definition. Prerequisites: Must first call offerProcess.start to get offerProcessId, then offer.start to get offerFormId and field definitions. Date fields must use YYYY-MM-DD format (not ISO timestamp). |
| `ASHBY_CREATE_OPENING` | Create Opening | Create a new opening (job requisition) in Ashby ATS. An opening represents a job requisition - a request to hire for a specific position. All parameters are optional, but typically you'll want to provide at least a title, job IDs, and location IDs for a meaningful opening. Use this action to: - Create a new job opening with specific requirements - Associate openings with existing jobs, locations, and teams - Set hiring targets, employment type, and opening state - Track backfill positions vs new roles Required permission: openingsWrite Note: To get valid IDs for teamId, locationIds, and jobIds, use the corresponding List Departments, List Locations, and List Jobs actions. |
| `ASHBY_CREATE_REFERRAL` | Create Referral | Create a referral in Ashby ATS by submitting a referral form with candidate information. Use this action to formally submit employee referrals for job openings, which creates both a candidate record (if new) and an application linked to the specified job. |
| `ASHBY_CREATE_SURVEY_REQUEST` | Create Survey Request | Generate a survey request and receive a survey URL to send to a candidate. Use this when you need to collect feedback or information from candidates via a survey. Requires candidatesWrite permission. Note that calling this endpoint does not automatically email the survey to the candidate. |
| `ASHBY_CREATE_SURVEY_SUBMISSION` | Create Survey Submission | Create a new survey submission for a candidate's application. Use this to submit candidate survey responses programmatically, such as candidate experience ratings or feedback forms. Requires the candidatesWrite permission. |
| `ASHBY_GET_API_KEY_INFO` | Get API Key Info | Retrieve information about the current API key, including associated organization, user details, and permissions. Use this to verify API key validity and check what access level the key has. Useful as a prerequisite check before write operations (e.g., ASHBY_UPDATE_CANDIDATE) or list operations (e.g., ASHBY_LIST_APPLICATIONS) to confirm the key has required scopes. |
| `ASHBY_GET_APPLICATION_INFO` | Get Application Info | Retrieve detailed information about a specific application by its ID. Returns complete details including candidate info, interview stages, and hiring team nested under data.results. To find a recruiting coordinator, match hiringTeam entries where role == "Recruiting Coordinator" (exact string). Optional fields such as primaryEmailAddress may be null; handle accordingly. |
| `ASHBY_GET_CANDIDATE_INFO` | Get Candidate Info | Retrieve detailed information about a specific candidate by their ID. This action returns comprehensive candidate data including: - Basic information (name, position, company, school) - Contact details (email addresses, phone numbers) - Social profiles (LinkedIn, etc.) - Associated applications and tags - Custom field values - Source and credited user information - Location and timezone - Profile URL Use this when you need complete details about a specific candidate. To find candidate IDs, use the list_candidates or search_candidates actions first. |
| `ASHBY_GET_CUSTOM_FIELD_INFO` | Get Custom Field Info | Retrieve detailed information about a specific custom field by its ID. Use this to get custom field details including title, object type, field type, and selectable values. Requires the hiringProcessMetadataRead permission. |
| `ASHBY_GET_DEPARTMENT_INFO` | Get Department Info | Retrieve detailed information about a specific department by its ID. Returns comprehensive department details including: - Department name (internal and external) - Parent department relationship (parentId for hierarchical structure) - Archive status (isArchived) - Creation and last update timestamps Use this action when you need information about a specific department (e.g., to verify a department exists, check its archive status, or understand organizational hierarchy). For listing all departments or finding department IDs, use the List Departments action instead. Requires 'organizationRead' permission. |
| `ASHBY_GET_FEEDBACK_FORM_DEFINITION` | Get Feedback Form Definition | Retrieve detailed information about a specific feedback form definition by its ID. Use this action to: - Get the complete structure of a feedback form including all sections and fields - View field types (ValueSelect, MultiValueSelect, Boolean, RichText) and their configurations - Check if a form is the default form or a custom scorecard - See which interview the form is linked to (if any) - Access selectable values and field metadata for form rendering To find available feedback form IDs, use the List Feedback Form Definitions action first. Requires the hiringProcessMetadataRead permission. |
| `ASHBY_GET_FILE_INFO` | Get File Info | Retrieve the URL of a file associated with a candidate. Use this to get a pre-signed S3 URL for accessing candidate files like resumes. Requires the candidatesRead permission. |
| `ASHBY_GET_INTERVIEWER_POOL_INFO` | Get Interviewer Pool Info | Retrieve detailed information about a specific interviewer pool by its ID. Returns comprehensive pool information including: - Basic details (ID, title, archived status) - Qualified members who can conduct interviews - Trainees currently in training - Training path configuration with stages and requirements Use this to view interviewer pool composition, training programs, and member status for scheduling interviews or managing interviewer assignments. |
| `ASHBY_GET_INTERVIEWER_USER_SETTINGS` | Get Interviewer User Settings | Get interviewer settings for a specific user by their ID. Use this to retrieve daily and weekly interview limits configured for the user. Requires the organizationRead permission. |
| `ASHBY_GET_INTERVIEW_INFO` | Get Interview Info | Retrieve detailed information about a specific interview type by its ID. Use this to get interview details including title, instructions, and feedback form configuration. |
| `ASHBY_GET_INTERVIEW_STAGE_INFO` | Get Interview Stage Info | Tool to fetch interview stage details by ID. Use when you need specific information about an interview stage including its title, type, order, and parent interview plan. Requires the interviewsRead permission. |
| `ASHBY_GET_JOB_INFO` | Get Job Info | Retrieve detailed information about a specific job by its ID. Use this to get complete details about a job including title, status, hiring team, interview plans, and more. Job offer payloads omit full job details; combine with ASHBY_LIST_APPLICATIONS and ASHBY_GET_APPLICATION_INFO for complete reporting data. |
| `ASHBY_GET_JOB_INTERVIEW_PLAN_INFO` | Get Job Interview Plan Info | Retrieve the interview plan information for a specific job. Use this to get details about the interview stages and process configured for a job. |
| `ASHBY_GET_JOB_POSTING_INFO` | Get Job Posting Info | Retrieve detailed information about a specific job posting by its ID. Use this to get complete job posting details including full description, application form fields, compensation, and publish settings. |
| `ASHBY_GET_LOCATION_INFO` | Get Location Info | Retrieve detailed information about a specific location by its ID. Use this to get complete location details including address, remote status, and workplace type. |
| `ASHBY_GET_OFFER_INFO` | Get Offer Info | Retrieve detailed information about a specific offer by its ID. This action returns comprehensive offer data including application ID, status, version, timestamps, candidate ID, job ID, start date, and custom fields. Use this when you need complete details about a specific offer. To find offer IDs, use the list_offers action first. Requires the 'offersRead' permission. |
| `ASHBY_GET_OPENING_INFO` | Get Opening Info | Retrieve detailed information about a specific opening (job requisition) by its ID. An opening represents a headcount position that needs to be filled. Each opening belongs to a job and tracks hiring progress including state (Open/Closed), hiring team members, employment type, and target dates. Use this action to: - Get complete details about a specific opening including its current state - View the hiring team assigned to the opening - Check employment type, target hire date, and target start date - Access custom fields associated with the opening Returns opening details on success, or error information if the opening ID is invalid. |
| `ASHBY_GET_REFERRAL_FORM` | Get Referral Form | Fetches the default referral form or creates a default referral form if none exists. Requires the hiringProcessMetadataRead permission. |
| `ASHBY_GET_SURVEY_FORM_DEFINITION` | Get Survey Form Definition | Retrieve detailed information about a specific survey form definition by its ID. This action returns comprehensive details about a survey form including: - Survey title and type (e.g., Diversity, EEOC, QualityOfHire) - Form definition with sections and fields - Field types (ValueSelect, MultiValueSelect, RichText, LinearRating, etc.) - Selectable values for choice fields - Required field indicators - Archive status Use the LIST_SURVEY_FORM_DEFINITIONS action first to discover available survey form IDs. Common survey types include candidate experience surveys, hiring manager assessments, quality of hire evaluations, and EEOC compliance forms. Requires the hiringProcessMetadataRead permission. |
| `ASHBY_GET_USER_INFO` | Get User Info | Retrieve detailed information about a specific user by their ID. Use this to get user details including name, email, role, and permissions. |
| `ASHBY_LIST_APPLICATION_CRITERIA_EVALUATIONS` | List Application Criteria Evaluations | Retrieve AI-generated criteria evaluations for an application. Use this to get the AI assessment of how well a candidate meets specific job requirements, including evaluation outcomes and reasoning. |
| `ASHBY_LIST_APPLICATION_FEEDBACK` | List Application Feedback | Retrieve all feedback submissions for an application. Use this to view interviewer feedback, evaluations, and notes collected throughout the hiring process. |
| `ASHBY_LIST_APPLICATION_HIRING_TEAM_ROLE` | List Application Hiring Team Roles | Retrieve all available hiring team roles for applications in the organization. These roles can be assigned to hiring team members at the application level (e.g., Recruiter, Hiring Manager, Recruiting Coordinator, Sourcer). Use this endpoint to get the complete list of role IDs and titles that are available for assignment. Requires candidatesRead permission. Note: This differs from the general hiring team roles list - this specifically returns roles used at the application level in the hiring workflow. |
| `ASHBY_LIST_APPLICATION_HISTORY` | List Application History | Retrieve the complete history of stage transitions for an application. Use this to get detailed information about when and how a candidate moved through different interview stages. |
| `ASHBY_LIST_APPLICATIONS` | List Applications | Retrieve a list of applications with optional pagination and sync-token filtering for incremental updates. No server-side recruiter filter exists; attribute applications to team members client-side. |
| `ASHBY_LIST_APPROVAL` | List Approvals | Retrieve a list of approvals (offer approvals, job approvals, etc.). Use this to track approval workflows and pending approvals. |
| `ASHBY_LIST_ARCHIVE_REASONS` | List Archive Reasons | Retrieve a list of all archive reasons. Use this to see available reasons for archiving candidates or applications, such as rejection categories. |
| `ASHBY_LIST_BRAND` | List Brands | Retrieve a list of all brands for the organization. Use this to get available brands for job postings and employer branding. Requires organizationRead permission. |
| `ASHBY_LIST_CANDIDATE_CLIENT_INFO` | List Candidate Client Info | Retrieve all client info records for a specific candidate with pagination support. Client info records contain additional client-specific information associated with candidates in your Ashby account. This endpoint supports pagination and incremental synchronization for efficient data retrieval. Use the cursor and limit parameters to paginate through large result sets. For incremental syncing, use the syncToken to fetch only changes since your last sync. Requires candidatesRead permission. |
| `ASHBY_LIST_CANDIDATE_NOTES` | List Candidate Notes | Retrieve all notes for a specific candidate in Ashby. Use this action to view comments, observations, and internal notes added by recruiters and hiring team members during the hiring process. Common use cases: - Review feedback and observations from interviewers - Check historical communication and decisions about a candidate - Audit the hiring process for a specific candidate Prerequisites: - You need a valid candidateId. Get this from list_candidates or search_candidates. - Requires 'candidatesRead' permission in Ashby. Returns an empty list if the candidate has no notes. |
| `ASHBY_LIST_CANDIDATE_PROJECTS` | List Candidate Projects | Retrieve all projects associated with a candidate. Use this to see which sourcing projects or recruiting initiatives a candidate is part of. |
| `ASHBY_LIST_CANDIDATES` | List Candidates | Retrieve a list of candidates. Use this to fetch all candidates with optional pagination and filtering by sync token for incremental updates. |
| `ASHBY_LIST_CANDIDATE_TAGS` | List Candidate Tags | Retrieve a list of all candidate tags in your Ashby account. Tags are labels used to categorize and organize candidates for filtering and search purposes. Use this action to: - Get all available tags for tagging candidates - Implement incremental sync using the syncToken parameter - Paginate through large sets of tags using cursor Returns tag id, title, and archived status for each tag. |
| `ASHBY_LIST_CLOSE_REASONS` | List Close Reasons | Lists all close reasons for jobs or openings. Close reasons categorize why a job or opening was closed (e.g., rejected by candidate, rejected by organization, or other reasons). Requires hiringProcessMetadataRead permission. |
| `ASHBY_LIST_COMMUNICATION_TEMPLATES` | List Communication Templates | Retrieve a list of all communication templates. Use this to see available email and message templates for candidate outreach. |
| `ASHBY_LIST_CUSTOM_FIELDS` | List Custom Fields | Retrieve a list of all custom field definitions configured in Ashby. Use this action to discover what custom fields are available for candidates, applications, jobs, offers, and openings. This is useful when you need to: - Know what custom fields exist before setting values on resources - Understand the data types and available options for each field - Identify the IDs of custom fields needed for other API operations Returns fields with their types (String, Number, Boolean, Date, ValueSelect, MultiValueSelect, Currency, CompensationRange) and selectable values for dropdown fields. |
| `ASHBY_LIST_DEPARTMENTS` | List Departments | Retrieve a list of all departments in the organization. Use this action to: - Get all departments for organizing jobs and candidates by team structure - Find department IDs needed for creating or filtering jobs - Understand the organizational hierarchy via parentId relationships - Perform incremental syncs to detect newly created or updated departments Supports pagination for large organizations. When moreDataAvailable is true, use the nextCursor value in subsequent requests to fetch more results. |
| `ASHBY_LIST_FEEDBACK_FORM_DEFINITIONS` | List Feedback Form Definitions | Retrieve all feedback form definitions from your Ashby organization. Use this action to: - Discover available interview feedback templates - Get form IDs for use with other feedback-related actions - Understand the structure of feedback forms including fields and sections - Identify the default feedback form vs custom scorecards Returns a list of feedback forms with their complete structure, including field types (ValueSelect, MultiValueSelect, Boolean, RichText) and selectable values. |
| `ASHBY_LIST_HIRING_TEAM_ROLE` | List Hiring Team Roles | Retrieve a list of possible hiring team roles in the organization. Use this to get available roles that can be assigned to members of a hiring team (e.g., Hiring Manager, Recruiter, Recruiting Coordinator, Sourcer). Requires the organizationRead permission. |
| `ASHBY_LIST_INTERVIEWER_POOLS` | List Interviewer Pools | Retrieve a list of all interviewer pools. Interviewer pools are groups of interviewers organized by expertise, role, or interview type (e.g., "Backend Engineer", "Bar Raiser"). Use this to see available interviewer pools for scheduling interviews or managing interview assignments. Supports pagination via cursor and incremental sync via syncToken. |
| `ASHBY_LIST_INTERVIEW_EVENTS` | List Interview Events | Retrieves all interview events for a specific interview schedule. Each interview event represents a scheduled time slot with assigned interviewers for conducting interviews. Use this action to: - Get details about scheduled interviews including start/end times, locations, and meeting links - View which interviewers are assigned to each interview event - Check feedback submission status for interviews - Access feedback submission links for interviewers Key information returned: - Event timing (start/end times in ISO 8601 format) - Interviewer details (names, emails, roles) - Meeting logistics (physical location or virtual meeting link) - Feedback status and submission links Supports pagination via cursor and incremental synchronization via syncToken. Requires interviewsRead permission. |
| `ASHBY_LIST_INTERVIEW_PLANS` | List Interview Plans | Retrieve a list of interview plans. Use this to get the structured interview processes and stages configured for different roles. |
| `ASHBY_LIST_INTERVIEWS` | List Interview Types | List all interview types defined in Ashby. Returns interview templates/definitions (e.g., 'Technical Phone Screen', 'Debrief', 'Intro Call with CEO') rather than scheduled interview instances. Use this to discover available interview types for building interview plans or to sync interview type data. Supports pagination via cursor and incremental sync via syncToken. |
| `ASHBY_LIST_INTERVIEW_SCHEDULES` | List Interview Schedules | Retrieve a list of interview schedules. Use this to fetch all scheduled interviews with candidates, including timing, interviewers, and interview details. |
| `ASHBY_LIST_INTERVIEW_STAGE_GROUPS` | List Interview Stage Groups | Retrieve a list of interview stage groups. Use this to see how interview stages are organized into logical groups within interview plans. |
| `ASHBY_LIST_INTERVIEW_STAGES` | List Interview Stages | Retrieve all interview stages for an interview plan in order. Use this to get the structured sequence of stages a candidate progresses through during the interview process for a specific role. Requires interviewsRead permission. |
| `ASHBY_LIST_JOB_BOARDS` | List Job Boards | Retrieve a list of job boards. Use this to see where job postings can be published (e.g., LinkedIn, Indeed, company career page). |
| `ASHBY_LIST_JOB_POSTINGS` | List Job Postings | Retrieve a list of job postings. Use this to fetch all public job postings with optional pagination and filtering. May return zero results even when open jobs exist; if so, use ASHBY_LIST_JOBS as a fallback. |
| `ASHBY_LIST_JOBS` | List Jobs | Retrieve a list of all jobs from Ashby ATS (Applicant Tracking System). Use this action to: - Fetch all open, closed, or draft jobs in your organization - Get job details including title, status, employment type, department, and hiring team - Paginate through large result sets using cursor-based pagination - Perform incremental syncs using syncToken to only fetch changed jobs Each job includes comprehensive information such as hiring team members, interview plan IDs, job posting IDs, custom fields, and timestamps. When ASHBY_LIST_JOB_POSTINGS returns zero results, use this action as the authoritative source for open job data. Job payloads exclude application details; follow up with ASHBY_LIST_APPLICATIONS or ASHBY_GET_APPLICATION_INFO for complete data. |
| `ASHBY_LIST_JOB_TEMPLATES` | List Job Templates | Retrieve a list of all job templates from Ashby ATS. Use this action to: - Discover available job templates for creating standardized job postings - View both active and inactive templates in your organization - Get template details including title, department, location, and employment type - Paginate through results using cursor-based pagination - Perform incremental syncs using syncToken to only fetch changed templates Job templates provide reusable configurations that help maintain consistency when creating new jobs in your organization. |
| `ASHBY_LIST_LOCATIONS` | List Locations | Retrieve a list of all locations. Use this to get available office locations and remote work settings for jobs. |
| `ASHBY_LIST_OFFERS` | List Offers | Retrieve a list of job offers with their latest versions. This action fetches all offers made to candidates in your Ashby account. Use pagination (cursor/perPage) to handle large result sets efficiently. Use syncToken for incremental updates to fetch only changed offers since last sync. Use createdAfter to filter offers by creation time. Requires the 'offersRead' permission. |
| `ASHBY_LIST_OPENINGS` | List Openings | Retrieve a list of openings (job requisitions). Use this to fetch all openings with optional pagination and filtering. Each opening may represent multiple hires; inspect headcount fields rather than assuming one opening equals one hire. |
| `ASHBY_LIST_PROJECTS` | List Projects | Retrieve a list of all projects. Use this to see candidate sourcing projects and recruiting initiatives. |
| `ASHBY_LIST_SOURCES` | List Sources | Retrieve a list of all candidate sources. Use this to get available sources for categorizing how candidates were sourced (e.g., LinkedIn, referrals, job boards). |
| `ASHBY_LIST_SOURCE_TRACKING_LINKS` | List Source Tracking Links | Retrieve all source tracking links configured in Ashby. Source tracking links are UTM-tagged URLs used to track where candidates are coming from (e.g., LinkedIn, job boards, referral campaigns). Use this action to: - View all active and inactive tracking links - Get tracking codes for analytics and reporting - Find the sourceId associated with each tracking link This is a read-only operation that requires no parameters. |
| `ASHBY_LIST_SURVEY_FORM_DEFINITIONS` | List Survey Form Definitions | Retrieve a list of all survey form definitions from Ashby. Use this action to discover available candidate survey templates including: - Candidate Experience Surveys (NPS ratings for candidate feedback) - Hiring Manager Experience Surveys (feedback on recruiting process) - Quality of Hire Assessments (post-hire performance ratings) - EEOC Surveys (equal employment opportunity information collection) Each survey form contains sections with fields defining the questions and response types. This is a read-only operation that does not require any parameters. |
| `ASHBY_LIST_SURVEY_SUBMISSIONS` | List Survey Submissions | Lists all survey submissions of a given survey type from Ashby. Survey submissions contain candidate responses to surveys sent during the hiring process. Use this action to: - Retrieve candidate experience survey responses - Access submitted survey data including answers and metadata - Paginate through large sets of submissions using cursor - Sync only new/updated submissions using syncToken - Filter submissions by creation date using createdAfter The most common survey type is 'CandidateExperience'. This action returns empty results if no submissions exist for the specified type. Requires candidatesRead permission. |
| `ASHBY_LIST_USERS` | List Users | Retrieve a list of all users in the organization. Use this to get information about team members, their roles, and permissions. |
| `ASHBY_MOVE_DEPARTMENT` | Move Department | Tool to move a department to another parent in the organizational hierarchy. Use when reorganizing department structure or changing parent-child relationships. Requires the organizationWrite permission. |
| `ASHBY_MOVE_LOCATION` | Move Location | Tool to move a location to a different parent in the location hierarchy. Use when reorganizing location structure or changing parent-child relationships. Requires the organizationWrite permission. |
| `ASHBY_REMOVE_HIRING_TEAM_MEMBER` | Remove Hiring Team Member | Remove an Ashby user from a hiring team at the application, job, or opening level. Use this when you need to unassign a team member from participating in the hiring process. Requires the jobsWrite permission. |
| `ASHBY_REMOVE_INTERVIEWER_POOL_USER` | Remove User from Interviewer Pool | Remove a user from an interviewer pool. Use this to unassign interviewers from specific interview pools when they should no longer be part of that pool's roster. This removes the user from either the qualified members list or trainees list, depending on their current status in the pool. Requires the hiringProcessMetadataWrite permission. |
| `ASHBY_REMOVE_OPENING_JOB` | Remove Opening Job | Remove a job from an opening (job requisition) in Ashby ATS. Use this action to disassociate a job from a specific opening. This is useful when: - A job was added to the wrong opening - You need to reorganize job-opening associations - An opening should no longer be associated with a particular job The action removes the job from the opening's jobIds list and returns the updated opening object. Both the opening and job must exist and be valid. Requires jobsWrite permission. |
| `ASHBY_REMOVE_OPENING_LOCATION` | Remove Opening Location | Tool to remove a location from an opening (job requisition). Use when you need to disassociate a location from an existing opening. Requires the jobsWrite permission. |
| `ASHBY_RESTORE_DEPARTMENT` | Restore Department | Restore an archived department by its unique identifier. Restoring a department marks it as active again, making it available in active department lists and allowing it to be used for new jobs and organizational structures. All historical data and relationships are preserved. This action is idempotent - restoring an already active department will succeed and return the same result. Use cases: - Reactivate a previously archived department that is needed again - Undo an accidental archive operation - Restore departments as part of organizational restructuring Required permission: organizationWrite |
| `ASHBY_RESTORE_INTERVIEWER_POOL` | Restore Interviewer Pool | Restore an archived interviewer pool in Ashby. This unarchives the pool, making it available again for new interview assignments. Use this when you need to reactivate an interviewer pool that was previously archived (e.g., reviving a pool for a role that's hiring again or correcting an accidental archive). The operation is idempotent - restoring an already active pool will succeed without errors. Requires the hiringProcessMetadataWrite permission. To archive a pool, use the ASHBY_ARCHIVE_INTERVIEWER_POOL action. |
| `ASHBY_RESTORE_LOCATION` | Restore Location | Restores an archived location or location hierarchy in Ashby. When restored, the location is marked as active (isArchived=false) and becomes available again for job postings, assignments, and other location-based operations. The operation is idempotent - restoring an already active location succeeds without error. Requires the organizationWrite permission. Common use cases: - Reactivating previously closed office locations - Undoing accidental location archives - Bringing back seasonal or temporary office locations - Restoring location availability during organizational restructuring Note: Use archive_location to archive a location if needed. |
| `ASHBY_SEARCH_CANDIDATES` | Search Candidates | Search for candidates by email or name. Use this for quick lookups to find specific candidates without pagination. Supports exact email match or partial name match. |
| `ASHBY_SEARCH_JOBS` | Search Jobs | Search for jobs by title in Ashby ATS (Applicant Tracking System). Use this action to: - Quickly find jobs by title without pagination - Perform partial match searches (e.g., "engineer" matches "Backend Engineer") - Look up specific job openings by name Returns matching jobs with full details including hiring team, department, location, interview plans, and job postings. Note: For listing all jobs or paginated results, use the List Jobs action instead. |
| `ASHBY_SEARCH_OPENING` | Search Opening | Search for openings by identifier. Use this for quick lookups to find specific openings without pagination. Returns empty results if no match found. |
| `ASHBY_SEARCH_PROJECTS` | Search Projects | Search for projects by title in Ashby. Projects in Ashby are used to track sourcing initiatives, recruiting campaigns, or candidate portfolios. Use this action to: - Quickly find projects by title without pagination - Perform partial match searches (e.g., "recruiting" matches "Engineering Recruiting 2024") - Look up specific sourcing projects or initiatives by name - Build project autocomplete features Returns matching projects with details including id, title, archive status, confidentiality settings, author, and creation timestamp. Note: Results are limited to 100 matches. If you need to browse all projects or expect more results, use the List Projects action instead for paginated access. |
| `ASHBY_SEARCH_USERS` | Search Users | Search for an Ashby user by email address. Use this to find specific team members in the organization when you know their email. Returns matching user details including their ID, name, role, and status. Requires organizationRead permission. |
| `ASHBY_SET_CUSTOM_FIELD_VALUE` | Set Custom Field Value | Set the value of a custom field for a given object (candidate, application, job, etc.). Use when you need to update a single custom field value. Important: When updating multiple custom fields on the same object, use customField.setValues instead to avoid race conditions. |
| `ASHBY_SET_CUSTOM_FIELD_VALUES` | Set Custom Field Values | Set the values of multiple custom fields for a given object in a single call. Use when you need to update multiple custom fields on the same object (candidate, application, job, or opening). This is the recommended approach to avoid race conditions that can occur with concurrent customField.setValue calls. |
| `ASHBY_SET_JOB_STATUS` | Set Job Status | Set the status of a job in Ashby ATS (Applicant Tracking System). Use this action to change the workflow state of a job: - Set to 'Open' to make a job actively accept applications - Set to 'Closed' to stop accepting applications for a job - Set to 'Draft' to put a job back into preparation mode Common use cases: - Close a job after filling the position - Open a draft job to start accepting applications - Transition a job through the hiring workflow Important limitations: - Cannot transition from Open to Draft (must close first) - Cannot transition from Closed to Open (create a new job instead) Returns the updated job details on success, or error information if the status transition is not allowed. |
| `ASHBY_SET_OPENING_ARCHIVED` | Set Opening Archived | Sets the archived state of an opening. Requires the jobsWrite permission. Use this to archive or unarchive job openings. |
| `ASHBY_SET_OPENING_OPENING_STATE` | Set Opening State | Set the workflow state of an opening (job requisition). Use this to transition an opening between states: Draft, Approved, Open, or Closed. Important notes: - When setting state to 'Closed', you must provide a closeReasonId (use LIST_CLOSE_REASONS to get valid IDs) - Not all state transitions are allowed by Ashby's workflow rules; the API will return an error for invalid transitions - Common valid transitions: Open ↔ Closed, Approved → Open - Returns the updated opening details including the new state, timestamps, and close reason if applicable |
| `ASHBY_START_OFFER` | Start Offer | Create a new offer version instance for an in-progress offer process. The returned offer version can be filled out and submitted using the Create Offer action. Use this action before creating a finalized offer to initialize the offer version that will hold the offer details. After starting the offer, use the Create Offer endpoint to populate and submit the offer form. Requires the 'offersWrite' permission. |
| `ASHBY_START_OFFER_PROCESS` | Start Offer Process | Start an offer process for a candidate's application in Ashby ATS. Use this action when you need to initiate the formal offer workflow for a candidate who has progressed through interviews and is ready to receive an offer. The application must be in the Offer stage before starting an offer process. Once started, the offer process will have a status (typically 'WaitingOnOfferCreation') and a unique identifier that can be used to track and manage the offer through completion. Requires the 'offersWrite' permission. |
| `ASHBY_SUBMIT_APPLICATION_FEEDBACK` | Submit Application Feedback | Submit structured feedback for an application using a feedback form. Use this for formal interview feedback and scorecards, not for informal notes or comments. |
| `ASHBY_TRANSFER_APPLICATION` | Transfer Application | Transfer an application to a different job position in Ashby ATS. Use this action to move a candidate's application from one job opening to another while optionally preserving their application history and context. This is useful when: - A candidate is better suited for a different open position - Job requirements change and the candidate should be considered for an alternative role - Internal mobility scenarios where employees are being considered for different positions The transfer will update the application's associated job, interview plan, and current interview stage. You can optionally trigger automatic activities (emails, tasks) configured for the target stage. |
| `ASHBY_UPDATE_APPLICATION` | Update Application | Update an application's properties in Ashby. Use this action to modify application metadata such as: - Source attribution (how the candidate found or was found for the job) - Credited user (who should get credit for sourcing the candidate) - Creation date (useful for backdating imported applications) - Notification settings for the update Note: To update custom fields on applications, use the customFields.setValue endpoint instead. To change the application's interview stage, use change_application_stage. To archive/unarchive, use the archive actions. Returns the full updated application object with all current field values. |
| `ASHBY_UPDATE_APPLICATION_HISTORY` | Update Application History | Update the complete history of an application's stage transitions. Use this to modify stage entry times, delete history events, or update existing history records. CRITICAL: This endpoint requires sending the COMPLETE application history array - use ASHBY_GET_APPLICATION_INFO first to retrieve all existing entries, then modify and send back the full array. Requires the 'candidatesWrite' permission and the 'Allow updating application history?' setting enabled in your admin API key configuration. |
| `ASHBY_UPDATE_CANDIDATE` | Update Candidate | Update an existing candidate's profile information in Ashby ATS. Use this action to modify candidate details like name, email, phone number, job title, company, school, and social profiles (LinkedIn, GitHub, website). Requires a valid candidateId which can be obtained from list_candidates or search_candidates actions. Note: To update custom fields, use the customFields.setValue endpoint separately. |
| `ASHBY_UPDATE_COMPENSATION_JOB` | Update Job Compensation | Update a job's compensation tiers in Ashby ATS. Use this action to define or modify the compensation structure for a job posting. You can specify multiple tiers (e.g., 'Junior', 'Senior', 'Staff') and each tier can include multiple compensation components like: - Salary (with min/max range, currency, and payment interval) - Equity (as percentage or cash value) - Bonus compensation - Commission structures Example use cases: - Setting salary ranges for a new job posting - Adding equity compensation to existing jobs - Updating compensation to reflect market changes - Creating tiered compensation for different experience levels Prerequisites: - You need a valid jobId from list_jobs or search_jobs - Compensation amounts should be appropriate for the currency and interval - At least one compensation tier with one component is required |
| `ASHBY_UPDATE_DEPARTMENT` | Update Department | Update an existing department's information such as its name. Use this action to: - Rename a department to reflect organizational changes - Update department names with Unicode/international characters - Modify department details as your organization evolves Prerequisites: - You need a valid departmentId (obtain from list_departments action) - The department must not be archived - At least one field (e.g., name) must be provided for the update Note: The externalName is automatically updated to match the name. |
| `ASHBY_UPDATE_INTERVIEWER_POOL` | Update Interviewer Pool | Update an existing interviewer pool's title or training requirements. Interviewer pools are groups of interviewers organized by expertise or role (e.g., "Backend Engineers", "Bar Raiser Interviews"). Use this action to: - Rename an interviewer pool by updating its title - Enable or disable training requirements for the pool - Update both title and training settings simultaneously At least one of title or requiresTraining must be provided. Use LIST_INTERVIEWER_POOLS to get valid pool IDs. Requires hiringProcessMetadataWrite permission. |
| `ASHBY_UPDATE_JOB` | Update Job | Update an existing job's properties in Ashby ATS. Use this action to modify job details such as: - Job title (e.g., change "Engineer" to "Senior Engineer") - Department/team assignment - Office location - Default interview plan - Confidentiality settings - Custom requisition ID Prerequisites: - You need a valid jobId from list_jobs or search_jobs - For teamId, get valid IDs from list_departments - For locationId, get valid IDs from list_locations - For defaultInterviewPlanId, get valid IDs from list_interview_plans Note: To update custom fields, use the customFields.setValue endpoint instead. To change job status (open/close/archive), use the set_job_status action. |
| `ASHBY_UPDATE_JOB_POSTING` | Update Job Posting | Update an existing job posting's details including title, description, and visibility status. This action allows you to modify public job postings properties such as: - Title: Change the job posting title - Description: Update the job description (HTML format, certain tags supported) - Listing Status: Control whether the posting is publicly visible on job boards - Description Customization: Suppress opening/closing sections for full API control Use this to keep job postings current, control visibility, or customize descriptions. Requires the jobsWrite permission. |
| `ASHBY_UPDATE_LOCATION_ADDRESS` | Update Location Address | Update the address of a location or location hierarchy. Use this when you need to modify location address details such as city, region, country, or postal code. Requires the organizationWrite permission. |
| `ASHBY_UPDATE_LOCATION_EXTERNAL_NAME` | Update Location External Name | Update a location's external (candidate-facing) name. Use this to set or clear an alternate name for a location that is displayed on job boards and used in the API. |
| `ASHBY_UPDATE_LOCATION_NAME` | Update Location Name | Update a location's name. Use this to rename an existing location. Requires the organizationWrite permission. |
| `ASHBY_UPDATE_LOCATION_REMOTE_STATUS` | Update Location Remote Status | Tool to update a location's remote status. Use when you need to mark a location as remote or non-remote. Requires organizationWrite permission. |
| `ASHBY_UPDATE_LOCATION_WORKPLACE_TYPE` | Update Location Workplace Type | Tool to update a location's workplace type (OnSite, Remote, or Hybrid). Use when you need to modify the workplace setting for an office location. Requires organizationWrite permission. |
| `ASHBY_UPDATE_OPENING` | Update Opening | Update properties of an existing opening (job requisition). Use this to modify opening details such as description, team, hire dates, and employment type. Requires jobsWrite permission. |
| `ASHBY_UPDATE_SELECTABLE_VALUES_CUSTOM_FIELD` | Update Selectable Values Custom Field | Update the selectable values for a ValueSelect or MultiValueSelect custom field. Use this to add new options, modify existing option labels, or archive outdated options. Values are merged with existing options - matching values are updated, new values are added. |
| `ASHBY_UPDATE_USER_INTERVIEWER_SETTINGS` | Update User Interviewer Settings | Update interviewer settings for a user, including daily and weekly interview limits. Use this when you need to set or modify interview scheduling constraints for a user. Either limit can be provided individually or both together; unprovided limits remain unchanged. |

## Supported Triggers

None listed.

## Creating MCP Server - Stand-alone vs Composio SDK

The Ashby MCP server is an implementation of the Model Context Protocol that connects your AI agent to Ashby. It provides structured and secure access so your agent can perform Ashby operations on your behalf through a secure, permission-based interface.
With Composio's managed implementation, you don't have to create your own developer app. For production, if you're building an end product, we recommend using your own credentials. The managed server helps you prototype fast and go from 0-1 faster.

## Step-by-step Guide

### 1. Prerequisites

Before you begin, make sure you have:
- Node.js and npm installed
- A Composio account with API key
- An OpenAI API key

### 1. Getting API Keys for OpenAI and Composio

OpenAI API Key
- Go to the [OpenAI dashboard](https://platform.openai.com/settings/organization/api-keys) and create an API key. You'll need credits to use the models, or you can connect to another model provider.
- Keep the API key safe.
Composio API Key
- Log in to the [Composio dashboard](https://dashboard.composio.dev?utm_source=toolkits&utm_medium=framework_docs).
- Navigate to your API settings and generate a new API key.
- Store this key securely as you'll need it for authentication.

### 2. Install required dependencies

First, install the necessary packages for your project.
What you're installing:
- @ai-sdk/openai: Vercel AI SDK's OpenAI provider
- @ai-sdk/mcp: MCP client for Vercel AI SDK
- @composio/core: Composio SDK for tool integration
- ai: Core Vercel AI SDK
- dotenv: Environment variable management
```bash
npm install @ai-sdk/openai @ai-sdk/mcp @composio/core ai dotenv
```

### 3. Set up environment variables

Create a .env file in your project root.
What's needed:
- OPENAI_API_KEY: Your OpenAI API key for GPT model access
- COMPOSIO_API_KEY: Your Composio API key for tool access
- COMPOSIO_USER_ID: A unique identifier for the user session
```bash
OPENAI_API_KEY=your_openai_api_key_here
COMPOSIO_API_KEY=your_composio_api_key_here
COMPOSIO_USER_ID=your_user_id_here
```

### 4. Import required modules and validate environment

What's happening:
- We're importing all necessary libraries including Vercel AI SDK's OpenAI provider and Composio
- The dotenv/config import automatically loads environment variables
- The MCP client import enables connection to Composio's tool server
```typescript
import "dotenv/config";
import { openai } from "@ai-sdk/openai";
import { Composio } from "@composio/core";
import * as readline from "readline";
import { streamText, type ModelMessage, stepCountIs } from "ai";
import { createMCPClient } from "@ai-sdk/mcp";

const composioAPIKey = process.env.COMPOSIO_API_KEY;
const composioUserID = process.env.COMPOSIO_USER_ID;

if (!process.env.OPENAI_API_KEY) throw new Error("OPENAI_API_KEY is not set");
if (!composioAPIKey) throw new Error("COMPOSIO_API_KEY is not set");
if (!composioUserID) throw new Error("COMPOSIO_USER_ID is not set");

const composio = new Composio({
  apiKey: composioAPIKey,
});
```

### 5. Create Tool Router session and initialize MCP client

What's happening:
- We're creating a Tool Router session that gives your agent access to Ashby tools
- The create method takes the user ID and specifies which toolkits should be available
- The returned mcp object contains the URL and authentication headers needed to connect to the MCP server
- This session provides access to all Ashby-related tools through the MCP protocol
```typescript
async function main() {
  // Create a tool router session for the user
  const session = await composio.create(composioUserID!, {
    toolkits: ["ashby"],
  });

  const mcpUrl = session.mcp.url;
```

### 6. Connect to MCP server and retrieve tools

What's happening:
- We're creating an MCP client that connects to our Composio Tool Router session via HTTP
- The mcp.url provides the endpoint, and mcp.headers contains authentication credentials
- The type: "http" is important - Composio requires HTTP transport
- tools() retrieves all available Ashby tools that the agent can use
```typescript
const mcpClient = await createMCPClient({
  transport: {
    type: "http",
    url: mcpUrl,
    headers: session.mcp.headers, // Authentication headers for the Composio MCP server
  },
});

const tools = await mcpClient.tools();
```

### 7. Initialize conversation and CLI interface

What's happening:
- We initialize an empty messages array to maintain conversation history
- A readline interface is created to accept user input from the command line
- Instructions are displayed to guide the user on how to interact with the agent
```typescript
let messages: ModelMessage[] = [];

console.log("Chat started! Type 'exit' or 'quit' to end the conversation.\n");
console.log(
  "Ask any questions related to ashby, like summarize my last 5 emails, send an email, etc... :)))\n",
);

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  prompt: "> ",
});

rl.prompt();
```

### 8. Handle user input and stream responses with real-time tool feedback

What's happening:
- We use streamText instead of generateText to stream responses in real-time
- toolChoice: "auto" allows the model to decide when to use Ashby tools
- stopWhen: stepCountIs(10) allows up to 10 steps for complex multi-tool operations
- onStepFinish callback displays which tools are being used in real-time
- We iterate through the text stream to create a typewriter effect as the agent responds
- The complete response is added to conversation history to maintain context
- Errors are caught and displayed with helpful retry suggestions
```typescript
rl.on("line", async (userInput: string) => {
  const trimmedInput = userInput.trim();

  if (["exit", "quit", "bye"].includes(trimmedInput.toLowerCase())) {
    console.log("\nGoodbye!");
    rl.close();
    process.exit(0);
  }

  if (!trimmedInput) {
    rl.prompt();
    return;
  }

  messages.push({ role: "user", content: trimmedInput });
  console.log("\nAgent is thinking...\n");

  try {
    const stream = streamText({
      model: openai("gpt-5"),
      messages,
      tools,
      toolChoice: "auto",
      stopWhen: stepCountIs(10),
      onStepFinish: (step) => {
        for (const toolCall of step.toolCalls) {
          console.log(`[Using tool: ${toolCall.toolName}]`);
          }
          if (step.toolCalls.length > 0) {
            console.log(""); // Add space after tool calls
          }
        },
      });

      for await (const chunk of stream.textStream) {
        process.stdout.write(chunk);
      }

      console.log("\n\n---\n");

      // Get final result for message history
      const response = await stream.response;
      if (response?.messages?.length) {
        messages.push(...response.messages);
      }
    } catch (error) {
      console.error("\nAn error occurred while talking to the agent:");
      console.error(error);
      console.log(
        "\nYou can try again or restart the app if it keeps happening.\n",
      );
    } finally {
      rl.prompt();
    }
  });

  rl.on("close", async () => {
    await mcpClient.close();
    console.log("\n👋 Session ended.");
    process.exit(0);
  });
}

main().catch((err) => {
  console.error("Fatal error:", err);
  process.exit(1);
});
```

## Complete Code

```typescript
import "dotenv/config";
import { openai } from "@ai-sdk/openai";
import { Composio } from "@composio/core";
import * as readline from "readline";
import { streamText, type ModelMessage, stepCountIs } from "ai";
import { createMCPClient } from "@ai-sdk/mcp";

const composioAPIKey = process.env.COMPOSIO_API_KEY;
const composioUserID = process.env.COMPOSIO_USER_ID;

if (!process.env.OPENAI_API_KEY) throw new Error("OPENAI_API_KEY is not set");
if (!composioAPIKey) throw new Error("COMPOSIO_API_KEY is not set");
if (!composioUserID) throw new Error("COMPOSIO_USER_ID is not set");

const composio = new Composio({
  apiKey: composioAPIKey,
});

async function main() {
  // Create a tool router session for the user
  const session = await composio.create(composioUserID!, {
    toolkits: ["ashby"],
  });

  const mcpUrl = session.mcp.url;

  const mcpClient = await createMCPClient({
    transport: {
      type: "http",
      url: mcpUrl,
      headers: session.mcp.headers, // Authentication headers for the Composio MCP server
    },
  });

  const tools = await mcpClient.tools();

  let messages: ModelMessage[] = [];

  console.log("Chat started! Type 'exit' or 'quit' to end the conversation.\n");
  console.log(
    "Ask any questions related to ashby, like summarize my last 5 emails, send an email, etc... :)))\n",
  );

  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    prompt: "> ",
  });

  rl.prompt();

  rl.on("line", async (userInput: string) => {
    const trimmedInput = userInput.trim();

    if (["exit", "quit", "bye"].includes(trimmedInput.toLowerCase())) {
      console.log("\nGoodbye!");
      rl.close();
      process.exit(0);
    }

    if (!trimmedInput) {
      rl.prompt();
      return;
    }

    messages.push({ role: "user", content: trimmedInput });
    console.log("\nAgent is thinking...\n");

    try {
      const stream = streamText({
        model: openai("gpt-5"),
        messages,
        tools,
        toolChoice: "auto",
        stopWhen: stepCountIs(10),
        onStepFinish: (step) => {
          for (const toolCall of step.toolCalls) {
            console.log(`[Using tool: ${toolCall.toolName}]`);
          }
          if (step.toolCalls.length > 0) {
            console.log(""); // Add space after tool calls
          }
        },
      });

      for await (const chunk of stream.textStream) {
        process.stdout.write(chunk);
      }

      console.log("\n\n---\n");

      // Get final result for message history
      const response = await stream.response;
      if (response?.messages?.length) {
        messages.push(...response.messages);
      }
    } catch (error) {
      console.error("\nAn error occurred while talking to the agent:");
      console.error(error);
      console.log(
        "\nYou can try again or restart the app if it keeps happening.\n",
      );
    } finally {
      rl.prompt();
    }
  });

  rl.on("close", async () => {
    await mcpClient.close();
    console.log("\n👋 Session ended.");
    process.exit(0);
  });
}

main().catch((err) => {
  console.error("Fatal error:", err);
  process.exit(1);
});
```

## Conclusion

You've successfully built a Ashby agent using the Vercel AI SDK with streaming capabilities! This implementation provides a powerful foundation for building AI applications with natural language interfaces and real-time feedback.
Key features of this implementation:
- Real-time streaming responses for a better user experience with typewriter effect
- Live tool execution feedback showing which tools are being used as the agent works
- Dynamic tool loading through Composio's Tool Router with secure authentication
- Multi-step tool execution with configurable step limits (up to 10 steps)
- Comprehensive error handling for robust agent execution
- Conversation history maintenance for context-aware responses
You can extend this further by adding custom error handling, implementing specific business logic, or integrating additional Composio toolkits to create multi-app workflows.

## How to build Ashby MCP Agent with another framework

- [OpenAI Agents SDK](https://composio.dev/toolkits/ashby/framework/open-ai-agents-sdk)
- [Claude Agent SDK](https://composio.dev/toolkits/ashby/framework/claude-agents-sdk)
- [Claude Code](https://composio.dev/toolkits/ashby/framework/claude-code)
- [Claude Cowork](https://composio.dev/toolkits/ashby/framework/claude-cowork)
- [Codex](https://composio.dev/toolkits/ashby/framework/codex)
- [OpenClaw](https://composio.dev/toolkits/ashby/framework/openclaw)
- [Hermes](https://composio.dev/toolkits/ashby/framework/hermes-agent)
- [CLI](https://composio.dev/toolkits/ashby/framework/cli)
- [Google ADK](https://composio.dev/toolkits/ashby/framework/google-adk)
- [LangChain](https://composio.dev/toolkits/ashby/framework/langchain)
- [Mastra AI](https://composio.dev/toolkits/ashby/framework/mastra-ai)
- [LlamaIndex](https://composio.dev/toolkits/ashby/framework/llama-index)
- [CrewAI](https://composio.dev/toolkits/ashby/framework/crew-ai)

## Related Toolkits

- [Async interview](https://composio.dev/toolkits/async_interview) - Async interview is an on-demand video interview platform for streamlined hiring. Candidates record responses on their schedule, so employers can review anytime.
- [Bamboohr](https://composio.dev/toolkits/bamboohr) - BambooHR is a cloud-based HR management platform for small and mid-sized businesses. It streamlines employee data, HR workflows, and reporting in one easy interface.
- [Breathe HR](https://composio.dev/toolkits/breathehr) - Breathe HR is cloud-based HR software for SMEs to manage employee data, absences, and performance. It simplifies HR admin, making it easy to keep employee records accurate and up to date.
- [Connecteam](https://composio.dev/toolkits/connecteam) - Connecteam is a workforce management platform for deskless teams, streamlining operations, HR, and team communication. It helps businesses save time by automating scheduling, time tracking, and staff engagement tasks.
- [Lever](https://composio.dev/toolkits/lever) - Lever is an applicant tracking system that blends sourcing, CRM, and analytics for recruiting. It helps companies scale hiring with collaborative workflows and actionable insights.
- [Recruitee](https://composio.dev/toolkits/recruitee) - Recruitee is collaborative hiring software that centralizes recruitment tasks for teams. It streamlines sourcing, interviewing, and hiring so you can fill roles faster.
- [Remote retrieval](https://composio.dev/toolkits/remote_retrieval) - Remote retrieval is a logistics automation tool for managing laptop and monitor returns. It streamlines return tracking, saving time and hassle for IT and ops teams.
- [Sap successfactors](https://composio.dev/toolkits/sap_successfactors) - Sap successfactors is a cloud-based human capital management suite for HR, payroll, recruiting, and talent management. It helps organizations centralize employee data and streamline the entire employee lifecycle.
- [Talenthr](https://composio.dev/toolkits/talenthr) - TalentHR is an intuitive, all-in-one HR tool for managing employee records, leave, and HR workflows. It streamlines HR operations so businesses can focus on people, not paperwork.
- [Workable](https://composio.dev/toolkits/workable) - Workable is an all-in-one HR software platform that streamlines hiring, employee management, and payroll. It helps teams simplify recruiting, onboarding, and staff operations in one place.
- [Workday](https://composio.dev/toolkits/workday) - Workday is a cloud-based ERP platform for HR, finance, and workforce analytics. It streamlines employee management, payroll, and business operations in a single system.
- [Gmail](https://composio.dev/toolkits/gmail) - Gmail is Google's email service with powerful spam protection, search, and G Suite integration. It keeps your inbox organized and makes communication fast and reliable.
- [Google Calendar](https://composio.dev/toolkits/googlecalendar) - Google Calendar is a time management service for scheduling meetings, events, and reminders. It streamlines personal and team organization with integrated notifications and sharing options.
- [Google Drive](https://composio.dev/toolkits/googledrive) - Google Drive is a cloud storage platform for uploading, sharing, and collaborating on files. It's perfect for keeping your documents accessible and organized across devices.
- [Outlook](https://composio.dev/toolkits/outlook) - Outlook is Microsoft's email and calendaring platform for unified communications and scheduling. It helps users stay organized with powerful email, contacts, and calendar management.
- [Twitter](https://composio.dev/toolkits/twitter) - Twitter is a social media platform for sharing real-time updates, conversations, and news. Stay connected, informed, and engaged with communities worldwide.
- [Google Sheets](https://composio.dev/toolkits/googlesheets) - Google Sheets is a cloud-based spreadsheet tool for real-time collaboration and data analysis. It lets teams work together from anywhere, updating information instantly.
- [Supabase](https://composio.dev/toolkits/supabase) - Supabase is an open-source backend platform offering scalable Postgres databases, authentication, storage, and real-time APIs. It lets developers build modern apps without managing infrastructure.
- [Composio](https://composio.dev/toolkits/composio) - Composio is an integration platform that connects AI agents with hundreds of business tools. It streamlines authentication and lets you trigger actions across services—no custom code needed.
- [Notion](https://composio.dev/toolkits/notion) - Notion is a collaborative workspace for notes, docs, wikis, and tasks. It streamlines team knowledge, project tracking, and workflow customization in one place.

## Frequently Asked Questions

### What are the differences in Tool Router MCP and Ashby MCP?

With a standalone Ashby MCP server, the agents and LLMs can only access a fixed set of Ashby tools tied to that server. However, with the Composio Tool Router, agents can dynamically load tools from Ashby and many other apps based on the task at hand, all through a single MCP endpoint.

### Can I use Tool Router MCP with Vercel AI SDK v6?

Yes, you can. Vercel AI SDK v6 fully supports MCP integration. You get structured tool calling, message history handling, and model orchestration while Tool Router takes care of discovering and serving the right Ashby tools.

### Can I manage the permissions and scopes for Ashby while using Tool Router?

Yes, absolutely. You can configure which Ashby scopes and actions are allowed when connecting your account to Composio. You can also bring your own OAuth credentials or API configuration so you keep full control over what the agent can do.

### How safe is my data with Composio Tool Router?

All sensitive data such as tokens, keys, and configuration is fully encrypted at rest and in transit. Composio is SOC 2 Type 2 compliant and follows strict security practices so your Ashby data and credentials are handled as safely as possible.

---
[See all toolkits](https://composio.dev/toolkits) · [Composio docs](https://docs.composio.dev/llms.txt)
